diff --git a/src/ap/dfs.c b/src/ap/dfs.c index b990fb342388..03c99b175215 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -1,1365 +1,1370 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "drivers/driver.h" #include "dfs.h" static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) { int n_chans = 1; *seg1 = 0; if (iface->conf->ieee80211n && iface->conf->secondary_channel) n_chans = 2; if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { switch (hostapd_get_oper_chwidth(iface->conf)) { case CHANWIDTH_USE_HT: break; case CHANWIDTH_80MHZ: n_chans = 4; break; case CHANWIDTH_160MHZ: n_chans = 8; break; case CHANWIDTH_80P80MHZ: n_chans = 4; *seg1 = 4; break; default: break; } } return n_chans; } static int dfs_channel_available(struct hostapd_channel_data *chan, int skip_radar) { /* * When radar detection happens, CSA is performed. However, there's no * time for CAC, so radar channels must be skipped when finding a new * channel for CSA, unless they are available for immediate use. */ if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) && ((chan->flag & HOSTAPD_CHAN_DFS_MASK) != HOSTAPD_CHAN_DFS_AVAILABLE)) return 0; if (chan->flag & HOSTAPD_CHAN_DISABLED) return 0; if ((chan->flag & HOSTAPD_CHAN_RADAR) && ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == HOSTAPD_CHAN_DFS_UNAVAILABLE)) return 0; return 1; } static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) { /* * The tables contain first valid channel number based on channel width. * We will also choose this first channel as the control one. */ int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173, 184, 192 }; /* * VHT80, valid channels based on center frequency: * 42, 58, 106, 122, 138, 155, 171 */ int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 }; /* * VHT160 valid channels based on center frequency: * 50, 114, 163 */ int allowed_160[] = { 36, 100, 149 }; int *allowed = allowed_40; unsigned int i, allowed_no = 0; switch (n_chans) { case 2: allowed = allowed_40; allowed_no = ARRAY_SIZE(allowed_40); break; case 4: allowed = allowed_80; allowed_no = ARRAY_SIZE(allowed_80); break; case 8: allowed = allowed_160; allowed_no = ARRAY_SIZE(allowed_160); break; default: wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); break; } for (i = 0; i < allowed_no; i++) { if (chan->chan == allowed[i]) return 1; } return 0; } static struct hostapd_channel_data * dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) { int i; for (i = first_chan_idx; i < mode->num_channels; i++) { if (mode->channels[i].freq == freq) return &mode->channels[i]; } return NULL; } static int dfs_chan_range_available(struct hostapd_hw_modes *mode, int first_chan_idx, int num_chans, int skip_radar) { struct hostapd_channel_data *first_chan, *chan; int i; u32 bw = num_chan_to_bw(num_chans); if (first_chan_idx + num_chans > mode->num_channels) { wpa_printf(MSG_DEBUG, "DFS: some channels in range not defined"); return 0; } first_chan = &mode->channels[first_chan_idx]; /* hostapd DFS implementation assumes the first channel as primary. * If it's not allowed to use the first channel as primary, decline the * whole channel range. */ if (!chan_pri_allowed(first_chan)) { wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed"); return 0; } for (i = 0; i < num_chans; i++) { chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, first_chan_idx); if (!chan) { wpa_printf(MSG_DEBUG, "DFS: no channel data for %d", first_chan->freq + i * 20); return 0; } /* HT 40 MHz secondary channel availability checked only for * primary channel */ if (!chan_bw_allowed(chan, bw, 1, !i)) { wpa_printf(MSG_DEBUG, "DFS: bw now allowed for %d", first_chan->freq + i * 20); return 0; } if (!dfs_channel_available(chan, skip_radar)) { wpa_printf(MSG_DEBUG, "DFS: channel not available %d", first_chan->freq + i * 20); return 0; } } return 1; } static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { if (!iface->conf->acs_ch_list.num) return 1; return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } /* * The function assumes HT40+ operation. * Make sure to adjust the following variables after calling this: * - hapd->secondary_channel * - hapd->vht/he_oper_centr_freq_seg0_idx * - hapd->vht/he_oper_centr_freq_seg1_idx */ static int dfs_find_channel(struct hostapd_iface *iface, struct hostapd_channel_data **ret_chan, int idx, int skip_radar) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int i, channel_idx = 0, n_chans, n_chans1; mode = iface->current_mode; n_chans = dfs_get_used_n_chans(iface, &n_chans1); wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; /* Skip HT40/VHT incompatible channels */ if (iface->conf->ieee80211n && iface->conf->secondary_channel && (!dfs_is_chan_allowed(chan, n_chans) || !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) { wpa_printf(MSG_DEBUG, "DFS: channel %d (%d) is incompatible", chan->freq, chan->chan); continue; } /* Skip incompatible chandefs */ if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) { wpa_printf(MSG_DEBUG, "DFS: range not available for %d (%d)", chan->freq, chan->chan); continue; } if (!is_in_chanlist(iface, chan)) { wpa_printf(MSG_DEBUG, "DFS: channel %d (%d) not in chanlist", chan->freq, chan->chan); continue; } if (ret_chan && idx == channel_idx) { wpa_printf(MSG_DEBUG, "Selected channel %d (%d)", chan->freq, chan->chan); *ret_chan = chan; return idx; } wpa_printf(MSG_DEBUG, "Adding channel %d (%d)", chan->freq, chan->chan); channel_idx++; } return channel_idx; } static void dfs_adjust_center_freq(struct hostapd_iface *iface, struct hostapd_channel_data *chan, int secondary_channel, int sec_chan_idx_80p80, u8 *oper_centr_freq_seg0_idx, u8 *oper_centr_freq_seg1_idx) { if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax) return; if (!chan) return; *oper_centr_freq_seg1_idx = 0; switch (hostapd_get_oper_chwidth(iface->conf)) { case CHANWIDTH_USE_HT: if (secondary_channel == 1) *oper_centr_freq_seg0_idx = chan->chan + 2; else if (secondary_channel == -1) *oper_centr_freq_seg0_idx = chan->chan - 2; else *oper_centr_freq_seg0_idx = chan->chan; break; case CHANWIDTH_80MHZ: *oper_centr_freq_seg0_idx = chan->chan + 6; break; case CHANWIDTH_160MHZ: *oper_centr_freq_seg0_idx = chan->chan + 14; break; case CHANWIDTH_80P80MHZ: *oper_centr_freq_seg0_idx = chan->chan + 6; *oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6; break; default: wpa_printf(MSG_INFO, "DFS: Unsupported channel width configuration"); *oper_centr_freq_seg0_idx = 0; break; } wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", *oper_centr_freq_seg0_idx, *oper_centr_freq_seg1_idx); } /* Return start channel idx we will use for mode->channels[idx] */ static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int channel_no = iface->conf->channel; int res = -1, i; int chan_seg1 = -1; *seg1_start = -1; /* HT40- */ if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) channel_no -= 4; /* VHT/HE */ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { switch (hostapd_get_oper_chwidth(iface->conf)) { case CHANWIDTH_USE_HT: break; case CHANWIDTH_80MHZ: channel_no = hostapd_get_oper_centr_freq_seg0_idx( iface->conf) - 6; break; case CHANWIDTH_160MHZ: channel_no = hostapd_get_oper_centr_freq_seg0_idx( iface->conf) - 14; break; case CHANWIDTH_80P80MHZ: channel_no = hostapd_get_oper_centr_freq_seg0_idx( iface->conf) - 6; chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx( iface->conf) - 6; break; default: wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160/80+80 is supported now"); channel_no = -1; break; } } /* Get idx */ mode = iface->current_mode; for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; if (chan->chan == channel_no) { res = i; break; } } if (res != -1 && chan_seg1 > -1) { int found = 0; /* Get idx for seg1 */ mode = iface->current_mode; for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; if (chan->chan == chan_seg1) { *seg1_start = i; found = 1; break; } } if (!found) res = -1; } if (res == -1) { wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d", mode->num_channels, channel_no, iface->conf->channel, iface->conf->ieee80211n, iface->conf->secondary_channel, hostapd_get_oper_chwidth(iface->conf)); for (i = 0; i < mode->num_channels; i++) { wpa_printf(MSG_DEBUG, "Available channel: %d", mode->channels[i].chan); } } return res; } /* At least one channel have radar flag */ static int dfs_check_chans_radar(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i, res = 0; mode = iface->current_mode; for (i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if (channel->flag & HOSTAPD_CHAN_RADAR) res++; } return res; } /* All channels available */ static int dfs_check_chans_available(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i; mode = iface->current_mode; for (i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if (channel->flag & HOSTAPD_CHAN_DISABLED) break; if (!(channel->flag & HOSTAPD_CHAN_RADAR)) continue; if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != HOSTAPD_CHAN_DFS_AVAILABLE) break; } return i == n_chans; } /* At least one channel unavailable */ static int dfs_check_chans_unavailable(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i, res = 0; mode = iface->current_mode; for (i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if (channel->flag & HOSTAPD_CHAN_DISABLED) res++; if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == HOSTAPD_CHAN_DFS_UNAVAILABLE) res++; } return res; } static struct hostapd_channel_data * dfs_get_valid_channel(struct hostapd_iface *iface, int *secondary_channel, u8 *oper_centr_freq_seg0_idx, u8 *oper_centr_freq_seg1_idx, int skip_radar) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan = NULL; struct hostapd_channel_data *chan2 = NULL; int num_available_chandefs; int chan_idx, chan_idx2; int sec_chan_idx_80p80 = -1; int i; u32 _rand; wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); *secondary_channel = 0; *oper_centr_freq_seg0_idx = 0; *oper_centr_freq_seg1_idx = 0; if (iface->current_mode == NULL) return NULL; mode = iface->current_mode; if (mode->mode != HOSTAPD_MODE_IEEE80211A) return NULL; /* Get the count first */ num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d", num_available_chandefs); if (num_available_chandefs == 0) return NULL; if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) return NULL; chan_idx = _rand % num_available_chandefs; dfs_find_channel(iface, &chan, chan_idx, skip_radar); if (!chan) { wpa_printf(MSG_DEBUG, "DFS: no random channel found"); return NULL; } wpa_printf(MSG_DEBUG, "DFS: got random channel %d (%d)", chan->freq, chan->chan); /* dfs_find_channel() calculations assume HT40+ */ if (iface->conf->secondary_channel) *secondary_channel = 1; else *secondary_channel = 0; /* Get secondary channel for HT80P80 */ if (hostapd_get_oper_chwidth(iface->conf) == CHANWIDTH_80P80MHZ) { if (num_available_chandefs <= 1) { wpa_printf(MSG_ERROR, "only 1 valid chan, can't support 80+80"); return NULL; } /* * Loop all channels except channel1 to find a valid channel2 * that is not adjacent to channel1. */ for (i = 0; i < num_available_chandefs - 1; i++) { /* start from chan_idx + 1, end when chan_idx - 1 */ chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs; dfs_find_channel(iface, &chan2, chan_idx2, skip_radar); if (chan2 && abs(chan2->chan - chan->chan) > 12) { /* two channels are not adjacent */ sec_chan_idx_80p80 = chan2->chan; wpa_printf(MSG_DEBUG, "DFS: got second chan: %d (%d)", chan2->freq, chan2->chan); break; } } /* Check if we got a valid secondary channel which is not * adjacent to the first channel. */ if (sec_chan_idx_80p80 == -1) { wpa_printf(MSG_INFO, "DFS: failed to get chan2 for 80+80"); return NULL; } } dfs_adjust_center_freq(iface, chan, *secondary_channel, sec_chan_idx_80p80, oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx); return chan; } static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) { struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan = NULL; int i; mode = iface->current_mode; if (mode == NULL) return 0; wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->freq == freq) { if (chan->flag & HOSTAPD_CHAN_RADAR) { chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; chan->flag |= state; return 1; /* Channel found */ } } } wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); return 0; } static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2, u32 state) { int n_chans = 1, i; struct hostapd_hw_modes *mode; int frequency = freq; int frequency2 = 0; int ret = 0; mode = iface->current_mode; if (mode == NULL) return 0; if (mode->mode != HOSTAPD_MODE_IEEE80211A) { wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); return 0; } /* Seems cf1 and chan_width is enough here */ switch (chan_width) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: n_chans = 1; if (frequency == 0) frequency = cf1; break; case CHAN_WIDTH_40: n_chans = 2; frequency = cf1 - 10; break; case CHAN_WIDTH_80: n_chans = 4; frequency = cf1 - 30; break; case CHAN_WIDTH_80P80: n_chans = 4; frequency = cf1 - 30; frequency2 = cf2 - 30; break; case CHAN_WIDTH_160: n_chans = 8; frequency = cf1 - 70; break; default: wpa_printf(MSG_INFO, "DFS chan_width %d not supported", chan_width); break; } wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, n_chans); for (i = 0; i < n_chans; i++) { ret += set_dfs_state_freq(iface, frequency, state); frequency = frequency + 20; if (chan_width == CHAN_WIDTH_80P80) { ret += set_dfs_state_freq(iface, frequency2, state); frequency2 = frequency2 + 20; } } return ret; } static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, int chan_width, int cf1, int cf2) { int start_chan_idx, start_chan_idx1; struct hostapd_hw_modes *mode; struct hostapd_channel_data *chan; int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1; u8 radar_chan; int res = 0; /* Our configuration */ mode = iface->current_mode; start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Check we are on DFS channel(s) */ if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) return 0; /* Reported via radar event */ switch (chan_width) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: radar_n_chans = 1; if (frequency == 0) frequency = cf1; break; case CHAN_WIDTH_40: radar_n_chans = 2; frequency = cf1 - 10; break; case CHAN_WIDTH_80: radar_n_chans = 4; frequency = cf1 - 30; break; case CHAN_WIDTH_160: radar_n_chans = 8; frequency = cf1 - 70; break; default: wpa_printf(MSG_INFO, "DFS chan_width %d not supported", chan_width); break; } ieee80211_freq_to_chan(frequency, &radar_chan); for (i = 0; i < n_chans; i++) { chan = &mode->channels[start_chan_idx + i]; if (!(chan->flag & HOSTAPD_CHAN_RADAR)) continue; for (j = 0; j < radar_n_chans; j++) { wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", chan->chan, radar_chan + j * 4); if (chan->chan == radar_chan + j * 4) res++; } } wpa_printf(MSG_DEBUG, "overlapped: %d", res); return res; } static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, int start_chan_idx, int n_chans) { struct hostapd_channel_data *channel; struct hostapd_hw_modes *mode; int i; unsigned int cac_time_ms = 0; mode = iface->current_mode; for (i = 0; i < n_chans; i++) { channel = &mode->channels[start_chan_idx + i]; if (!(channel->flag & HOSTAPD_CHAN_RADAR)) continue; if (channel->dfs_cac_ms > cac_time_ms) cac_time_ms = channel->dfs_cac_ms; } return cac_time_ms; } /* * Main DFS handler * 1 - continue channel/ap setup * 0 - channel/ap setup will be continued after CAC * -1 - hit critical error */ int hostapd_handle_dfs(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; int skip_radar = 0; if (is_6ghz_freq(iface->freq)) return 1; if (!iface->current_mode) { /* * This can happen with drivers that do not provide mode * information and as such, cannot really use hostapd for DFS. */ wpa_printf(MSG_DEBUG, "DFS: No current_mode information - assume no need to perform DFS operations by hostapd"); return 1; } iface->cac_started = 0; do { /* Get start (first) channel for current configuration */ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); if (start_chan_idx == -1) return -1; /* Get number of used channels, depend on width */ n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Setup CAC time */ iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx, n_chans); /* Check if any of configured channels require DFS */ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); wpa_printf(MSG_DEBUG, "DFS %d channels required radar detection", res); if (!res) return 1; /* Check if all channels are DFS available */ res = dfs_check_chans_available(iface, start_chan_idx, n_chans); wpa_printf(MSG_DEBUG, "DFS all channels available, (SKIP CAC): %s", res ? "yes" : "no"); if (res) return 1; /* Check if any of configured channels is unavailable */ res = dfs_check_chans_unavailable(iface, start_chan_idx, n_chans); wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", res, res ? "yes": "no"); if (res) { int sec = 0; u8 cf1 = 0, cf2 = 0; channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "could not get valid channel"); hostapd_set_state(iface, HAPD_IFACE_DFS); return 0; } iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = sec; hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1); hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2); } } while (res); /* Finally start CAC */ hostapd_set_state(iface, HAPD_IFACE_DFS); wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", iface->freq, iface->conf->channel, iface->conf->secondary_channel, hostapd_get_oper_chwidth(iface->conf), hostapd_get_oper_centr_freq_seg0_idx(iface->conf), hostapd_get_oper_centr_freq_seg1_idx(iface->conf), iface->dfs_cac_ms / 1000); res = hostapd_start_dfs_cac( iface, iface->conf->hw_mode, iface->freq, iface->conf->channel, iface->conf->ieee80211n, iface->conf->ieee80211ac, iface->conf->ieee80211ax, iface->conf->secondary_channel, hostapd_get_oper_chwidth(iface->conf), hostapd_get_oper_centr_freq_seg0_idx(iface->conf), hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); if (res) { wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); return -1; } return 0; } int hostapd_is_dfs_chan_available(struct hostapd_iface *iface) { int n_chans, n_chans1, start_chan_idx, start_chan_idx1; /* Get the start (first) channel for current configuration */ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); if (start_chan_idx < 0) return 0; /* Get the number of used channels, depending on width */ n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Check if all channels are DFS available */ return dfs_check_chans_available(iface, start_chan_idx, n_chans); } int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); if (success) { /* Complete iface/ap configuration */ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) { /* Complete AP configuration for the first bring up. */ if (iface->state != HAPD_IFACE_ENABLED) hostapd_setup_interface_complete(iface, 0); else iface->cac_started = 0; } else { set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_AVAILABLE); /* * Just mark the channel available when CAC completion * event is received in enabled state. CAC result could * have been propagated from another radio having the * same regulatory configuration. When CAC completion is * received during non-HAPD_IFACE_ENABLED state, make * sure the configured channel is available because this * CAC completion event could have been propagated from * another radio. */ if (iface->state != HAPD_IFACE_ENABLED && hostapd_is_dfs_chan_available(iface)) { hostapd_setup_interface_complete(iface, 0); iface->cac_started = 0; } } } return 0; } int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); /* Proceed only if DFS is not offloaded to the driver */ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) return 0; set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); return 0; } static struct hostapd_channel_data * dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel, u8 *oper_centr_freq_seg0_idx, u8 *oper_centr_freq_seg1_idx, int *skip_radar) { struct hostapd_channel_data *channel; for (;;) { channel = dfs_get_valid_channel(iface, secondary_channel, oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx, *skip_radar); if (channel) { wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d", channel->chan); return channel; } if (*skip_radar) { *skip_radar = 0; } else { int oper_chwidth; oper_chwidth = hostapd_get_oper_chwidth(iface->conf); if (oper_chwidth == CHANWIDTH_USE_HT) break; *skip_radar = 1; hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1); } } wpa_printf(MSG_INFO, "%s: no DFS channels left, waiting for NOP to finish", __func__); return NULL; } static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int secondary_channel; u8 oper_centr_freq_seg0_idx = 0; u8 oper_centr_freq_seg1_idx = 0; int skip_radar = 0; int err = 1; /* Radar detected during active CAC */ iface->cac_started = 0; channel = dfs_get_valid_channel(iface, &secondary_channel, &oper_centr_freq_seg0_idx, &oper_centr_freq_seg1_idx, skip_radar); if (!channel) { channel = dfs_downgrade_bandwidth(iface, &secondary_channel, &oper_centr_freq_seg0_idx, &oper_centr_freq_seg1_idx, &skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "No valid channel available"); return err; } } wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel->chan); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL "freq=%d chan=%d sec_chan=%d", channel->freq, channel->chan, secondary_channel); iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; hostapd_set_oper_centr_freq_seg0_idx(iface->conf, oper_centr_freq_seg0_idx); hostapd_set_oper_centr_freq_seg1_idx(iface->conf, oper_centr_freq_seg1_idx); err = 0; hostapd_setup_interface_complete(iface, err); return err; } static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int secondary_channel; u8 oper_centr_freq_seg0_idx; u8 oper_centr_freq_seg1_idx; u8 new_vht_oper_chwidth; int skip_radar = 1; struct csa_settings csa_settings; unsigned int i; int err = 1; struct hostapd_hw_modes *cmode = iface->current_mode; u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); int ieee80211_mode = IEEE80211_MODE_AP; wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", __func__, iface->cac_started ? "yes" : "no", hostapd_csa_in_progress(iface) ? "yes" : "no"); /* Check if CSA in progress */ if (hostapd_csa_in_progress(iface)) return 0; /* Check if active CAC */ if (iface->cac_started) return hostapd_dfs_start_channel_switch_cac(iface); /* * Allow selection of DFS channel in ETSI to comply with * uniform spreading. */ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) skip_radar = 0; /* Perform channel switch/CSA */ channel = dfs_get_valid_channel(iface, &secondary_channel, &oper_centr_freq_seg0_idx, &oper_centr_freq_seg1_idx, skip_radar); if (!channel) { /* * If there is no channel to switch immediately to, check if * there is another channel where we can switch even if it * requires to perform a CAC first. */ skip_radar = 0; channel = dfs_downgrade_bandwidth(iface, &secondary_channel, &oper_centr_freq_seg0_idx, &oper_centr_freq_seg1_idx, &skip_radar); if (!channel) { /* * Toggle interface state to enter DFS state * until NOP is finished. */ hostapd_disable_iface(iface); hostapd_enable_iface(iface); return 0; } if (!skip_radar) { iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; hostapd_set_oper_centr_freq_seg0_idx( iface->conf, oper_centr_freq_seg0_idx); hostapd_set_oper_centr_freq_seg1_idx( iface->conf, oper_centr_freq_seg1_idx); hostapd_disable_iface(iface); hostapd_enable_iface(iface); return 0; } } wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel->chan); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL "freq=%d chan=%d sec_chan=%d", channel->freq, channel->chan, secondary_channel); new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth); /* Setup CSA request */ os_memset(&csa_settings, 0, sizeof(csa_settings)); csa_settings.cs_count = 5; csa_settings.block_tx = 1; #ifdef CONFIG_MESH if (iface->mconf) ieee80211_mode = IEEE80211_MODE_MESH; #endif /* CONFIG_MESH */ err = hostapd_set_freq_params(&csa_settings.freq_params, iface->conf->hw_mode, channel->freq, channel->chan, iface->conf->enable_edmg, iface->conf->edmg_channel, iface->conf->ieee80211n, iface->conf->ieee80211ac, iface->conf->ieee80211ax, secondary_channel, new_vht_oper_chwidth, oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx, cmode->vht_capab, &cmode->he_capab[ieee80211_mode]); if (err) { wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); hostapd_disable_iface(iface); return err; } for (i = 0; i < iface->num_bss; i++) { err = hostapd_switch_channel(iface->bss[i], &csa_settings); if (err) break; } if (err) { wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", err); iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth); hostapd_set_oper_centr_freq_seg0_idx(iface->conf, oper_centr_freq_seg0_idx); hostapd_set_oper_centr_freq_seg1_idx(iface->conf, oper_centr_freq_seg1_idx); hostapd_disable_iface(iface); hostapd_enable_iface(iface); return 0; } /* Channel configuration will be updated once CSA completes and * ch_switch_notify event is received */ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); return 0; } int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { int res; wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); /* Proceed only if DFS is not offloaded to the driver */ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) return 0; if (!iface->conf->ieee80211h) return 0; /* mark radar frequency as invalid */ res = set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE); if (!res) return 0; /* Skip if reported radar event not overlapped our channels */ res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); if (!res) return 0; /* radar detected while operating, switch the channel. */ res = hostapd_dfs_start_channel_switch(iface); return res; } int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); /* Proceed only if DFS is not offloaded to the driver */ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) return 0; /* TODO add correct implementation here */ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); /* Handle cases where all channels were initially unavailable */ if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) hostapd_handle_dfs(iface); return 0; } int hostapd_is_dfs_required(struct hostapd_iface *iface) { int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res; - if (!iface->conf->ieee80211h || !iface->current_mode || + if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + !iface->conf->ieee80211h) || + !iface->current_mode || iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; /* Get start (first) channel for current configuration */ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); if (start_chan_idx == -1) return -1; /* Get number of used channels, depend on width */ n_chans = dfs_get_used_n_chans(iface, &n_chans1); /* Check if any of configured channels require DFS */ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); if (res) return res; if (start_chan_idx1 >= 0 && n_chans1 > 0) res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1); return res; } int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { /* This is called when the driver indicates that an offloaded DFS has * started CAC. */ hostapd_set_state(iface, HAPD_IFACE_DFS); /* TODO: How to check CAC time for ETSI weather channels? */ iface->dfs_cac_ms = 60000; wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " "seg1=%d cac_time=%ds", freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, iface->dfs_cac_ms / 1000); iface->cac_started = 1; os_get_reltime(&iface->dfs_cac_start); return 0; } /* * Main DFS handler for offloaded case. * 2 - continue channel/AP setup for non-DFS channel * 1 - continue channel/AP setup for DFS channel * 0 - channel/AP setup will be continued after CAC * -1 - hit critical error */ int hostapd_handle_dfs_offload(struct hostapd_iface *iface) { + int dfs_res; + wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", __func__, iface->cac_started); /* * If DFS has already been started, then we are being called from a * callback to continue AP/channel setup. Reset the CAC start flag and * return. */ if (iface->cac_started) { wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", __func__, iface->cac_started); iface->cac_started = 0; return 1; } - if (ieee80211_is_dfs(iface->freq, iface->hw_features, - iface->num_hw_features)) { - wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS", - __func__, iface->freq); + dfs_res = hostapd_is_dfs_required(iface); + if (dfs_res > 0) { + wpa_printf(MSG_DEBUG, + "%s: freq %d MHz requires DFS for %d chans", + __func__, iface->freq, dfs_res); return 0; } wpa_printf(MSG_DEBUG, "%s: freq %d MHz does not require DFS. Continue channel/AP setup", __func__, iface->freq); return 2; } int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width, int center_freq) { struct hostapd_channel_data *chan; struct hostapd_hw_modes *mode = iface->current_mode; int half_width; int res = 0; int i; if (!iface->conf->ieee80211h || !mode || mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; switch (width) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: half_width = 10; break; case CHAN_WIDTH_40: half_width = 20; break; case CHAN_WIDTH_80: case CHAN_WIDTH_80P80: half_width = 40; break; case CHAN_WIDTH_160: half_width = 80; break; default: wpa_printf(MSG_WARNING, "DFS chanwidth %d not supported", width); return 0; } for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; if (!(chan->flag & HOSTAPD_CHAN_RADAR)) continue; if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == HOSTAPD_CHAN_DFS_AVAILABLE) continue; if (center_freq - chan->freq < half_width && chan->freq - center_freq < half_width) res++; } wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s", center_freq - half_width, center_freq + half_width, res ? "yes" : "no"); return res; } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index b404e84affe5..22cce961063e 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,7072 +1,7074 @@ /* * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/sha384.h" #include "crypto/sha512.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/sae.h" #include "common/dpp.h" #include "common/ocv.h" #include "common/wpa_common.h" #include "common/wpa_ctrl.h" #include "common/ptksa_cache.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" #include "wps/wps.h" #include "fst/fst.h" #include "hostapd.h" #include "beacon.h" #include "ieee802_11_auth.h" #include "sta_info.h" #include "ieee802_1x.h" #include "wpa_auth.h" #include "pmksa_cache_auth.h" #include "wmm.h" #include "ap_list.h" #include "accounting.h" #include "ap_config.h" #include "ap_mlme.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "wnm_ap.h" #include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" #include "mbo_ap.h" #include "rrm.h" #include "taxonomy.h" #include "fils_hlp.h" #include "dpp_hostapd.h" #include "gas_query_ap.h" #ifdef CONFIG_FILS static struct wpabuf * prepare_auth_resp_fils(struct hostapd_data *hapd, struct sta_info *sta, u16 *resp, struct rsn_pmksa_cache_entry *pmksa, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len, int *is_pub); #endif /* CONFIG_FILS */ #ifdef CONFIG_PASN static int handle_auth_pasn_resp(struct hostapd_data *hapd, struct sta_info *sta, struct rsn_pmksa_cache_entry *pmksa, u16 status); #ifdef CONFIG_FILS static void pasn_fils_auth_resp(struct hostapd_data *hapd, struct sta_info *sta, u16 status, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len); #endif /* CONFIG_FILS */ #endif /* CONFIG_PASN */ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi, int from_queue); u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid) { u8 multi_ap_val = 0; if (!hapd->conf->multi_ap) return eid; if (hapd->conf->multi_ap & BACKHAUL_BSS) multi_ap_val |= MULTI_AP_BACKHAUL_BSS; if (hapd->conf->multi_ap & FRONTHAUL_BSS) multi_ap_val |= MULTI_AP_FRONTHAUL_BSS; return eid + add_multi_ap_ie(eid, 9, multi_ap_val); } u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; int h2e_required; if (hapd->iface->current_rates == NULL) return eid; *pos++ = WLAN_EID_SUPP_RATES; num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; h2e_required = (hapd->conf->sae_pwe == 1 || hostapd_sae_pw_id_in_use(hapd->conf) == 2) && hapd->conf->sae_pwe != 3 && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); if (h2e_required) num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ num = 8; } *pos++ = num; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; i++) { count++; *pos = hapd->iface->current_rates[i].rate / 5; if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } if (h2e_required && count < 8) { count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; } return pos; } u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; int i, num, count; int h2e_required; if (hapd->iface->current_rates == NULL) return eid; num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) num++; h2e_required = (hapd->conf->sae_pwe == 1 || hostapd_sae_pw_id_in_use(hapd->conf) == 2) && hapd->conf->sae_pwe != 3 && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); if (h2e_required) num++; if (num <= 8) return eid; num -= 8; *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = num; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; i++) { count++; if (count <= 8) continue; /* already in SuppRates IE */ *pos = hapd->iface->current_rates[i].rate / 5; if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; } if (h2e_required) { count++; if (count > 8) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; } return pos; } u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, size_t len) { size_t i; for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { if (hapd->conf->radio_measurements[i]) break; } if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) return eid; *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; *eid++ = RRM_CAPABILITIES_IE_LEN; os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); return eid + RRM_CAPABILITIES_IE_LEN; } u16 hostapd_own_capab_info(struct hostapd_data *hapd) { int capab = WLAN_CAPABILITY_ESS; int privacy = 0; int dfs; int i; /* Check if any of configured channels require DFS */ dfs = hostapd_is_dfs_required(hapd->iface); if (dfs < 0) { wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", dfs); dfs = 0; } if (hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; #ifdef CONFIG_WEP privacy = hapd->conf->ssid.wep.keys_set; if (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)) privacy = 1; #endif /* CONFIG_WEP */ if (hapd->conf->wpa) privacy = 1; #ifdef CONFIG_HS20 if (hapd->conf->osen) privacy = 1; #endif /* CONFIG_HS20 */ if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; /* * Currently, Spectrum Management capability bit is set when directly * requested in configuration by spectrum_mgmt_required or when AP is * running on DFS channel. * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit */ if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && (hapd->iconf->spectrum_mgmt_required || dfs)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { if (hapd->conf->radio_measurements[i]) { capab |= IEEE80211_CAP_RRM; break; } } return capab; } #ifdef CONFIG_WEP #ifndef CONFIG_NO_RC4 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, int iswep) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication (shared key, transaction %d)", auth_transaction); if (auth_transaction == 1) { if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (os_get_random(key, sizeof(key)) < 0) { os_free(sta->challenge); sta->challenge = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } return 0; } if (auth_transaction != 3) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* Transaction 3 */ if (!iswep || !sta->challenge || !challenge || os_memcmp_const(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "shared key authentication - invalid " "challenge-response"); return WLAN_STATUS_CHALLENGE_FAIL; } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (shared key)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); os_free(sta->challenge); sta->challenge = NULL; return 0; } #endif /* CONFIG_NO_RC4 */ #endif /* CONFIG_WEP */ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, const u8 *ies, size_t ies_len, const char *dbg) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) return -1; reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_AUTH); os_memcpy(reply->da, dst, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, bssid, ETH_ALEN); reply->u.auth.auth_alg = host_to_le16(auth_alg); reply->u.auth.auth_transaction = host_to_le16(auth_transaction); reply->u.auth.status_code = host_to_le16(resp); if (ies && ies_len) os_memcpy(reply->u.auth.variable, ies, ies_len); wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len, dbg); #ifdef CONFIG_TESTING_OPTIONS #ifdef CONFIG_SAE if (hapd->conf->sae_confirm_immediate == 2 && auth_alg == WLAN_AUTH_SAE) { if (auth_transaction == 1 && sta && (resp == WLAN_STATUS_SUCCESS || resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT || resp == WLAN_STATUS_SAE_PK)) { wpa_printf(MSG_DEBUG, "TESTING: Postpone SAE Commit transmission until Confirm is ready"); os_free(sta->sae_postponed_commit); sta->sae_postponed_commit = buf; sta->sae_postponed_commit_len = rlen; return WLAN_STATUS_SUCCESS; } if (auth_transaction == 2 && sta && sta->sae_postponed_commit) { wpa_printf(MSG_DEBUG, "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm"); if (hostapd_drv_send_mlme(hapd, sta->sae_postponed_commit, sta->sae_postponed_commit_len, 0, NULL, 0, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); os_free(sta->sae_postponed_commit); sta->sae_postponed_commit = NULL; sta->sae_postponed_commit_len = 0; } } #endif /* CONFIG_SAE */ #endif /* CONFIG_TESTING_OPTIONS */ if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else reply_res = WLAN_STATUS_SUCCESS; os_free(buf); return reply_res; } #ifdef CONFIG_IEEE80211R_AP static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta; int reply_res; reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT, auth_transaction, status, ies, ies_len, "auth-ft-finish"); sta = ap_get_sta(hapd, dst); if (sta == NULL) return; if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || status != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; return; } if (status != WLAN_STATUS_SUCCESS) return; hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE static void sae_set_state(struct sta_info *sta, enum sae_state state, const char *reason) { wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)", sae_state_txt(sta->sae->state), sae_state_txt(state), MAC2STR(sta->addr), reason); sta->sae->state = state; } static const char * sae_get_password(struct hostapd_data *hapd, struct sta_info *sta, const char *rx_id, struct sae_password_entry **pw_entry, struct sae_pt **s_pt, const struct sae_pk **s_pk) { const char *password = NULL; struct sae_password_entry *pw; struct sae_pt *pt = NULL; const struct sae_pk *pk = NULL; for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { if (!is_broadcast_ether_addr(pw->peer_addr) && os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) continue; if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) continue; if (rx_id && pw->identifier && os_strcmp(rx_id, pw->identifier) != 0) continue; password = pw->password; pt = pw->pt; if (!(hapd->conf->mesh & MESH_ENABLED)) pk = pw->pk; break; } if (!password) { password = hapd->conf->ssid.wpa_passphrase; pt = hapd->conf->ssid.pt; } if (pw_entry) *pw_entry = pw; if (s_pt) *s_pt = pt; if (s_pk) *s_pk = pk; return password; } static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, int update, int status_code) { struct wpabuf *buf; const char *password = NULL; struct sae_password_entry *pw; const char *rx_id = NULL; int use_pt = 0; struct sae_pt *pt = NULL; const struct sae_pk *pk = NULL; if (sta->sae->tmp) { rx_id = sta->sae->tmp->pw_id; use_pt = sta->sae->h2e; #ifdef CONFIG_SAE_PK os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN); os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN); #endif /* CONFIG_SAE_PK */ } if (rx_id && hapd->conf->sae_pwe != 3) use_pt = 1; else if (status_code == WLAN_STATUS_SUCCESS) use_pt = 0; else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || status_code == WLAN_STATUS_SAE_PK) use_pt = 1; password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk); if (!password || (use_pt && !pt)) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } if (update && use_pt && sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr, NULL, pk) < 0) return NULL; if (update && !use_pt && sae_prepare_commit(hapd->own_addr, sta->addr, (u8 *) password, os_strlen(password), sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } if (pw && pw->vlan_id) { if (!sta->sae->tmp) { wpa_printf(MSG_INFO, "SAE: No temporary data allocated - cannot store VLAN ID"); return NULL; } sta->sae->tmp->vlan_id = pw->vlan_id; } buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + (rx_id ? 3 + os_strlen(rx_id) : 0)); if (buf && sae_write_commit(sta->sae, buf, sta->sae->tmp ? sta->sae->tmp->anti_clogging_token : NULL, rx_id) < 0) { wpabuf_free(buf); buf = NULL; } return buf; } static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta) { struct wpabuf *buf; buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; #ifdef CONFIG_SAE_PK #ifdef CONFIG_TESTING_OPTIONS if (sta->sae->tmp) sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_SAE_PK */ if (sae_write_confirm(sta->sae, buf) < 0) { wpabuf_free(buf); return NULL; } return buf; } static int auth_sae_send_commit(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, int update, int status_code) { struct wpabuf *data; int reply_res; u16 status; data = auth_build_sae_commit(hapd, sta, update, status_code); if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (sta->sae->tmp && sta->sae->pk) status = WLAN_STATUS_SAE_PK; else if (sta->sae->tmp && sta->sae->h2e) status = WLAN_STATUS_SAE_HASH_TO_ELEMENT; else status = WLAN_STATUS_SUCCESS; #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->sae_commit_status >= 0 && hapd->conf->sae_commit_status != status) { wpa_printf(MSG_INFO, "TESTING: Override SAE commit status code %u --> %d", status, hapd->conf->sae_commit_status); status = hapd->conf->sae_commit_status; } #endif /* CONFIG_TESTING_OPTIONS */ reply_res = send_auth_reply(hapd, sta, sta->addr, bssid, WLAN_AUTH_SAE, 1, status, wpabuf_head(data), wpabuf_len(data), "sae-send-commit"); wpabuf_free(data); return reply_res; } static int auth_sae_send_confirm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid) { struct wpabuf *data; int reply_res; data = auth_build_sae_confirm(hapd, sta); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; reply_res = send_auth_reply(hapd, sta, sta->addr, bssid, WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), wpabuf_len(data), "sae-send-confirm"); wpabuf_free(data); return reply_res; } #endif /* CONFIG_SAE */ #if defined(CONFIG_SAE) || defined(CONFIG_PASN) static int use_anti_clogging(struct hostapd_data *hapd) { struct sta_info *sta; unsigned int open = 0; if (hapd->conf->anti_clogging_threshold == 0) return 1; for (sta = hapd->sta_list; sta; sta = sta->next) { #ifdef CONFIG_SAE if (sta->sae && (sta->sae->state == SAE_COMMITTED || sta->sae->state == SAE_CONFIRMED)) open++; #endif /* CONFIG_SAE */ #ifdef CONFIG_PASN if (sta->pasn && sta->pasn->ecdh) open++; #endif /* CONFIG_PASN */ if (open >= hapd->conf->anti_clogging_threshold) return 1; } #ifdef CONFIG_SAE /* In addition to already existing open SAE sessions, check whether * there are enough pending commit messages in the processing queue to * potentially result in too many open sessions. */ if (open + dl_list_len(&hapd->sae_commit_queue) >= hapd->conf->anti_clogging_threshold) return 1; #endif /* CONFIG_SAE */ return 0; } static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr, u8 *idx) { u8 hash[SHA256_MAC_LEN]; if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key), addr, ETH_ALEN, hash) < 0) return -1; *idx = hash[0]; return 0; } static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr, const u8 *token, size_t token_len) { u8 mac[SHA256_MAC_LEN]; const u8 *addrs[2]; size_t len[2]; u16 token_idx; u8 idx; if (token_len != SHA256_MAC_LEN || comeback_token_hash(hapd, addr, &idx) < 0) return -1; token_idx = hapd->comeback_pending_idx[idx]; if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { wpa_printf(MSG_DEBUG, "Comeback: Invalid anti-clogging token from " MACSTR " - token_idx 0x%04x, expected 0x%04x", MAC2STR(addr), WPA_GET_BE16(token), token_idx); return -1; } addrs[0] = addr; len[0] = ETH_ALEN; addrs[1] = token; len[1] = 2; if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key), 2, addrs, len, mac) < 0 || os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) return -1; hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */ return 0; } static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, int group, const u8 *addr, int h2e) { struct wpabuf *buf; u8 *token; struct os_reltime now; u8 idx[2]; const u8 *addrs[2]; size_t len[2]; u8 p_idx; u16 token_idx; os_get_reltime(&now); if (!os_reltime_initialized(&hapd->last_comeback_key_update) || os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) || hapd->comeback_idx == 0xffff) { if (random_get_bytes(hapd->comeback_key, sizeof(hapd->comeback_key)) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key", hapd->comeback_key, sizeof(hapd->comeback_key)); hapd->last_comeback_key_update = now; hapd->comeback_idx = 0; os_memset(hapd->comeback_pending_idx, 0, sizeof(hapd->comeback_pending_idx)); } buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN); if (buf == NULL) return NULL; if (group) wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ if (h2e) { /* Encapsulate Anti-clogging Token field in a container IE */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN); wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); } if (comeback_token_hash(hapd, addr, &p_idx) < 0) { wpabuf_free(buf); return NULL; } token_idx = hapd->comeback_pending_idx[p_idx]; if (!token_idx) { hapd->comeback_idx++; token_idx = hapd->comeback_idx; hapd->comeback_pending_idx[p_idx] = token_idx; } WPA_PUT_BE16(idx, token_idx); token = wpabuf_put(buf, SHA256_MAC_LEN); addrs[0] = addr; len[0] = ETH_ALEN; addrs[1] = idx; len[1] = sizeof(idx); if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key), 2, addrs, len, token) < 0) { wpabuf_free(buf); return NULL; } WPA_PUT_BE16(token, token_idx); return buf; } #endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */ #ifdef CONFIG_SAE static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->sae->sync > hapd->conf->sae_sync) { sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync"); sta->sae->sync = 0; return -1; } return 0; } static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = eloop_data; int ret; if (sae_check_big_sync(hapd, sta)) return; sta->sae->sync++; wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR " (sync=%d state=%s)", MAC2STR(sta->addr), sta->sae->sync, sae_state_txt(sta->sae->state)); switch (sta->sae->state) { case SAE_COMMITTED: ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; case SAE_CONFIRMED: ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; default: ret = -1; break; } if (ret != WLAN_STATUS_SUCCESS) wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret); } void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) { eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); } static void sae_set_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) { if (!(hapd->conf->mesh & MESH_ENABLED)) return; eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); } static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, struct sta_info *sta, u16 status) { struct external_auth params; os_memset(¶ms, 0, sizeof(params)); params.status = status; params.bssid = sta->addr; if (status == WLAN_STATUS_SUCCESS && sta->sae && !hapd->conf->disable_pmksa_caching) params.pmkid = sta->sae->pmkid; hostapd_drv_send_external_auth_status(hapd, ¶ms); } void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN struct vlan_description vlan_desc; if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) { wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR " to VLAN ID %d", MAC2STR(sta->addr), sta->sae->tmp->vlan_id); os_memset(&vlan_desc, 0, sizeof(vlan_desc)); vlan_desc.notempty = 1; vlan_desc.untagged = sta->sae->tmp->vlan_id; if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { wpa_printf(MSG_INFO, "Invalid VLAN ID %d in sae_password", sta->sae->tmp->vlan_id); return; } if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 || ap_sta_bind_vlan(hapd, sta) < 0) { wpa_printf(MSG_INFO, "Failed to assign VLAN ID %d from sae_password to " MACSTR, sta->sae->tmp->vlan_id, MAC2STR(sta->addr)); return; } } #endif /* CONFIG_NO_VLAN */ sta->flags |= WLAN_STA_AUTH; sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0); sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar; sta->sae->peer_commit_scalar = NULL; wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); } static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, u16 auth_transaction, u16 status_code, int allow_reuse, int *sta_removed) { int ret; *sta_removed = 0; if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u", MAC2STR(sta->addr), sae_state_txt(sta->sae->state), auth_transaction); switch (sta->sae->state) { case SAE_NOTHING: if (auth_transaction == 1) { if (sta->sae->tmp) { sta->sae->h2e = (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || status_code == WLAN_STATUS_SAE_PK); sta->sae->pk = status_code == WLAN_STATUS_SAE_PK; } ret = auth_sae_send_commit(hapd, sta, bssid, !allow_reuse, status_code); if (ret) return ret; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* * In mesh case, both Commit and Confirm are sent * immediately. In infrastructure BSS, by default, only * a single Authentication frame (Commit) is expected * from the AP here and the second one (Confirm) will * be sent once the STA has sent its second * Authentication frame (Confirm). This behavior can be * overridden with explicit configuration so that the * infrastructure BSS case sends both frames together. */ if ((hapd->conf->mesh & MESH_ENABLED) || hapd->conf->sae_confirm_immediate) { /* * Send both Commit and Confirm immediately * based on SAE finite state machine * Nothing -> Confirm transition. */ ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm (mesh)"); } else { /* * For infrastructure BSS, send only the Commit * message now to get alternating sequence of * Authentication frames between the AP and STA. * Confirm will be sent in * Committed -> Confirmed/Accepted transition * when receiving Confirm from STA. */ } sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE confirm before commit"); } break; case SAE_COMMITTED: sae_clear_retransmit_timer(hapd, sta); if (auth_transaction == 1) { if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else if (hapd->conf->mesh & MESH_ENABLED) { /* * In mesh case, follow SAE finite state machine and * send Commit now, if sync count allows. */ if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; ret = auth_sae_send_commit(hapd, sta, bssid, 0, status_code); if (ret) return ret; sae_set_retransmit_timer(hapd, sta); } else { /* * For instructure BSS, send the postponed Confirm from * Nothing -> Confirmed transition that was reduced to * Nothing -> Committed above. */ ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); /* * Since this was triggered on Confirm RX, run another * step to get to Accepted without waiting for * additional events. */ return sae_sm_step(hapd, sta, bssid, auth_transaction, WLAN_STATUS_SUCCESS, 0, sta_removed); } break; case SAE_CONFIRMED: sae_clear_retransmit_timer(hapd, sta); if (auth_transaction == 1) { if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; ret = auth_sae_send_commit(hapd, sta, bssid, 1, status_code); if (ret) return ret; if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; sae_set_retransmit_timer(hapd, sta); } else { sta->sae->send_confirm = 0xffff; sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: if (auth_transaction == 1 && (hapd->conf->mesh & MESH_ENABLED)) { wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", MAC2STR(sta->addr)); wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); ap_free_sta(hapd, sta); *sta_removed = 1; } else if (auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); ret = auth_sae_send_commit(hapd, sta, bssid, 1, status_code); if (ret) return ret; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else { if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; ret = auth_sae_send_confirm(hapd, sta, bssid); sae_clear_temp_data(sta->sae); if (ret) return ret; } break; default: wpa_printf(MSG_ERROR, "SAE: invalid state %d", sta->sae->state); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) { struct sae_data *sae = sta->sae; int i, *groups = hapd->conf->sae_groups; int default_groups[] = { 19, 0 }; if (sae->state != SAE_COMMITTED) return; wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); if (!groups) groups = default_groups; for (i = 0; groups[i] > 0; i++) { if (sae->group == groups[i]) break; } if (groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: Previously selected group not found from the current configuration"); return; } for (;;) { i++; if (groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: No alternative group enabled"); return; } if (sae_set_group(sae, groups[i]) < 0) continue; break; } wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]); } static int sae_status_success(struct hostapd_data *hapd, u16 status_code) { int sae_pwe = hapd->conf->sae_pwe; int id_in_use; bool sae_pk = false; id_in_use = hostapd_sae_pw_id_in_use(hapd->conf); if (id_in_use == 2 && sae_pwe != 3) sae_pwe = 1; else if (id_in_use == 1 && sae_pwe == 0) sae_pwe = 2; #ifdef CONFIG_SAE_PK sae_pk = hostapd_sae_pk_in_use(hapd->conf); if (sae_pwe == 0 && sae_pk) sae_pwe = 2; #endif /* CONFIG_SAE_PK */ return ((sae_pwe == 0 || sae_pwe == 3) && status_code == WLAN_STATUS_SUCCESS) || (sae_pwe == 1 && (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || (sae_pk && status_code == WLAN_STATUS_SAE_PK))) || (sae_pwe == 2 && (status_code == WLAN_STATUS_SUCCESS || status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || (sae_pk && status_code == WLAN_STATUS_SAE_PK))); } static int sae_is_group_enabled(struct hostapd_data *hapd, int group) { int *groups = hapd->conf->sae_groups; int default_groups[] = { 19, 0 }; int i; if (!groups) groups = default_groups; for (i = 0; groups[i] > 0; i++) { if (groups[i] == group) return 1; } return 0; } static int check_sae_rejected_groups(struct hostapd_data *hapd, struct sae_data *sae) { const struct wpabuf *groups; size_t i, count; const u8 *pos; if (!sae->tmp) return 0; groups = sae->tmp->peer_rejected_groups; if (!groups) return 0; pos = wpabuf_head(groups); count = wpabuf_len(groups) / 2; for (i = 0; i < count; i++) { int enabled; u16 group; group = WPA_GET_LE16(pos); pos += 2; enabled = sae_is_group_enabled(hapd, group); wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s", group, enabled ? "enabled" : "disabled"); if (enabled) return 1; } return 0; } static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) { int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; int *groups = hapd->conf->sae_groups; int default_groups[] = { 19, 0 }; const u8 *pos, *end; int sta_removed = 0; bool success_status; if (!groups) groups = default_groups; #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; resp = status_code; send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, pos, end - pos, "auth-sae-reflection-attack"); goto remove_sta; } if (hapd->conf->sae_commit_override && auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, wpabuf_head(hapd->conf->sae_commit_override), wpabuf_len(hapd->conf->sae_commit_override), "sae-commit-override"); goto remove_sta; } #endif /* CONFIG_TESTING_OPTIONS */ if (!sta->sae) { if (auth_transaction != 1 || !sae_status_success(hapd, status_code)) { wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u", status_code); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } sta->sae = os_zalloc(sizeof(*sta->sae)); if (!sta->sae) { resp = -1; goto remove_sta; } sae_set_state(sta, SAE_NOTHING, "Init"); sta->sae->sync = 0; } if (sta->mesh_sae_pmksa_caching) { wpa_printf(MSG_DEBUG, "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication"); wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); sta->mesh_sae_pmksa_caching = 0; } if (auth_transaction == 1) { const u8 *token = NULL; size_t token_len = 0; int allow_reuse = 0; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit, status=%u (%s))", status_code, status2str(status_code)); if ((hapd->conf->mesh & MESH_ENABLED) && status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && sta->sae->tmp) { pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; if (pos + sizeof(le16) > end) { wpa_printf(MSG_ERROR, "SAE: Too short anti-clogging token request"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } resp = sae_group_allowed(sta->sae, groups, WPA_GET_LE16(pos)); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Invalid group in anti-clogging token request"); goto reply; } pos += sizeof(le16); wpabuf_free(sta->sae->tmp->anti_clogging_token); sta->sae->tmp->anti_clogging_token = wpabuf_alloc_copy(pos, end - pos); if (sta->sae->tmp->anti_clogging_token == NULL) { wpa_printf(MSG_ERROR, "SAE: Failed to alloc for anti-clogging token"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto remove_sta; } /* * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code * is 76, a new Commit Message shall be constructed * with the Anti-Clogging Token from the received * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0, status_code); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); goto remove_sta; } sae_set_state(sta, SAE_COMMITTED, "Sent Commit (anti-clogging token case in mesh)"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return; } if ((hapd->conf->mesh & MESH_ENABLED) && status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && sta->sae->tmp) { wpa_printf(MSG_DEBUG, "SAE: Peer did not accept our SAE group"); sae_pick_next_group(hapd, sta); goto remove_sta; } if (!sae_status_success(hapd, status_code)) goto remove_sta; if (!(hapd->conf->mesh & MESH_ENABLED) && sta->sae->state == SAE_COMMITTED) { /* This is needed in the infrastructure BSS case to * address a sequence where a STA entry may remain in * hostapd across two attempts to do SAE authentication * by the same STA. The second attempt may end up trying * to use a different group and that would not be * allowed if we remain in Committed state with the * previously set parameters. */ pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; if (end - pos >= (int) sizeof(le16) && sae_group_allowed(sta->sae, groups, WPA_GET_LE16(pos)) == WLAN_STATUS_SUCCESS) { /* Do not waste resources deriving the same PWE * again since the same group is reused. */ sae_set_state(sta, SAE_NOTHING, "Allow previous PWE to be reused"); allow_reuse = 1; } else { sae_set_state(sta, SAE_NOTHING, "Clear existing state to allow restart"); sae_clear_data(sta->sae); } } resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, &token_len, groups, status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || status_code == WLAN_STATUS_SAE_PK); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", MAC2STR(sta->addr)); goto remove_sta; } if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR, MAC2STR(sta->addr)); sae_clear_retransmit_timer(hapd, sta); sae_set_state(sta, SAE_NOTHING, "Unknown Password Identifier"); goto remove_sta; } if (token && check_comeback_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, MAC2STR(sta->addr)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto remove_sta; } if (resp != WLAN_STATUS_SUCCESS) goto reply; if (check_sae_rejected_groups(hapd, sta->sae)) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } if (!token && use_anti_clogging(hapd) && !allow_reuse) { int h2e = 0; wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); if (sta->sae->tmp) h2e = sta->sae->h2e; if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || status_code == WLAN_STATUS_SAE_PK) h2e = 1; data = auth_build_token_req(hapd, sta->sae->group, sta->addr, h2e); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) sae_set_state(sta, SAE_NOTHING, "Request anti-clogging token case in mesh"); goto reply; } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, status_code, allow_reuse, &sta_removed); } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE authentication (RX confirm, status=%u (%s))", status_code, status2str(status_code)); if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; if (sta->sae->state >= SAE_CONFIRMED || !(hapd->conf->mesh & MESH_ENABLED)) { const u8 *var; size_t var_len; u16 peer_send_confirm; var = mgmt->u.auth.variable; var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable; if (var_len < 2) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } peer_send_confirm = WPA_GET_LE16(var); if (sta->sae->state == SAE_ACCEPTED && (peer_send_confirm <= sta->sae->rc || peer_send_confirm == 0xffff)) { wpa_printf(MSG_DEBUG, "SAE: Silently ignore unexpected Confirm from peer " MACSTR " (peer-send-confirm=%u Rc=%u)", MAC2STR(sta->addr), peer_send_confirm, sta->sae->rc); return; } if (sae_check_confirm(sta->sae, var, var_len) < 0) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } sta->sae->rc = peer_send_confirm; } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, status_code, 0, &sta_removed); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unexpected SAE authentication transaction %u (status=%u (%s))", auth_transaction, status_code, status2str(status_code)); if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } reply: if (!sta_removed && resp != WLAN_STATUS_SUCCESS) { pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; /* Copy the Finite Cyclic Group field from the request if we * rejected it as unsupported group. */ if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && !data && end - pos >= 2) data = wpabuf_alloc_copy(pos, 2); sae_sme_send_external_auth_status(hapd, sta, resp); send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0, "auth-sae"); } remove_sta: if (auth_transaction == 1) success_status = sae_status_success(hapd, status_code); else success_status = status_code == WLAN_STATUS_SUCCESS; if (!sta_removed && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || !success_status)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } wpabuf_free(data); } /** * auth_sae_init_committed - Send COMMIT and start SAE in committed state * @hapd: BSS data for the device initiating the authentication * @sta: the peer to which commit authentication frame is sent * * This function implements Init event handling (IEEE Std 802.11-2012, * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the * sta->sae structure should be initialized appropriately via a call to * sae_prepare_commit(). */ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) { int ret; if (!sta->sae || !sta->sae->tmp) return -1; if (sta->sae->state != SAE_NOTHING) return -1; ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1); if (ret) return -1; sae_set_state(sta, SAE_COMMITTED, "Init and sent commit"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return 0; } void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) { struct hostapd_data *hapd = eloop_ctx; struct hostapd_sae_commit_queue *q; unsigned int queue_len; q = dl_list_first(&hapd->sae_commit_queue, struct hostapd_sae_commit_queue, list); if (!q) return; wpa_printf(MSG_DEBUG, "SAE: Process next available message from queue"); dl_list_del(&q->list); handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len, q->rssi, 1); os_free(q); if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) return; queue_len = dl_list_len(&hapd->sae_commit_queue); eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, hapd, NULL); } static void auth_sae_queue(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi) { struct hostapd_sae_commit_queue *q, *q2; unsigned int queue_len; const struct ieee80211_mgmt *mgmt2; queue_len = dl_list_len(&hapd->sae_commit_queue); if (queue_len >= 15) { wpa_printf(MSG_DEBUG, "SAE: No more room in message queue - drop the new frame from " MACSTR, MAC2STR(mgmt->sa)); return; } wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from " MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa), queue_len); q = os_zalloc(sizeof(*q) + len); if (!q) return; q->rssi = rssi; q->len = len; os_memcpy(q->msg, mgmt, len); /* Check whether there is already a queued Authentication frame from the * same station with the same transaction number and if so, replace that * queue entry with the new one. This avoids issues with a peer that * sends multiple times (e.g., due to frequent SAE retries). There is no * point in us trying to process the old attempts after a new one has * obsoleted them. */ dl_list_for_each(q2, &hapd->sae_commit_queue, struct hostapd_sae_commit_queue, list) { mgmt2 = (const struct ieee80211_mgmt *) q2->msg; if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 && mgmt->u.auth.auth_transaction == mgmt2->u.auth.auth_transaction) { wpa_printf(MSG_DEBUG, "SAE: Replace queued message from same STA with same transaction number"); dl_list_add(&q2->list, &q->list); dl_list_del(&q2->list); os_free(q2); goto queued; } } /* No pending identical entry, so add to the end of the queue */ dl_list_add_tail(&hapd->sae_commit_queue, &q->list); queued: if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) return; eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, hapd, NULL); } static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) { struct hostapd_sae_commit_queue *q; const struct ieee80211_mgmt *mgmt; dl_list_for_each(q, &hapd->sae_commit_queue, struct hostapd_sae_commit_queue, list) { mgmt = (const struct ieee80211_mgmt *) q->msg; if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0) return 1; } return 0; } #endif /* CONFIG_SAE */ static u16 wpa_res_to_status_code(enum wpa_validate_result res) { switch (res) { case WPA_IE_OK: return WLAN_STATUS_SUCCESS; case WPA_INVALID_IE: return WLAN_STATUS_INVALID_IE; case WPA_INVALID_GROUP: return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; case WPA_INVALID_PAIRWISE: return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; case WPA_INVALID_AKMP: return WLAN_STATUS_AKMP_NOT_VALID; case WPA_NOT_ENABLED: return WLAN_STATUS_INVALID_IE; case WPA_ALLOC_FAIL: return WLAN_STATUS_UNSPECIFIED_FAILURE; case WPA_MGMT_FRAME_PROTECTION_VIOLATION: return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; case WPA_INVALID_MGMT_GROUP_CIPHER: return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; case WPA_INVALID_MDIE: return WLAN_STATUS_INVALID_MDIE; case WPA_INVALID_PROTO: return WLAN_STATUS_INVALID_IE; case WPA_INVALID_PMKID: return WLAN_STATUS_INVALID_PMKID; case WPA_DENIED_OTHER_REASON: return WLAN_STATUS_ASSOC_DENIED_UNSPEC; } return WLAN_STATUS_INVALID_IE; } #ifdef CONFIG_FILS static void handle_auth_fils_finish(struct hostapd_data *hapd, struct sta_info *sta, u16 resp, struct wpabuf *data, int pub); void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, const u8 *pos, size_t len, u16 auth_alg, u16 auth_transaction, u16 status_code, void (*cb)(struct hostapd_data *hapd, struct sta_info *sta, u16 resp, struct wpabuf *data, int pub)) { u16 resp = WLAN_STATUS_SUCCESS; const u8 *end; struct ieee802_11_elems elems; enum wpa_validate_result res; struct wpa_ie_data rsn; struct rsn_pmksa_cache_entry *pmksa = NULL; if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) return; end = pos + len; wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", pos, end - pos); /* TODO: FILS PK */ #ifdef CONFIG_FILS_SK_PFS if (auth_alg == WLAN_AUTH_FILS_SK_PFS) { u16 group; struct wpabuf *pub; size_t elem_len; /* Using FILS PFS */ /* Finite Cyclic Group */ if (end - pos < 2) { wpa_printf(MSG_DEBUG, "FILS: No room for Finite Cyclic Group"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } group = WPA_GET_LE16(pos); pos += 2; if (group != hapd->conf->fils_dh_group) { wpa_printf(MSG_DEBUG, "FILS: Unsupported Finite Cyclic Group: %u (expected %u)", group, hapd->conf->fils_dh_group); resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; goto fail; } crypto_ecdh_deinit(sta->fils_ecdh); sta->fils_ecdh = crypto_ecdh_init(group); if (!sta->fils_ecdh) { wpa_printf(MSG_INFO, "FILS: Could not initialize ECDH with group %d", group); resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; goto fail; } pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); if (!pub) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive ECDH public key"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } elem_len = wpabuf_len(pub); wpabuf_free(pub); /* Element */ if ((size_t) (end - pos) < elem_len) { wpa_printf(MSG_DEBUG, "FILS: No room for Element"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpabuf_free(sta->fils_g_sta); sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len); wpabuf_clear_free(sta->fils_dh_ss); sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1, pos, elem_len); if (!sta->fils_dh_ss) { wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss); pos += elem_len; } else { crypto_ecdh_deinit(sta->fils_ecdh); sta->fils_ecdh = NULL; wpabuf_clear_free(sta->fils_dh_ss); sta->fils_dh_ss = NULL; } #endif /* CONFIG_FILS_SK_PFS */ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } /* RSNE */ wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie, elems.rsn_ie_len); if (!elems.rsn_ie || wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &rsn) < 0) { wpa_printf(MSG_DEBUG, "FILS: No valid RSN element"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (!sta->wpa_sm) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (!sta->wpa_sm) { wpa_printf(MSG_DEBUG, "FILS: Failed to initialize RSN state machine"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, elems.rsn_ie - 2, elems.rsn_ie_len + 2, elems.rsnxe ? elems.rsnxe - 2 : NULL, elems.rsnxe ? elems.rsnxe_len + 2 : 0, elems.mdie, elems.mdie_len, NULL, 0); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) goto fail; if (!elems.fils_nonce) { wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, FILS_NONCE_LEN); os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); /* PMKID List */ if (rsn.pmkid && rsn.num_pmkid > 0) { u8 num; const u8 *pmkid; wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", rsn.pmkid, rsn.num_pmkid * PMKID_LEN); pmkid = rsn.pmkid; num = rsn.num_pmkid; while (num) { wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, pmkid); if (pmksa) break; pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth, sta->addr, pmkid); if (pmksa) break; pmkid += PMKID_LEN; num--; } } if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) { wpa_printf(MSG_DEBUG, "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore", wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp); pmksa = NULL; } if (pmksa) wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry"); /* FILS Session */ if (!elems.fils_session) { wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, FILS_SESSION_LEN); os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN); /* Wrapped Data */ if (elems.wrapped_data) { wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", elems.wrapped_data, elems.wrapped_data_len); if (!pmksa) { #ifndef CONFIG_NO_RADIUS if (!sta->eapol_sm) { sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); } wpa_printf(MSG_DEBUG, "FILS: Forward EAP-Initiate/Re-auth to authentication server"); ieee802_1x_encapsulate_radius( hapd, sta, elems.wrapped_data, elems.wrapped_data_len); sta->fils_pending_cb = cb; wpa_printf(MSG_DEBUG, "FILS: Will send Authentication frame once the response from authentication server is available"); sta->flags |= WLAN_STA_PENDING_FILS_ERP; /* Calculate pending PMKID here so that we do not need * to maintain a copy of the EAP-Initiate/Reauth * message. */ if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm), elems.wrapped_data, elems.wrapped_data_len, sta->fils_erp_pmkid) == 0) sta->fils_erp_pmkid_set = 1; return; #else /* CONFIG_NO_RADIUS */ resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; #endif /* CONFIG_NO_RADIUS */ } } fail: if (cb) { struct wpabuf *data; int pub = 0; data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL, NULL, 0, &pub); if (!data) { wpa_printf(MSG_DEBUG, "%s: prepare_auth_resp_fils() returned failure", __func__); } cb(hapd, sta, resp, data, pub); } } static struct wpabuf * prepare_auth_resp_fils(struct hostapd_data *hapd, struct sta_info *sta, u16 *resp, struct rsn_pmksa_cache_entry *pmksa, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len, int *is_pub) { u8 fils_nonce[FILS_NONCE_LEN]; size_t ielen; struct wpabuf *data = NULL; const u8 *ie; u8 *ie_buf = NULL; const u8 *pmk = NULL; size_t pmk_len = 0; u8 pmk_buf[PMK_LEN_MAX]; struct wpabuf *pub = NULL; if (*resp != WLAN_STATUS_SUCCESS) goto fail; ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); if (!ie) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (pmksa) { /* Add PMKID of the selected PMKSA into RSNE */ ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN); if (!ie_buf) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } os_memcpy(ie_buf, ie, ielen); if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } ie = ie_buf; } if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", fils_nonce, FILS_NONCE_LEN); #ifdef CONFIG_FILS_SK_PFS if (sta->fils_dh_ss && sta->fils_ecdh) { pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); if (!pub) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } } #endif /* CONFIG_FILS_SK_PFS */ data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0)); if (!data) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } /* TODO: FILS PK */ #ifdef CONFIG_FILS_SK_PFS if (pub) { /* Finite Cyclic Group */ wpabuf_put_le16(data, hapd->conf->fils_dh_group); /* Element */ wpabuf_put_buf(data, pub); } #endif /* CONFIG_FILS_SK_PFS */ /* RSNE */ wpabuf_put_data(data, ie, ielen); /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */ #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */ int res; int use_sha384 = wpa_key_mgmt_sha384( wpa_auth_sta_key_mgmt(sta->wpa_sm)); res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384, wpabuf_put(data, 0), wpabuf_tailroom(data)); if (res < 0) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpabuf_put(data, res); } #endif /* CONFIG_IEEE80211R_AP */ /* FILS Nonce */ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ /* Element ID Extension */ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); /* FILS Session */ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */ /* Element ID Extension */ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION); wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN); /* Wrapped Data */ if (!pmksa && erp_resp) { wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */ /* Element ID Extension */ wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA); wpabuf_put_buf(data, erp_resp); if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), msk, msk_len, sta->fils_snonce, fils_nonce, sta->fils_dh_ss ? wpabuf_head(sta->fils_dh_ss) : NULL, sta->fils_dh_ss ? wpabuf_len(sta->fils_dh_ss) : 0, pmk_buf, &pmk_len)) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; wpabuf_free(data); data = NULL; goto fail; } pmk = pmk_buf; /* Don't use DHss in PTK derivation if PMKSA caching is not * used. */ wpabuf_clear_free(sta->fils_dh_ss); sta->fils_dh_ss = NULL; if (sta->fils_erp_pmkid_set) { /* TODO: get PMKLifetime from WPA parameters */ unsigned int dot11RSNAConfigPMKLifetime = 43200; int session_timeout; session_timeout = dot11RSNAConfigPMKLifetime; if (sta->session_timeout_set) { struct os_reltime now, diff; os_get_reltime(&now); os_reltime_sub(&sta->session_timeout, &now, &diff); session_timeout = diff.sec; } sta->fils_erp_pmkid_set = 0; wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len, sta->fils_erp_pmkid); if (!hapd->conf->disable_pmksa_caching && wpa_auth_pmksa_add2( hapd->wpa_auth, sta->addr, pmk, pmk_len, sta->fils_erp_pmkid, session_timeout, wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) { wpa_printf(MSG_ERROR, "FILS: Failed to add PMKSA cache entry based on ERP"); } } } else if (pmksa) { pmk = pmksa->pmk; pmk_len = pmksa->pmk_len; } if (!pmk) { wpa_printf(MSG_DEBUG, "FILS: No PMK available"); *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; wpabuf_free(data); data = NULL; goto fail; } if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len, sta->fils_snonce, fils_nonce, sta->fils_dh_ss ? wpabuf_head(sta->fils_dh_ss) : NULL, sta->fils_dh_ss ? wpabuf_len(sta->fils_dh_ss) : 0, sta->fils_g_sta, pub) < 0) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; wpabuf_free(data); data = NULL; goto fail; } fail: if (is_pub) *is_pub = pub != NULL; os_free(ie_buf); wpabuf_free(pub); wpabuf_clear_free(sta->fils_dh_ss); sta->fils_dh_ss = NULL; #ifdef CONFIG_FILS_SK_PFS crypto_ecdh_deinit(sta->fils_ecdh); sta->fils_ecdh = NULL; #endif /* CONFIG_FILS_SK_PFS */ return data; } static void handle_auth_fils_finish(struct hostapd_data *hapd, struct sta_info *sta, u16 resp, struct wpabuf *data, int pub) { u16 auth_alg; auth_alg = (pub || resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0, "auth-fils-finish"); wpabuf_free(data); if (resp == WLAN_STATUS_SUCCESS) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; mlme_authenticate_indication(hapd, sta); } } void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, struct sta_info *sta, int success, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len) { u16 resp; u32 flags = sta->flags; sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP | WLAN_STA_PENDING_PASN_FILS_ERP); resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; if (flags & WLAN_STA_PENDING_FILS_ERP) { struct wpabuf *data; int pub = 0; if (!sta->fils_pending_cb) return; data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, msk, msk_len, &pub); if (!data) { wpa_printf(MSG_DEBUG, "%s: prepare_auth_resp_fils() failure", __func__); } sta->fils_pending_cb(hapd, sta, resp, data, pub); #ifdef CONFIG_PASN } else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) { pasn_fils_auth_resp(hapd, sta, resp, erp_resp, msk, msk_len); #endif /* CONFIG_PASN */ } } #endif /* CONFIG_FILS */ static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, struct radius_sta *info) { int res; res = hostapd_allowed_address(hapd, addr, msg, len, info, 0); if (res == HOSTAPD_ACL_REJECT) { wpa_printf(MSG_DEBUG, "Station " MACSTR " not allowed to authenticate", MAC2STR(addr)); return HOSTAPD_ACL_REJECT; } if (res == HOSTAPD_ACL_PENDING) { wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR " waiting for an external authentication", MAC2STR(addr)); /* Authentication code will re-send the authentication frame * after it has received (and cached) information from the * external source. */ return HOSTAPD_ACL_PENDING; } return res; } static int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, int res, struct radius_sta *info) { u32 session_timeout = info->session_timeout; u32 acct_interim_interval = info->acct_interim_interval; struct vlan_description *vlan_id = &info->vlan_id; struct hostapd_sta_wpa_psk_short *psk = info->psk; char *identity = info->identity; char *radius_cui = info->radius_cui; if (vlan_id->notempty && !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Invalid VLAN %d%s received from RADIUS server", vlan_id->untagged, vlan_id->tagged[0] ? "+" : ""); return -1; } if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0) return -1; if (sta->vlan_id) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); hostapd_free_psk_list(sta->psk); if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) hostapd_copy_psk_list(&sta->psk, psk); else sta->psk = NULL; os_free(sta->identity); if (identity) sta->identity = os_strdup(identity); else sta->identity = NULL; os_free(sta->radius_cui); if (radius_cui) sta->radius_cui = os_strdup(radius_cui); else sta->radius_cui = NULL; if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) sta->acct_interim_interval = acct_interim_interval; if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) { sta->session_timeout_set = 1; os_get_reltime(&sta->session_timeout); sta->session_timeout.sec += session_timeout; ap_sta_session_timeout(hapd, sta, session_timeout); } else { sta->session_timeout_set = 0; ap_sta_no_session_timeout(hapd, sta); } return 0; } #ifdef CONFIG_PASN #ifdef CONFIG_SAE static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, struct wpabuf *wd) { struct pasn_data *pasn = sta->pasn; const char *password; const u8 *data; size_t buf_len; u16 res, alg, seq, status; int groups[] = { pasn->group, 0 }; struct sae_pt *pt = NULL; int ret; if (!wd) return -1; data = wpabuf_head_u8(wd); buf_len = wpabuf_len(wd); if (buf_len < 6) { wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu", buf_len); return -1; } alg = WPA_GET_LE16(data); seq = WPA_GET_LE16(data + 2); status = WPA_GET_LE16(data + 4); wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u", alg, seq, status); if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) { wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit"); return -1; } sae_clear_data(&pasn->sae); pasn->sae.state = SAE_NOTHING; ret = sae_set_group(&pasn->sae, pasn->group); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); return -1; } password = sae_get_password(hapd, sta, NULL, NULL, &pt, NULL); if (!password || !pt) { wpa_printf(MSG_DEBUG, "PASN: No SAE PT found"); return -1; } ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); return -1; } res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0, groups, 0); if (res != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); return -1; } /* Process the commit message and derive the PMK */ ret = sae_process_commit(&pasn->sae); if (ret) { wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); return -1; } pasn->sae.state = SAE_COMMITTED; return 0; } static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta, struct wpabuf *wd) { struct pasn_data *pasn = sta->pasn; const u8 *data; size_t buf_len; u16 res, alg, seq, status; if (!wd) return -1; data = wpabuf_head_u8(wd); buf_len = wpabuf_len(wd); if (buf_len < 6) { wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu", buf_len); return -1; } alg = WPA_GET_LE16(data); seq = WPA_GET_LE16(data + 2); status = WPA_GET_LE16(data + 4); wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u", alg, seq, status); if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm"); return -1; } res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6); if (res != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); return -1; } pasn->sae.state = SAE_ACCEPTED; /* * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with * PASN/SAE should only be allowed with future PASN only. For now do not * restrict this only for PASN. */ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, pasn->sae.pmk, pasn->sae.pmkid); return 0; } static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd, struct sta_info *sta) { struct pasn_data *pasn = sta->pasn; struct wpabuf *buf = NULL; u8 *len_ptr; size_t len; /* Need to add the entire Authentication frame body */ buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); if (!buf) { wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); return NULL; } /* Need to add the entire authentication frame body for the commit */ len_ptr = wpabuf_put(buf, 2); wpabuf_put_le16(buf, WLAN_AUTH_SAE); wpabuf_put_le16(buf, 1); wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); /* Write the actual commit and update the length accordingly */ sae_write_commit(&pasn->sae, buf, NULL, 0); len = wpabuf_len(buf); WPA_PUT_LE16(len_ptr, len - 2); /* Need to add the entire Authentication frame body for the confirm */ len_ptr = wpabuf_put(buf, 2); wpabuf_put_le16(buf, WLAN_AUTH_SAE); wpabuf_put_le16(buf, 2); wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); sae_write_confirm(&pasn->sae, buf); WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2); pasn->sae.state = SAE_CONFIRMED; return buf; } #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd, struct sta_info *sta) { struct pasn_data *pasn = sta->pasn; struct pasn_fils_data *fils = &pasn->fils; struct wpabuf *buf = NULL; if (!fils->erp_resp) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp"); return NULL; } buf = wpabuf_alloc(1500); if (!buf) return NULL; /* Add the authentication algorithm */ wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); /* Authentication Transaction seq# */ wpabuf_put_le16(buf, 2); /* Status Code */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); /* Own RSNE */ wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN); /* Wrapped Data */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp)); wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); wpabuf_put_buf(buf, fils->erp_resp); return buf; } static void pasn_fils_auth_resp(struct hostapd_data *hapd, struct sta_info *sta, u16 status, struct wpabuf *erp_resp, const u8 *msk, size_t msk_len) { struct pasn_data *pasn = sta->pasn; struct pasn_fils_data *fils = &pasn->fils; u8 pmk[PMK_LEN_MAX]; size_t pmk_len; int ret; wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u", status); if (status != WLAN_STATUS_SUCCESS) goto fail; if (!pasn->secret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret"); goto fail; } if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce", fils->anonce, FILS_NONCE_LEN); ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce, fils->anonce, NULL, 0, pmk, &pmk_len); if (ret) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); goto fail; } ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr, wpabuf_head(pasn->secret), wpabuf_len(pasn->secret), &sta->pasn->ptk, sta->pasn->akmp, sta->pasn->cipher, sta->pasn->kdk_len); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK"); goto fail; } wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); wpabuf_free(pasn->secret); pasn->secret = NULL; fils->erp_resp = erp_resp; ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS); fils->erp_resp = NULL; if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response"); goto fail; } fils->state = PASN_FILS_STATE_COMPLETE; return; fail: ap_free_sta(hapd, sta); } static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, struct wpabuf *wd) { #ifdef CONFIG_NO_RADIUS wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail"); return -1; #else /* CONFIG_NO_RADIUS */ struct pasn_data *pasn = sta->pasn; struct pasn_fils_data *fils = &pasn->fils; struct ieee802_11_elems elems; struct wpa_ie_data rsne_data; struct wpabuf *fils_wd; const u8 *data; size_t buf_len; u16 alg, seq, status; int ret; if (fils->state != PASN_FILS_STATE_NONE) { wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data"); return -1; } if (!wd) { wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data"); return -1; } data = wpabuf_head_u8(wd); buf_len = wpabuf_len(wd); if (buf_len < 6) { wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%lu", buf_len); return -1; } alg = WPA_GET_LE16(data); seq = WPA_GET_LE16(data + 2); status = WPA_GET_LE16(data + 4); wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u", alg, seq, status); if (alg != WLAN_AUTH_FILS_SK || seq != 1 || status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: FILS: Dropping peer authentication"); return -1; } data += 6; buf_len -= 6; if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements"); return -1; } if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || !elems.wrapped_data) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); return -1; } ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &rsne_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE"); return -1; } ret = wpa_pasn_validate_rsne(&rsne_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); return -1; } if (rsne_data.num_pmkid) { wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting PMKID in RSNE"); return -1; } wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce, FILS_NONCE_LEN); os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session, FILS_SESSION_LEN); os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN); fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION, WLAN_EID_EXT_WRAPPED_DATA); if (!fils_wd) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data"); return -1; } if (!sta->eapol_sm) sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); wpa_printf(MSG_DEBUG, "PASN: FILS: Forward EAP-Initiate/Re-auth to AS"); ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd), wpabuf_len(fils_wd)); sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP; fils->state = PASN_FILS_STATE_PENDING_AS; /* * Calculate pending PMKID here so that we do not need to maintain a * copy of the EAP-Initiate/Reautt message. */ fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd), fils->erp_pmkid); wpabuf_free(fils_wd); return 0; #endif /* CONFIG_NO_RADIUS */ } #endif /* CONFIG_FILS */ static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd, struct sta_info *sta) { switch (sta->pasn->akmp) { case WPA_KEY_MGMT_PASN: /* no wrapped data */ return NULL; case WPA_KEY_MGMT_SAE: #ifdef CONFIG_SAE return pasn_get_sae_wd(hapd, sta); #else /* CONFIG_SAE */ wpa_printf(MSG_ERROR, "PASN: SAE: Cannot derive wrapped data"); return NULL; #endif /* CONFIG_SAE */ case WPA_KEY_MGMT_FILS_SHA256: case WPA_KEY_MGMT_FILS_SHA384: #ifdef CONFIG_FILS return pasn_get_fils_wd(hapd, sta); #endif /* CONFIG_FILS */ /* fall through */ case WPA_KEY_MGMT_FT_PSK: case WPA_KEY_MGMT_FT_IEEE8021X: case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: default: wpa_printf(MSG_ERROR, "PASN: TODO: Wrapped data for akmp=0x%x", sta->pasn->akmp); return NULL; } } static int pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta, const u8 *cached_pmk, size_t cached_pmk_len, struct wpa_pasn_params_data *pasn_data, struct wpabuf *wrapped_data, struct wpabuf *secret) { static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; u8 pmk[PMK_LEN_MAX]; u8 pmk_len; int ret; os_memset(pmk, 0, sizeof(pmk)); pmk_len = 0; if (!cached_pmk || !cached_pmk_len) wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry"); if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) { wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); pmk_len = WPA_PASN_PMK_LEN; os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); } else if (cached_pmk && cached_pmk_len) { wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry"); pmk_len = cached_pmk_len; os_memcpy(pmk, cached_pmk, cached_pmk_len); } else { switch (sta->pasn->akmp) { #ifdef CONFIG_SAE case WPA_KEY_MGMT_SAE: if (sta->pasn->sae.state == SAE_COMMITTED) { pmk_len = PMK_LEN; os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN); break; } #endif /* CONFIG_SAE */ /* fall through */ default: /* TODO: Derive PMK based on wrapped data */ wpa_printf(MSG_DEBUG, "PASN: Missing PMK derivation"); return -1; } } ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr, wpabuf_head(secret), wpabuf_len(secret), &sta->pasn->ptk, sta->pasn->akmp, sta->pasn->cipher, sta->pasn->kdk_len); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); return -1; } wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); return 0; } static void handle_auth_pasn_comeback(struct hostapd_data *hapd, struct sta_info *sta, u16 group) { struct wpabuf *buf, *comeback; int ret; wpa_printf(MSG_DEBUG, "PASN: Building comeback frame 2. Comeback after=%u", hapd->conf->pasn_comeback_after); buf = wpabuf_alloc(1500); if (!buf) return; wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr, sta->addr, 2, WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY); /* * Do not include the group as a part of the token since it is not going * to be used. */ comeback = auth_build_token_req(hapd, 0, sta->addr, 0); if (!comeback) { wpa_printf(MSG_DEBUG, "PASN: Failed sending auth with comeback"); wpabuf_free(buf); return; } wpa_pasn_add_parameter_ie(buf, group, WPA_PASN_WRAPPED_DATA_NO, NULL, 0, comeback, hapd->conf->pasn_comeback_after); wpabuf_free(comeback); wpa_printf(MSG_DEBUG, "PASN: comeback: STA=" MACSTR, MAC2STR(sta->addr)); ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0, NULL, 0, 0); if (ret) wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2"); wpabuf_free(buf); } static int handle_auth_pasn_resp(struct hostapd_data *hapd, struct sta_info *sta, struct rsn_pmksa_cache_entry *pmksa, u16 status) { struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN]; u8 mic_len; u8 *ptr; const u8 *frame, *data, *rsn_ie, *rsnxe_ie; u8 *data_buf = NULL; size_t rsn_ie_len, frame_len, data_len; int ret; const u8 *pmkid = NULL; wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status); buf = wpabuf_alloc(1500); if (!buf) goto fail; wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr, sta->addr, 2, status); if (status != WLAN_STATUS_SUCCESS) goto done; if (pmksa) { pmkid = pmksa->pmkid; #ifdef CONFIG_SAE } else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID"); pmkid = sta->pasn->sae.pmkid; #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS } else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID"); pmkid = sta->pasn->fils.erp_pmkid; #endif /* CONFIG_FILS */ } if (wpa_pasn_add_rsne(buf, pmkid, sta->pasn->akmp, sta->pasn->cipher) < 0) goto fail; /* No need to derive PMK if PMKSA is given */ if (!pmksa) wrapped_data_buf = pasn_get_wrapped_data(hapd, sta); else sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO; /* Get public key */ pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0); pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(sta->pasn->ecdh)); if (!pubkey) { wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); goto fail; } wpa_pasn_add_parameter_ie(buf, sta->pasn->group, sta->pasn->wrapped_data_format, pubkey, true, NULL, 0); if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) goto fail; wpabuf_free(wrapped_data_buf); wrapped_data_buf = NULL; wpabuf_free(pubkey); pubkey = NULL; /* Add RSNXE if needed */ rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX); if (rsnxe_ie) wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); /* Add the mic */ mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher); wpabuf_put_u8(buf, WLAN_EID_MIC); wpabuf_put_u8(buf, mic_len); ptr = wpabuf_put(buf, mic_len); os_memset(ptr, 0, mic_len); frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN; rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len); if (!rsn_ie || !rsn_ie_len) goto fail; /* * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also * MDE, etc. Thus, do not use the returned length but instead use the * length specified in the IE header. */ data_len = rsn_ie[1] + 2; if (rsnxe_ie) { data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2); if (!data_buf) goto fail; os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2); os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2); data_len += rsnxe_ie[1] + 2; data = data_buf; } else { data = rsn_ie; } ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher, hapd->own_addr, sta->addr, data, data_len, frame, frame_len, mic); os_free(data_buf); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation"); goto fail; } #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->pasn_corrupt_mic) { wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC"); mic[0] = ~mic[0]; } #endif /* CONFIG_TESTING_OPTIONS */ os_memcpy(ptr, mic, mic_len); done: wpa_printf(MSG_DEBUG, "PASN: Building frame 2: success; resp STA=" MACSTR, MAC2STR(sta->addr)); ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0, NULL, 0, 0); if (ret) wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); wpabuf_free(buf); return ret; fail: wpabuf_free(wrapped_data_buf); wpabuf_free(pubkey); wpabuf_free(buf); return -1; } static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len) { struct ieee802_11_elems elems; struct wpa_ie_data rsn_data; struct wpa_pasn_params_data pasn_params; struct rsn_pmksa_cache_entry *pmksa = NULL; const u8 *cached_pmk = NULL; size_t cached_pmk_len = 0; #ifdef CONFIG_IEEE80211R_AP u8 pmk_r1[PMK_LEN_MAX]; size_t pmk_r1_len; #endif /* CONFIG_IEEE80211R_AP */ struct wpabuf *wrapped_data = NULL, *secret = NULL; const int *groups = hapd->conf->pasn_groups; static const int default_groups[] = { 19, 0 }; u16 status = WLAN_STATUS_SUCCESS; int ret, inc_y; bool derive_keys; u32 i; if (!groups) groups = default_groups; if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - offsetof(struct ieee80211_mgmt, u.auth.variable), &elems, 0) == ParseFailed) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing Authentication frame"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &rsn_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE"); status = WLAN_STATUS_INVALID_RSNIE; goto send_resp; } ret = wpa_pasn_validate_rsne(&rsn_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); status = WLAN_STATUS_INVALID_RSNIE; goto send_resp; } if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) || !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) { wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); status = WLAN_STATUS_INVALID_RSNIE; goto send_resp; } sta->pasn->akmp = rsn_data.key_mgmt; sta->pasn->cipher = rsn_data.pairwise_cipher; if (hapd->conf->force_kdk_derivation || ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) && ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, WLAN_RSNX_CAPAB_SECURE_LTF))) sta->pasn->kdk_len = WPA_KDK_MAX_LEN; else sta->pasn->kdk_len = 0; wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len); if (!elems.pasn_params || !elems.pasn_params_len) { wpa_printf(MSG_DEBUG, "PASN: No PASN Parameters element found"); status = WLAN_STATUS_INVALID_PARAMETERS; goto send_resp; } ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, elems.pasn_params_len + 3, false, &pasn_params); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validation of PASN Parameters IE"); status = WLAN_STATUS_INVALID_PARAMETERS; goto send_resp; } for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) ; if (!pasn_params.group || groups[i] != pasn_params.group) { wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed", pasn_params.group); status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; goto send_resp; } if (!pasn_params.pubkey || !pasn_params.pubkey_len) { wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } if (pasn_params.comeback) { wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token"); ret = check_comeback_token(hapd, sta->addr, pasn_params.comeback, pasn_params.comeback_len); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } } else if (use_anti_clogging(hapd)) { wpa_printf(MSG_DEBUG, "PASN: Respond with comeback"); handle_auth_pasn_comeback(hapd, sta, pasn_params.group); ap_free_sta(hapd, sta); return; } sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group); if (!sta->pasn->ecdh) { wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } sta->pasn->group = pasn_params.group; if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) { inc_y = 1; } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 || pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) { inc_y = 0; } else { wpa_printf(MSG_DEBUG, "PASN: Invalid first octet in pubkey=0x%x", pasn_params.pubkey[0]); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y, pasn_params.pubkey + 1, pasn_params.pubkey_len - 1); if (!secret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } derive_keys = true; if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION, WLAN_EID_EXT_WRAPPED_DATA); if (!wrapped_data) { wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } #ifdef CONFIG_SAE if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { ret = pasn_wd_handle_sae_commit(hapd, sta, wrapped_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed processing SAE commit"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } } #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { ret = pasn_wd_handle_fils(hapd, sta, wrapped_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed processing FILS wrapped data"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } wpa_printf(MSG_DEBUG, "PASN: FILS: Pending AS response"); /* * With PASN/FILS, keys can be derived only after a * response from the AS is processed. */ derive_keys = false; } #endif /* CONFIG_FILS */ } sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format; ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher, ((const u8 *) mgmt) + IEEE80211_HDRLEN, len - IEEE80211_HDRLEN, sta->pasn->hash); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } if (!derive_keys) { wpa_printf(MSG_DEBUG, "PASN: Storing secret"); sta->pasn->secret = secret; wpabuf_free(wrapped_data); return; } if (rsn_data.num_pmkid) { if (wpa_key_mgmt_ft(sta->pasn->akmp)) { #ifdef CONFIG_IEEE80211R_AP wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1"); ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr, rsn_data.pmkid, pmk_r1, &pmk_r1_len, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FT: Failed getting PMK-R1"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } cached_pmk = pmk_r1; cached_pmk_len = pmk_r1_len; #else /* CONFIG_IEEE80211R_AP */ wpa_printf(MSG_DEBUG, "PASN: FT: Not supported"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; #endif /* CONFIG_IEEE80211R_AP */ } else { wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry"); pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, rsn_data.pmkid); if (pmksa) { cached_pmk = pmksa->pmk; cached_pmk_len = pmksa->pmk_len; } } } else { wpa_printf(MSG_DEBUG, "PASN: No PMKID specified"); } ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len, &pasn_params, wrapped_data, secret); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher, ((const u8 *) mgmt) + IEEE80211_HDRLEN, len - IEEE80211_HDRLEN, sta->pasn->hash); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; } send_resp: ret = handle_auth_pasn_resp(hapd, sta, pmksa, status); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to send response"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; } else { wpa_printf(MSG_DEBUG, "PASN: Success handling transaction == 1"); } wpabuf_free(secret); wpabuf_free(wrapped_data); if (status != WLAN_STATUS_SUCCESS) ap_free_sta(hapd, sta); } static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len) { struct ieee802_11_elems elems; struct wpa_pasn_params_data pasn_params; struct wpabuf *wrapped_data = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; u8 mic_len; int ret; if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - offsetof(struct ieee80211_mgmt, u.auth.variable), &elems, 0) == ParseFailed) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing Authentication frame"); goto fail; } /* Check that the MIC IE exists. Save it and zero out the memory. */ mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher); if (!elems.mic || elems.mic_len != mic_len) { wpa_printf(MSG_DEBUG, "PASN: Invalid MIC. Expecting len=%u", mic_len); goto fail; } else { os_memcpy(mic, elems.mic, mic_len); /* TODO: Clean this up.. Should not modify received frame * buffer. */ os_memset((u8 *) elems.mic, 0, mic_len); } if (!elems.pasn_params || !elems.pasn_params_len) { wpa_printf(MSG_DEBUG, "PASN: No PASN Parameters element found"); goto fail; } ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, elems.pasn_params_len + 3, false, &pasn_params); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validation of PASN Parameters IE"); goto fail; } if (pasn_params.pubkey || pasn_params.pubkey_len) { wpa_printf(MSG_DEBUG, "PASN: Public key should not be included"); goto fail; } /* Verify the MIC */ ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher, sta->addr, hapd->own_addr, sta->pasn->hash, mic_len * 2, (u8 *) &mgmt->u.auth, len - offsetof(struct ieee80211_mgmt, u.auth), out_mic); wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); goto fail; } if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION, WLAN_EID_EXT_WRAPPED_DATA); if (!wrapped_data) { wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); goto fail; } #ifdef CONFIG_SAE if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) { ret = pasn_wd_handle_sae_confirm(hapd, sta, wrapped_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed processing SAE confirm"); wpabuf_free(wrapped_data); goto fail; } } #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { if (wrapped_data) { wpa_printf(MSG_DEBUG, "PASN: FILS: Ignore wrapped data"); } } #endif /* CONFIG_FILS */ wpabuf_free(wrapped_data); } wpa_printf(MSG_INFO, "PASN: Success handling transaction == 3. Store PTK"); ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200, &sta->pasn->ptk); fail: ap_free_sta(hapd, sta); } static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 trans_seq, u16 status) { if (hapd->conf->wpa != WPA_PROTO_RSN) { wpa_printf(MSG_INFO, "PASN: RSN is not configured"); return; } wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR, MAC2STR(sta->addr)); if (trans_seq == 1) { if (sta->pasn) { wpa_printf(MSG_DEBUG, "PASN: Not expecting transaction == 1"); return; } if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: Failure status in transaction == 1"); return; } sta->pasn = os_zalloc(sizeof(*sta->pasn)); if (!sta->pasn) { wpa_printf(MSG_DEBUG, "PASN: Failed to allocate PASN context"); return; } handle_auth_pasn_1(hapd, sta, mgmt, len); } else if (trans_seq == 3) { if (!sta->pasn) { wpa_printf(MSG_DEBUG, "PASN: Not expecting transaction == 3"); return; } if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: Failure status in transaction == 3"); ap_free_sta_pasn(hapd, sta); return; } handle_auth_pasn_3(hapd, sta, mgmt, len); } else { wpa_printf(MSG_DEBUG, "PASN: Invalid transaction %u - ignore", trans_seq); } } #endif /* CONFIG_PASN */ static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int rssi, int from_queue) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; int res, reply_res; u16 fc; const u8 *challenge = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; u16 seq_ctrl; struct radius_sta rad_info; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); return; } #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_auth_probability > 0.0 && drand48() < hapd->iconf->ignore_auth_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring auth frame from " MACSTR, MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_TESTING_OPTIONS */ auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); fc = le_to_host16(mgmt->frame_control); seq_ctrl = le_to_host16(mgmt->seq_ctrl); if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + WLAN_AUTH_CHALLENGE_LEN && mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) challenge = &mgmt->u.auth.variable[2]; wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " "auth_transaction=%d status_code=%d wep=%d%s " "seq_ctrl=0x%x%s%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), challenge ? " challenge" : "", seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "", from_queue ? " (from queue)" : ""); #ifdef CONFIG_NO_RC4 if (auth_alg == WLAN_AUTH_SHARED_KEY) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } #endif /* CONFIG_NO_RC4 */ if (hapd->tkip_countermeasures) { wpa_printf(MSG_DEBUG, "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || #ifdef CONFIG_IEEE80211R_AP (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FILS_SK) || (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && hapd->conf->fils_dh_group && auth_alg == WLAN_AUTH_FILS_SK_PFS) || #endif /* CONFIG_FILS */ #ifdef CONFIG_PASN (hapd->conf->wpa && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) && auth_alg == WLAN_AUTH_PASN) || #endif /* CONFIG_PASN */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || #ifdef CONFIG_PASN (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) || #endif /* CONFIG_PASN */ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; goto fail; } if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (hapd->conf->no_auth_if_seen_on) { struct hostapd_data *other; other = sta_track_seen_on(hapd->iface, mgmt->sa, hapd->conf->no_auth_if_seen_on); if (other) { u8 *pos; u32 info; u8 op_class, channel, phytype; wpa_printf(MSG_DEBUG, "%s: Reject authentication from " MACSTR " since STA has been seen on %s", hapd->conf->iface, MAC2STR(mgmt->sa), hapd->conf->no_auth_if_seen_on); resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION; pos = &resp_ies[0]; *pos++ = WLAN_EID_NEIGHBOR_REPORT; *pos++ = 13; os_memcpy(pos, other->own_addr, ETH_ALEN); pos += ETH_ALEN; info = 0; /* TODO: BSSID Information */ WPA_PUT_LE32(pos, info); pos += 4; if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) phytype = 8; /* dmg */ else if (other->iconf->ieee80211ac) phytype = 9; /* vht */ else if (other->iconf->ieee80211n) phytype = 7; /* ht */ else if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A) phytype = 4; /* ofdm */ else if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211G) phytype = 6; /* erp */ else phytype = 5; /* hrdsss */ if (ieee80211_freq_to_channel_ext( hostapd_hw_get_freq(other, other->iconf->channel), other->iconf->secondary_channel, other->iconf->ieee80211ac, &op_class, &channel) == NUM_HOSTAPD_MODES) { op_class = 0; channel = other->iconf->channel; } *pos++ = op_class; *pos++ = channel; *pos++ = phytype; resp_ies_len = pos - &resp_ies[0]; goto fail; } } res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, &rad_info); if (res == HOSTAPD_ACL_REJECT) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Ignore Authentication frame from " MACSTR " due to ACL reject", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (res == HOSTAPD_ACL_PENDING) return; #ifdef CONFIG_SAE if (auth_alg == WLAN_AUTH_SAE && !from_queue && (auth_transaction == 1 || (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) { /* Handle SAE Authentication commit message through a queue to * provide more control for postponing the needed heavy * processing under a possible DoS attack scenario. In addition, * queue SAE Authentication confirm message if there happens to * be a queued commit message from the same peer. This is needed * to avoid reordering Authentication frames within the same * SAE exchange. */ auth_sae_queue(hapd, mgmt, len, rssi); return; } #endif /* CONFIG_SAE */ sta = ap_get_sta(hapd, mgmt->sa); if (sta) { sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; sta->ft_over_ds = 0; if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && sta->last_subtype == WLAN_FC_STYPE_AUTH) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated authentication frame seq_ctrl=0x%x", seq_ctrl); return; } #ifdef CONFIG_MESH if ((hapd->conf->mesh & MESH_ENABLED) && sta->plink_state == PLINK_BLOCKED) { wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR " is blocked - drop Authentication frame", MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_MESH */ #ifdef CONFIG_PASN if (auth_alg == WLAN_AUTH_PASN && (sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "PASN: auth: Existing station: " MACSTR, MAC2STR(sta->addr)); return; } #endif /* CONFIG_PASN */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { /* if the mesh peer is not available, we don't do auth. */ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR " not yet known - drop Authentication frame", MAC2STR(mgmt->sa)); /* * Save a copy of the frame so that it can be processed * if a new peer entry is added shortly after this. */ wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len); os_get_reltime(&hapd->mesh_pending_auth_time); return; } #endif /* CONFIG_MESH */ sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { wpa_printf(MSG_DEBUG, "ap_sta_add() failed"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; #ifdef CONFIG_MBO sta->auth_rssi = rssi; #endif /* CONFIG_MBO */ res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info); if (res) { wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); /* * If the driver supports full AP client state, add a station to the * driver before sending authentication reply to make sure the driver * has resources, and not to go through the entire authentication and * association handshake, and fail it at the end. * * If this is not the first transaction, in a multi-step authentication * algorithm, the station already exists in the driver * (sta->added_unassoc = 1) so skip it. * * In mesh mode, the station was already added to the driver when the * NEW_PEER_CANDIDATE event is received. * * If PMF was negotiated for the existing association, skip this to * avoid dropping the STA entry and the associated keys. This is needed * to allow the original connection work until the attempt can complete * (re)association, so that unprotected Authentication frame cannot be * used to bypass PMF protection. * * PASN authentication does not require adding/removing station to the * driver so skip this flow in case of PASN authentication. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) { if (ap_sta_re_add(hapd, sta) < 0) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } } switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (open system)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; #ifdef CONFIG_WEP #ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); if (resp != 0) wpa_printf(MSG_DEBUG, "auth_shared_key() failed: status=%d", resp); sta->auth_alg = WLAN_AUTH_SHARED_KEY; mlme_authenticate_indication(hapd, sta); if (sta->challenge && auth_transaction == 1) { resp_ies[0] = WLAN_EID_CHALLENGE; resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; os_memcpy(resp_ies + 2, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; } break; #endif /* CONFIG_NO_RC4 */ #endif /* CONFIG_WEP */ #ifdef CONFIG_IEEE80211R_AP case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, auth_transaction, mgmt->u.auth.variable, len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), handle_auth_ft_finish, hapd); /* handle_auth_ft_finish() callback will complete auth. */ return; #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: #ifdef CONFIG_MESH if (status_code == WLAN_STATUS_SUCCESS && hapd->conf->mesh & MESH_ENABLED) { if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "SAE: Failed to initialize WPA state machine"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } } #endif /* CONFIG_MESH */ handle_auth_sae(hapd, sta, mgmt, len, auth_transaction, status_code); return; #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS case WLAN_AUTH_FILS_SK: case WLAN_AUTH_FILS_SK_PFS: handle_auth_fils(hapd, sta, mgmt->u.auth.variable, len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), auth_alg, auth_transaction, status_code, handle_auth_fils_finish); return; #endif /* CONFIG_FILS */ #ifdef CONFIG_PASN case WLAN_AUTH_PASN: handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction, status_code); return; #endif /* CONFIG_PASN */ } fail: reply_res = send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, auth_alg, - auth_transaction + 1, resp, resp_ies, - resp_ies_len, "handle-auth"); + auth_alg == WLAN_AUTH_SAE ? + auth_transaction : auth_transaction + 1, + resp, resp_ies, resp_ies_len, + "handle-auth"); if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || reply_res != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } } int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; /* get a unique AID */ if (sta->aid > 0) { wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); return 0; } if (TEST_FAIL()) return -1; for (i = 0; i < AID_WORDS; i++) { if (hapd->sta_aid[i] == (u32) -1) continue; for (j = 0; j < 32; j++) { if (!(hapd->sta_aid[i] & BIT(j))) break; } if (j < 32) break; } if (j == 32) return -1; aid = i * 32 + j + 1; if (aid > 2007) return -1; sta->aid = aid; hapd->sta_aid[i] |= BIT(j); wpa_printf(MSG_DEBUG, " new AID %d", sta->aid); return 0; } static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ssid_ie, size_t ssid_ie_len) { if (ssid_ie == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; if (ssid_ie_len != hapd->conf->ssid.ssid_len || os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to associate with unknown SSID " "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *wmm_ie, size_t wmm_ie_len) { sta->flags &= ~WLAN_STA_WMM; sta->qosinfo = 0; if (wmm_ie && hapd->conf->wmm_enabled) { struct wmm_information_element *wmm; if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "invalid WMM element in association " "request"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->flags |= WLAN_STA_WMM; wmm = (struct wmm_information_element *) wmm_ie; sta->qosinfo = wmm->qos_info; } return WLAN_STATUS_SUCCESS; } static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta, const u8 *multi_ap_ie, size_t multi_ap_len) { u8 multi_ap_value = 0; sta->flags &= ~WLAN_STA_MULTI_AP; if (!hapd->conf->multi_ap) return WLAN_STATUS_SUCCESS; if (multi_ap_ie) { const u8 *multi_ap_subelem; multi_ap_subelem = get_ie(multi_ap_ie + 4, multi_ap_len - 4, MULTI_AP_SUB_ELEM_TYPE); if (multi_ap_subelem && multi_ap_subelem[1] == 1) { multi_ap_value = multi_ap_subelem[2]; } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Multi-AP IE has missing or invalid Multi-AP subelement"); return WLAN_STATUS_INVALID_IE; } } if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Multi-AP IE with unexpected value 0x%02x", multi_ap_value); if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) { if (hapd->conf->multi_ap & FRONTHAUL_BSS) return WLAN_STATUS_SUCCESS; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Non-Multi-AP STA tries to associate with backhaul-only BSS"); return WLAN_STATUS_ASSOC_DENIED_UNSPEC; } if (!(hapd->conf->multi_ap & BACKHAUL_BSS)) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Backhaul STA tries to associate with fronthaul-only BSS"); sta->flags |= WLAN_STA_MULTI_AP; return WLAN_STATUS_SUCCESS; } static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) { /* Supported rates not used in IEEE 802.11ad/DMG */ if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) return WLAN_STATUS_SUCCESS; if (!elems->supp_rates) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "No supported rates element in AssocReq"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (elems->supp_rates_len + elems->ext_supp_rates_len > sizeof(sta->supported_rates)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Invalid supported rates element length %d+%d", elems->supp_rates_len, elems->ext_supp_rates_len); return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->supported_rates_len = merge_byte_arrays( sta->supported_rates, sizeof(sta->supported_rates), elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); return WLAN_STATUS_SUCCESS; } static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ext_capab_ie, size_t ext_capab_ie_len) { #ifdef CONFIG_INTERWORKING /* check for QoS Map support */ if (ext_capab_ie_len >= 5) { if (ext_capab_ie[4] & 0x01) sta->qos_map_enabled = 1; } #endif /* CONFIG_INTERWORKING */ if (ext_capab_ie_len > 0) { sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); os_free(sta->ext_capability); sta->ext_capability = os_malloc(1 + ext_capab_ie_len); if (sta->ext_capability) { sta->ext_capability[0] = ext_capab_ie_len; os_memcpy(sta->ext_capability + 1, ext_capab_ie, ext_capab_ie_len); } } return WLAN_STATUS_SUCCESS; } #ifdef CONFIG_OWE static int owe_group_supported(struct hostapd_data *hapd, u16 group) { int i; int *groups = hapd->conf->owe_groups; if (group != 19 && group != 20 && group != 21) return 0; if (!groups) return 1; for (i = 0; groups[i] > 0; i++) { if (groups[i] == group) return 1; } return 0; } static u16 owe_process_assoc_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh, u8 owe_dh_len) { struct wpabuf *secret, *pub, *hkey; int res; u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; const char *info = "OWE Key Generation"; const u8 *addr[2]; size_t len[2]; u16 group; size_t hash_len, prime_len; if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); return WLAN_STATUS_SUCCESS; } group = WPA_GET_LE16(owe_dh); if (!owe_group_supported(hapd, group)) { wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group); return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; } if (group == 19) prime_len = 32; else if (group == 20) prime_len = 48; else if (group == 21) prime_len = 66; else return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; crypto_ecdh_deinit(sta->owe_ecdh); sta->owe_ecdh = crypto_ecdh_init(group); if (!sta->owe_ecdh) return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; sta->owe_group = group; secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2, owe_dh_len - 2); secret = wpabuf_zeropad(secret, prime_len); if (!secret) { wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); /* prk = HKDF-extract(C | A | group, z) */ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); if (!pub) { wpabuf_clear_free(secret); return WLAN_STATUS_UNSPECIFIED_FAILURE; } /* PMKID = Truncate-128(Hash(C | A)) */ addr[0] = owe_dh + 2; len[0] = owe_dh_len - 2; addr[1] = wpabuf_head(pub); len[1] = wpabuf_len(pub); if (group == 19) { res = sha256_vector(2, addr, len, pmkid); hash_len = SHA256_MAC_LEN; } else if (group == 20) { res = sha384_vector(2, addr, len, pmkid); hash_len = SHA384_MAC_LEN; } else if (group == 21) { res = sha512_vector(2, addr, len, pmkid); hash_len = SHA512_MAC_LEN; } else { wpabuf_free(pub); wpabuf_clear_free(secret); return WLAN_STATUS_UNSPECIFIED_FAILURE; } pub = wpabuf_zeropad(pub, prime_len); if (res < 0 || !pub) { wpabuf_free(pub); wpabuf_clear_free(secret); return WLAN_STATUS_UNSPECIFIED_FAILURE; } hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2); if (!hkey) { wpabuf_free(pub); wpabuf_clear_free(secret); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */ wpabuf_put_buf(hkey, pub); /* A */ wpabuf_free(pub); wpabuf_put_le16(hkey, group); /* group */ if (group == 19) res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), wpabuf_head(secret), wpabuf_len(secret), prk); else if (group == 20) res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), wpabuf_head(secret), wpabuf_len(secret), prk); else if (group == 21) res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), wpabuf_head(secret), wpabuf_len(secret), prk); wpabuf_clear_free(hkey); wpabuf_clear_free(secret); if (res < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ os_free(sta->owe_pmk); sta->owe_pmk = os_malloc(hash_len); if (!sta->owe_pmk) { os_memset(prk, 0, SHA512_MAC_LEN); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (group == 19) res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sta->owe_pmk, hash_len); else if (group == 20) res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sta->owe_pmk, hash_len); else if (group == 21) res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sta->owe_pmk, hash_len); os_memset(prk, 0, SHA512_MAC_LEN); if (res < 0) { os_free(sta->owe_pmk); sta->owe_pmk = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } sta->owe_pmk_len = hash_len; wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE); return WLAN_STATUS_SUCCESS; } u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, const u8 *rsn_ie, size_t rsn_ie_len, const u8 *owe_dh, size_t owe_dh_len) { struct wpa_ie_data data; int res; if (!rsn_ie || rsn_ie_len < 2) { wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR, MAC2STR(peer)); return WLAN_STATUS_INVALID_IE; } rsn_ie -= 2; rsn_ie_len += 2; res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data); if (res) { wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR " (res=%d)", MAC2STR(peer), res); wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len); return wpa_res_to_status_code(res); } if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) { wpa_printf(MSG_DEBUG, "OWE: Unexpected key mgmt 0x%x from " MACSTR, (unsigned int) data.key_mgmt, MAC2STR(peer)); return WLAN_STATUS_AKMP_NOT_VALID; } if (!owe_dh) { wpa_printf(MSG_DEBUG, "OWE: No Diffie-Hellman Parameter element from " MACSTR, MAC2STR(peer)); return WLAN_STATUS_AKMP_NOT_VALID; } return WLAN_STATUS_SUCCESS; } u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta, const u8 *rsn_ie, size_t rsn_ie_len, const u8 *owe_dh, size_t owe_dh_len) { u16 status; u8 *owe_buf, ie[256 * 2]; size_t ie_len = 0; enum wpa_validate_result res; if (!rsn_ie || rsn_ie_len < 2) { wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq"); status = WLAN_STATUS_INVALID_IE; goto end; } if (!sta->wpa_sm) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (!sta->wpa_sm) { wpa_printf(MSG_WARNING, "OWE: Failed to initialize WPA state machine"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto end; } rsn_ie -= 2; rsn_ie_len += 2; res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, rsn_ie, rsn_ie_len, NULL, 0, NULL, 0, owe_dh, owe_dh_len); status = wpa_res_to_status_code(res); if (status != WLAN_STATUS_SUCCESS) goto end; status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); if (status != WLAN_STATUS_SUCCESS) goto end; owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie), NULL, 0); if (!owe_buf) { status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto end; } if (sta->owe_ecdh) { struct wpabuf *pub; pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); if (!pub) { status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto end; } /* OWE Diffie-Hellman Parameter element */ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ WPA_PUT_LE16(owe_buf, sta->owe_group); owe_buf += 2; os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); owe_buf += wpabuf_len(pub); wpabuf_free(pub); sta->external_dh_updated = 1; } ie_len = owe_buf - ie; end: wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer " MACSTR, status, (unsigned int) ie_len, MAC2STR(sta->addr)); hostapd_drv_update_dh_ie(hapd, sta->addr, status, status == WLAN_STATUS_SUCCESS ? ie : NULL, ie_len); return status; } #endif /* CONFIG_OWE */ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta, int reassoc) { if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) != (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) return false; if (!sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); if (!sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA Query timeout * has not been reached. Reject the association attempt * temporarily and start SA Query, if one is not pending. */ if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); return true; } return false; } static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { struct ieee802_11_elems elems; int resp; const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station sent an invalid " "association request"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && !(sta->flags & WLAN_STA_HT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory HT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_HT; } #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac) { resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); if (resp != WLAN_STATUS_SUCCESS) return resp; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory VHT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_VHT; } if (hapd->conf->vendor_vht && !elems.vht_capabilities) { resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht, elems.vendor_vht_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP, elems.he_capabilities, elems.he_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; if (is_6ghz_op_class(hapd->iconf->op_class)) { if (!(sta->flags & WLAN_STA_HE)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support mandatory HE PHY - reject association"); return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; } resp = copy_sta_he_6ghz_capab(hapd, sta, elems.he_6ghz_band_cap); if (resp != WLAN_STATUS_SUCCESS) return resp; } } #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (sta->p2p_ie) p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); } else { wpabuf_free(sta->p2p_ie); sta->p2p_ie = NULL; } #endif /* CONFIG_P2P */ if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && elems.wpa_ie) { wpa_ie = elems.wpa_ie; wpa_ie_len = elems.wpa_ie_len; } else { wpa_ie = NULL; wpa_ie_len = 0; } #ifdef CONFIG_WPS sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); if (hapd->conf->wps_state && elems.wps_ie) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); if (check_sa_query(hapd, sta, reassoc)) return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; sta->flags |= WLAN_STA_WPS; wpabuf_free(sta->wps_ie); sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_IE_VENDOR_TYPE); if (sta->wps_ie && wps_is_20(sta->wps_ie)) { wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpa_ie = NULL; wpa_ie_len = 0; if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " "(Re)Association Request - reject"); return WLAN_STATUS_INVALID_IE; } } else if (hapd->conf->wps_state && wpa_ie == NULL) { wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " "(Re)Association Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; } else #endif /* CONFIG_WPS */ if (hapd->conf->wpa && wpa_ie == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No WPA/RSN IE in association request"); return WLAN_STATUS_INVALID_IE; } if (hapd->conf->wpa && wpa_ie) { enum wpa_validate_result res; wpa_ie -= 2; wpa_ie_len += 2; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, wpa_ie, wpa_ie_len, elems.rsnxe ? elems.rsnxe - 2 : NULL, elems.rsnxe ? elems.rsnxe_len + 2 : 0, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) return resp; if (check_sa_query(hapd, sta, reassoc)) return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; if (wpa_auth_uses_mfp(sta->wpa_sm)) sta->flags |= WLAN_STA_MFP; else sta->flags &= ~WLAN_STA_MFP; #ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { if (!reassoc) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " "to use association (not " "re-association) with FT auth_alg", MAC2STR(sta->addr)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies, ies_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae && sta->sae->state == SAE_ACCEPTED) wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid); if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg == WLAN_AUTH_OPEN) { struct rsn_pmksa_cache_entry *sa; sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) { wpa_printf(MSG_DEBUG, "SAE: No PMKSA cache entry found for " MACSTR, MAC2STR(sta->addr)); return WLAN_STATUS_INVALID_PMKID; } wpa_printf(MSG_DEBUG, "SAE: " MACSTR " using PMKSA caching", MAC2STR(sta->addr)); } else if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg != WLAN_AUTH_SAE && !(sta->auth_alg == WLAN_AUTH_FT && wpa_auth_uses_ft_sae(sta->wpa_sm))) { wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " "SAE AKM after non-SAE auth_alg %u", MAC2STR(sta->addr), sta->auth_alg); return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; } if (hapd->conf->sae_pwe == 2 && sta->auth_alg == WLAN_AUTH_SAE && sta->sae && !sta->sae->h2e && ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, WLAN_RSNX_CAPAB_SAE_H2E)) { wpa_printf(MSG_INFO, "SAE: " MACSTR " indicates support for SAE H2E, but did not use it", MAC2STR(sta->addr)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } #endif /* CONFIG_SAE */ #ifdef CONFIG_OWE if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && elems.owe_dh) { resp = owe_process_assoc_req(hapd, sta, elems.owe_dh, elems.owe_dh_len); if (resp != WLAN_STATUS_SUCCESS) return resp; } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 dpp_pfs_free(sta->dpp_pfs); sta->dpp_pfs = NULL; if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && hapd->conf->dpp_netaccesskey && sta->wpa_sm && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && elems.owe_dh) { sta->dpp_pfs = dpp_pfs_init( wpabuf_head(hapd->conf->dpp_netaccesskey), wpabuf_len(hapd->conf->dpp_netaccesskey)); if (!sta->dpp_pfs) { wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); /* Try to continue without PFS */ goto pfs_fail; } if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, elems.owe_dh_len) < 0) { dpp_pfs_free(sta->dpp_pfs); sta->dpp_pfs = NULL; return WLAN_STATUS_UNSPECIFIED_FAILURE; } } wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? sta->dpp_pfs->secret : NULL); pfs_fail: #endif /* CONFIG_DPP2 */ if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to use TKIP with HT " "association"); return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } #ifdef CONFIG_HS20 } else if (hapd->conf->osen) { if (elems.osen == NULL) { hostapd_logger( hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No HS 2.0 OSEN element in association request"); return WLAN_STATUS_INVALID_IE; } wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association"); if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, elems.osen - 2, elems.osen_len + 2) < 0) return WLAN_STATUS_INVALID_IE; #endif /* CONFIG_HS20 */ } else wpa_auth_sta_no_wpa(sta->wpa_sm); #ifdef CONFIG_P2P p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { int release; sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); release = ((elems.hs20[4] >> 4) & 0x0f) + 1; if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) && hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_DEBUG, "HS 2.0: PMF not negotiated by release %d station " MACSTR, release, MAC2STR(sta->addr)); return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; } } else { sta->hs20_ie = NULL; } wpabuf_free(sta->roaming_consortium); if (elems.roaming_cons_sel) sta->roaming_consortium = wpabuf_alloc_copy( elems.roaming_cons_sel + 4, elems.roaming_cons_sel_len - 4); else sta->roaming_consortium = NULL; #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST wpabuf_free(sta->mb_ies); if (hapd->iface->fst) sta->mb_ies = mb_ies_by_info(&elems.mb_ies); else sta->mb_ies = NULL; #endif /* CONFIG_FST */ #ifdef CONFIG_MBO mbo_ap_check_sta_assoc(hapd, sta, &elems); if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_INFO, "MBO: Reject WPA2 association without PMF"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } #endif /* CONFIG_MBO */ #if defined(CONFIG_FILS) && defined(CONFIG_OCV) if (wpa_auth_uses_ocv(sta->wpa_sm) && (sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK)) { struct wpa_channel_info ci; int tx_chanwidth; int tx_seg1_idx; enum oci_verify_result res; if (hostapd_drv_channel_info(hapd, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (get_sta_tx_parameters(sta->wpa_sm, channel_width_to_int(ci.chanwidth), ci.seg1_idx, &tx_chanwidth, &tx_seg1_idx) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, tx_chanwidth, tx_seg1_idx); if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 && res == OCI_NOT_FOUND) { /* Work around misbehaving STAs */ wpa_printf(MSG_INFO, "FILS: Disable OCV with a STA that does not send OCI"); wpa_auth_set_ocv(sta->wpa_sm, 0); } else if (res != OCI_SUCCESS) { wpa_printf(MSG_WARNING, "FILS: OCV failed: %s", ocv_errorstr); wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" MACSTR " frame=fils-reassoc-req error=%s", MAC2STR(sta->addr), ocv_errorstr); return WLAN_STATUS_UNSPECIFIED_FAILURE; } } #endif /* CONFIG_FILS && CONFIG_OCV */ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, elems.supp_op_classes_len); if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && elems.rrm_enabled && elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, sizeof(sta->rrm_enabled_capa)); if (elems.power_capab) { sta->min_tx_power = elems.power_capab[0]; sta->max_tx_power = elems.power_capab[1]; sta->power_capab = 1; } else { sta->power_capab = 0; } return WLAN_STATUS_SUCCESS; } static void send_deauth(struct hostapd_data *hapd, const u8 *addr, u16 reason_code) { int send_len; struct ieee80211_mgmt reply; os_memset(&reply, 0, sizeof(reply)); reply.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); os_memcpy(reply.da, addr, ETH_ALEN); os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN); send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } static int add_associated_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc) { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; struct ieee80211_he_capabilities he_cap; int set = 1; /* * Remove the STA entry to ensure the STA PS state gets cleared and * configuration gets updated. This is relevant for cases, such as * FT-over-the-DS, where a station re-associates back to the same AP but * skips the authentication flow, or if working with a driver that * does not support full AP client state. * * Skip this if the STA has already completed FT reassociation and the * TK has been configured since the TX/RX PN must not be reset to 0 for * the same key. * * FT-over-the-DS has a special case where the STA entry (and as such, * the TK) has not yet been configured to the driver depending on which * driver interface is used. For that case, allow add-STA operation to * be used (instead of set-STA). This is needed to allow mac80211-based * drivers to accept the STA parameter configuration. Since this is * after a new FT-over-DS exchange, a new TK has been derived, so key * reinstallation is not a concern for this case. */ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)", MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg, sta->ft_over_ds, reassoc, !!(sta->flags & WLAN_STA_AUTHORIZED), wpa_auth_sta_ft_tk_already_set(sta->wpa_sm), wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)); if (!sta->added_unassoc && (!(sta->flags & WLAN_STA_AUTHORIZED) || (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) || (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) { hostapd_drv_sta_remove(hapd, sta->addr); wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); set = 0; /* Do not allow the FT-over-DS exception to be used more than * once per authentication exchange to guarantee a new TK is * used here */ sta->ft_over_ds = 0; } if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); #ifdef CONFIG_IEEE80211AC if (sta->flags & WLAN_STA_VHT) hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX if (sta->flags & WLAN_STA_HE) { hostapd_get_he_capab(hapd, sta->he_capab, &he_cap, sta->he_capab_len); } #endif /* CONFIG_IEEE80211AX */ /* * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags * will be set when the ACK frame for the (Re)Association Response frame * is processed (TX status driver event). */ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, sta->supported_rates_len, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, sta->flags & WLAN_STA_HE ? &he_cap : NULL, sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0, sta->he_6ghz_capab, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, set)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not %s STA to kernel driver", set ? "set" : "add"); if (sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } return -1; } sta->added_unassoc = 0; return 0; } static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 status_code, int reassoc, const u8 *ies, size_t ies_len, int rssi, int omit_rsnxe) { int send_len; u8 *buf; size_t buflen; struct ieee80211_mgmt *reply; u8 *p; u16 res = WLAN_STATUS_SUCCESS; buflen = sizeof(struct ieee80211_mgmt) + 1024; #ifdef CONFIG_FILS if (sta && sta->fils_hlp_resp) buflen += wpabuf_len(sta->fils_hlp_resp); if (sta) buflen += 150; #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) buflen += 150; #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 if (sta && sta->dpp_pfs) buflen += 5 + sta->dpp_pfs->curve->prime_len; #endif /* CONFIG_DPP2 */ buf = os_zalloc(buflen); if (!buf) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP)); os_memcpy(reply->da, addr, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); send_len = IEEE80211_HDRLEN; send_len += sizeof(reply->u.assoc_resp); reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) | BIT(14) | BIT(15)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); /* Radio measurement capabilities */ p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p); #ifdef CONFIG_MBO if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && rssi != 0) { int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi; p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p, delta); } #endif /* CONFIG_MBO */ #ifdef CONFIG_IEEE80211R_AP if (sta && status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, buf + buflen - p, sta->auth_alg, ies, ies_len, omit_rsnxe); if (!p) { wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); res = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_FILS if (sta && status_code == WLAN_STATUS_SUCCESS && (sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK)) p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p, buf + buflen - p, ies, ies_len); #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (sta && status_code == WLAN_STATUS_SUCCESS && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p, buf + buflen - p, ies, ies_len); #endif /* CONFIG_OWE */ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); p = hostapd_eid_ht_capabilities(hapd, p); p = hostapd_eid_ht_operation(hapd, p); #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && !is_6ghz_op_class(hapd->iconf->op_class)) { u32 nsts = 0, sta_nsts; if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { struct ieee80211_vht_capabilities *capa; nsts = (hapd->iface->conf->vht_capab >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; capa = sta->vht_capabilities; sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; if (nsts < sta_nsts) nsts = 0; else nsts = sta_nsts; } p = hostapd_eid_vht_capabilities(hapd, p, nsts); p = hostapd_eid_vht_operation(hapd, p); } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_he_operation(hapd, p); p = hostapd_eid_spatial_reuse(hapd, p); p = hostapd_eid_he_mu_edca_parameter_set(hapd, p); p = hostapd_eid_he_6ghz_band_cap(hapd, p); } #endif /* CONFIG_IEEE80211AX */ p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta && sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); #ifdef CONFIG_FST if (hapd->iface->fst_ies) { os_memcpy(p, wpabuf_head(hapd->iface->fst_ies), wpabuf_len(hapd->iface->fst_ies)); p += wpabuf_len(hapd->iface->fst_ies); } #endif /* CONFIG_FST */ #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->rsnxe_override_ft && buf + buflen - p >= (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) && sta && sta->auth_alg == WLAN_AUTH_FT) { wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override"); os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft), wpabuf_len(hapd->conf->rsnxe_override_ft)); p += wpabuf_len(hapd->conf->rsnxe_override_ft); goto rsnxe_done; } #endif /* CONFIG_TESTING_OPTIONS */ if (!omit_rsnxe) p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p); #ifdef CONFIG_TESTING_OPTIONS rsnxe_done: #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_OWE if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && !wpa_auth_sta_get_pmksa(sta->wpa_sm)) { struct wpabuf *pub; pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); if (!pub) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } /* OWE Diffie-Hellman Parameter element */ *p++ = WLAN_EID_EXTENSION; /* Element ID */ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ WPA_PUT_LE16(p, sta->owe_group); p += 2; os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); p += wpabuf_len(pub); wpabuf_free(pub); } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) { os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie), wpabuf_len(sta->dpp_pfs->ie)); p += wpabuf_len(sta->dpp_pfs->ie); } #endif /* CONFIG_DPP2 */ #ifdef CONFIG_IEEE80211AC if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); #endif /* CONFIG_IEEE80211AC */ if (sta && (sta->flags & WLAN_STA_WMM)) p = hostapd_eid_wmm(hapd, p); #ifdef CONFIG_WPS if (sta && ((sta->flags & WLAN_STA_WPS) || ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); p += wpabuf_len(wps); wpabuf_free(wps); } } #endif /* CONFIG_WPS */ if (sta && (sta->flags & WLAN_STA_MULTI_AP)) p = hostapd_eid_multi_ap(hapd, p); #ifdef CONFIG_P2P if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { case WLAN_STATUS_SUCCESS: status = P2P_SC_SUCCESS; break; case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: status = P2P_SC_FAIL_LIMIT_REACHED; break; default: status = P2P_SC_FAIL_INVALID_PARAMS; break; } p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status); if (p2p_resp_ie) { os_memcpy(p, wpabuf_head(p2p_resp_ie), wpabuf_len(p2p_resp_ie)); p += wpabuf_len(p2p_resp_ie); wpabuf_free(p2p_resp_ie); } } #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER if (hapd->conf->p2p & P2P_MANAGE) p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ p = hostapd_eid_mbo(hapd, p, buf + buflen - p); if (hapd->conf->assocresp_elements && (size_t) (buf + buflen - p) >= wpabuf_len(hapd->conf->assocresp_elements)) { os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), wpabuf_len(hapd->conf->assocresp_elements)); p += wpabuf_len(hapd->conf->assocresp_elements); } send_len += p - reply->u.assoc_resp.variable; #ifdef CONFIG_FILS if (sta && (sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK) && status_code == WLAN_STATUS_SUCCESS) { struct ieee802_11_elems elems; if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed || !elems.fils_session) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } /* FILS Session */ *p++ = WLAN_EID_EXTENSION; /* Element ID */ *p++ = 1 + FILS_SESSION_LEN; /* Length */ *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ os_memcpy(p, elems.fils_session, FILS_SESSION_LEN); send_len += 2 + 1 + FILS_SESSION_LEN; send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len, buflen, sta->fils_hlp_resp); if (send_len < 0) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; goto done; } } #endif /* CONFIG_FILS */ if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); res = WLAN_STATUS_UNSPECIFIED_FAILURE; } done: os_free(buf); return res; } #ifdef CONFIG_OWE u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh, u8 owe_dh_len, u8 *owe_buf, size_t owe_buf_len, u16 *status) { #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->own_ie_override) { wpa_printf(MSG_DEBUG, "OWE: Using IE override"); *status = WLAN_STATUS_SUCCESS; return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, owe_buf_len, NULL, 0); } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, owe_buf_len, NULL, 0); *status = WLAN_STATUS_SUCCESS; return owe_buf; } if (sta->owe_pmk && sta->external_dh_updated) { wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK"); *status = WLAN_STATUS_SUCCESS; return owe_buf; } *status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); if (*status != WLAN_STATUS_SUCCESS) return NULL; owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, owe_buf_len, NULL, 0); if (sta->owe_ecdh && owe_buf) { struct wpabuf *pub; pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); if (!pub) { *status = WLAN_STATUS_UNSPECIFIED_FAILURE; return owe_buf; } /* OWE Diffie-Hellman Parameter element */ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ WPA_PUT_LE16(owe_buf, sta->owe_group); owe_buf += 2; os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); owe_buf += wpabuf_len(pub); wpabuf_free(pub); } return owe_buf; } #endif /* CONFIG_OWE */ #ifdef CONFIG_FILS void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) { u16 reply_res; wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR, MAC2STR(sta->addr)); eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); if (!sta->fils_pending_assoc_req) return; reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, sta->fils_pending_assoc_is_reassoc, sta->fils_pending_assoc_req, sta->fils_pending_assoc_req_len, 0, 0); os_free(sta->fils_pending_assoc_req); sta->fils_pending_assoc_req = NULL; sta->fils_pending_assoc_req_len = 0; wpabuf_free(sta->fils_hlp_resp); sta->fils_hlp_resp = NULL; wpabuf_free(sta->hlp_dhcp_discover); sta->hlp_dhcp_discover = NULL; /* * Remove the station in case transmission of a success response fails. * At this point the station was already added associated to the driver. */ if (reply_res != WLAN_STATUS_SUCCESS) hostapd_drv_sta_remove(hapd, sta->addr); } void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = eloop_data; wpa_printf(MSG_DEBUG, "FILS: HLP response timeout - continue with association response for " MACSTR, MAC2STR(sta->addr)); if (sta->fils_drv_assoc_finish) hostapd_notify_assoc_fils_finish(hapd, sta); else fils_hlp_finish_assoc(hapd, sta); } #endif /* CONFIG_FILS */ static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int rssi) { u16 capab_info, listen_interval, seq_ctrl, fc; int resp = WLAN_STATUS_SUCCESS; u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; const u8 *pos; int left, i; struct sta_info *sta; u8 *tmp = NULL; #ifdef CONFIG_FILS int delay_assoc = 0; #endif /* CONFIG_FILS */ int omit_rsnxe = 0; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long) len); return; } #ifdef CONFIG_TESTING_OPTIONS if (reassoc) { if (hapd->iconf->ignore_reassoc_probability > 0.0 && drand48() < hapd->iconf->ignore_reassoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring reassoc request from " MACSTR, MAC2STR(mgmt->sa)); return; } } else { if (hapd->iconf->ignore_assoc_probability > 0.0 && drand48() < hapd->iconf->ignore_assoc_probability) { wpa_printf(MSG_INFO, "TESTING: ignoring assoc request from " MACSTR, MAC2STR(mgmt->sa)); return; } } #endif /* CONFIG_TESTING_OPTIONS */ fc = le_to_host16(mgmt->frame_control); seq_ctrl = le_to_host16(mgmt->seq_ctrl); if (reassoc) { capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.reassoc_req.listen_interval); wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d current_ap=" MACSTR " seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), capab_info, listen_interval, MAC2STR(mgmt->u.reassoc_req.current_ap), seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); pos = mgmt->u.reassoc_req.variable; } else { capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.assoc_req.listen_interval); wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d " "seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), capab_info, listen_interval, seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); pos = mgmt->u.assoc_req.variable; } sta = ap_get_sta(hapd, mgmt->sa); #ifdef CONFIG_IEEE80211R_AP if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); /* * Mark station as authenticated, to avoid adding station * entry in the driver as associated and not authenticated */ sta->flags |= WLAN_STA_AUTH; } else #endif /* CONFIG_IEEE80211R_AP */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { int acl_res; struct radius_sta info; acl_res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, &info); if (acl_res == HOSTAPD_ACL_REJECT) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Ignore Association Request frame from " MACSTR " due to ACL reject", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (acl_res == HOSTAPD_ACL_PENDING) return; /* DMG/IEEE 802.11ad does not use authentication. * Allocate sta entry upon association. */ sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Failed to add STA"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } acl_res = ieee802_11_set_radius_info( hapd, sta, acl_res, &info); if (acl_res) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Skip authentication for DMG/IEEE 802.11ad"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_OPEN; } else { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to associate before authentication (aid=%d flags=0x%x)", sta ? sta->aid : -1, sta ? sta->flags : 0); send_deauth(hapd, mgmt->sa, WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); return; } } if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ : WLAN_FC_STYPE_ASSOC_REQ)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated association frame seq_ctrl=0x%x", seq_ctrl); return; } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ : WLAN_FC_STYPE_ASSOC_REQ; if (hapd->tkip_countermeasures) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (listen_interval > hapd->conf->max_listen_interval) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Too large Listen Interval (%d)", listen_interval); resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; goto fail; } #ifdef CONFIG_MBO if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } if (hapd->iconf->rssi_reject_assoc_rssi && rssi && rssi < hapd->iconf->rssi_reject_assoc_rssi && (sta->auth_rssi == 0 || sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) { resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS; goto fail; } #endif /* CONFIG_MBO */ /* * sta->capability is used in check_assoc_ies() for RRM enabled * capability element. */ sta->capability = capab_info; #ifdef CONFIG_FILS if (sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK) { int res; /* The end of the payload is encrypted. Need to decrypt it * before parsing. */ tmp = os_memdup(pos, left); if (!tmp) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt, len, tmp, left); if (res < 0) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } pos = tmp; left = res; } #endif /* CONFIG_FILS */ /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); if (resp != WLAN_STATUS_SUCCESS) goto fail; omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX); if (hostapd_get_aid(hapd, sta) < 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No room for more AIDs"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } sta->listen_interval = listen_interval; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) sta->flags |= WLAN_STA_NONERP; for (i = 0; i < sta->supported_rates_len; i++) { if ((sta->supported_rates[i] & 0x7f) > 22) { sta->flags &= ~WLAN_STA_NONERP; break; } } if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { sta->nonerp_set = 1; hapd->iface->num_sta_non_erp++; if (hapd->iface->num_sta_non_erp == 1) ieee802_11_set_beacons(hapd->iface); } if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && !sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 1; hapd->iface->num_sta_no_short_slot_time++; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 1) ieee802_11_set_beacons(hapd->iface); } if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) sta->flags |= WLAN_STA_SHORT_PREAMBLE; else sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && !sta->no_short_preamble_set) { sta->no_short_preamble_set = 1; hapd->iface->num_sta_no_short_preamble++; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 1) ieee802_11_set_beacons(hapd->iface); } update_ht_state(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "association OK (aid %d)", sta->aid); /* Station will be marked associated, after it acknowledges AssocResp */ sta->flags |= WLAN_STA_ASSOC_REQ_OK; if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " "SA Query procedure", reassoc ? "re" : ""); /* TODO: Send a protected Disassociate frame to the STA using * the old key and Reason Code "Previous Authentication no * longer valid". Make sure this is only sent protected since * unprotected frame would be received by the STA that is now * trying to associate. */ } /* Make sure that the previously registered inactivity timer will not * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; #ifdef CONFIG_TAXONOMY taxonomy_sta_info_assoc_req(hapd, sta, pos, left); #endif /* CONFIG_TAXONOMY */ sta->pending_wds_enable = 0; #ifdef CONFIG_FILS if (sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK) { if (fils_process_hlp(hapd, sta, pos, left) > 0) delay_assoc = 1; } #endif /* CONFIG_FILS */ fail: /* * In case of a successful response, add the station to the driver. * Otherwise, the kernel may ignore Data frames before we process the * ACK frame (TX status). In case of a failure, this station will be * removed. * * Note that this is not compliant with the IEEE 802.11 standard that * states that a non-AP station should transition into the * authenticated/associated state only after the station acknowledges * the (Re)Association Response frame. However, still do this as: * * 1. In case the station does not acknowledge the (Re)Association * Response frame, it will be removed. * 2. Data frames will be dropped in the kernel until the station is * set into authorized state, and there are no significant known * issues with processing other non-Data Class 3 frames during this * window. */ if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta, reassoc)) resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; #ifdef CONFIG_FILS if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS && eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) && sta->fils_pending_assoc_req) { /* Do not reschedule fils_hlp_timeout in case the station * retransmits (Re)Association Request frame while waiting for * the previously started FILS HLP wait, so that the timeout can * be determined from the first pending attempt. */ wpa_printf(MSG_DEBUG, "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to " MACSTR, MAC2STR(sta->addr)); os_free(tmp); return; } if (sta) { eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); os_free(sta->fils_pending_assoc_req); sta->fils_pending_assoc_req = NULL; sta->fils_pending_assoc_req_len = 0; wpabuf_free(sta->fils_hlp_resp); sta->fils_hlp_resp = NULL; } if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) { sta->fils_pending_assoc_req = tmp; sta->fils_pending_assoc_req_len = left; sta->fils_pending_assoc_is_reassoc = reassoc; sta->fils_drv_assoc_finish = 0; wpa_printf(MSG_DEBUG, "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " MACSTR, MAC2STR(sta->addr)); eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024, fils_hlp_timeout, hapd, sta); return; } #endif /* CONFIG_FILS */ if (resp >= 0) reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, left, rssi, omit_rsnxe); os_free(tmp); /* * Remove the station in case transmission of a success response fails * (the STA was added associated to the driver) or if the station was * previously added unassociated. */ if (sta && ((reply_res != WLAN_STATUS_SUCCESS && resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } } static void handle_disassoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)", (unsigned long) len); return; } wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated", MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); hostapd_set_sta_flags(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(hapd, sta); if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { sta->timeout_next = STA_DEAUTH; eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); } mlme_disassociate_indication( hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon * disassociation. */ if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { sta->flags &= ~WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); ap_free_sta(hapd, sta); } } static void handle_deauth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short " "payload (len=%lu)", (unsigned long) len); return; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code)); /* Clear the PTKSA cache entries for PASN */ ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " "to deauthenticate, but it is not authenticated", MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); hostapd_set_sta_flags(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); mlme_deauthenticate_indication( hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); ap_free_sta(hapd, sta); } static void handle_beacon(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, struct hostapd_frame_info *fi) { struct ieee802_11_elems elems; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)", (unsigned long) len); return; } (void) ieee802_11_parse_elems(mgmt->u.beacon.variable, len - (IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)), &elems, 0); ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); } static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && category != WLAN_ACTION_HT; } static int handle_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, unsigned int freq) { struct sta_info *sta; u8 *action __maybe_unused; if (len < IEEE80211_HDRLEN + 2 + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", (unsigned long) len); return 0; } action = (u8 *) &mgmt->u.action.u; wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR " da " MACSTR " len %d freq %u", mgmt->u.action.category, *action, MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq); sta = ap_get_sta(hapd, mgmt->sa); if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " "frame (category=%u) from unassociated STA " MACSTR, mgmt->u.action.category, MAC2STR(mgmt->sa)); return 0; } if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && robust_action_frame(mgmt->u.action.category)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Dropped unprotected Robust Action frame from " "an MFP STA"); return 0; } if (sta) { u16 fc = le_to_host16(mgmt->frame_control); u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl); if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && sta->last_subtype == WLAN_FC_STYPE_ACTION) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated action frame seq_ctrl=0x%x", seq_ctrl); return 1; } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_ACTION; } switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R_AP case WLAN_ACTION_FT: if (!sta || wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; return 1; #endif /* CONFIG_IEEE80211R_AP */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; case WLAN_ACTION_SA_QUERY: ieee802_11_sa_query_action(hapd, mgmt, len); return 1; #ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; #endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST case WLAN_ACTION_FST: if (hapd->iface->fst) fst_rx_action(hapd->iface->fst, mgmt, len); else wpa_printf(MSG_DEBUG, "FST: Ignore FST Action frame - no FST attached"); return 1; #endif /* CONFIG_FST */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: if (len >= IEEE80211_HDRLEN + 2 && mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { hostapd_2040_coex_action(hapd, mgmt, len); return 1; } #ifdef CONFIG_DPP if (len >= IEEE80211_HDRLEN + 6 && mgmt->u.action.u.vs_public_action.action == WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == OUI_WFA && mgmt->u.action.u.vs_public_action.variable[0] == DPP_OUI_TYPE) { const u8 *pos, *end; pos = mgmt->u.action.u.vs_public_action.oui; end = ((const u8 *) mgmt) + len; hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, freq); return 1; } if (len >= IEEE80211_HDRLEN + 2 && (mgmt->u.action.u.public_action.action == WLAN_PA_GAS_INITIAL_RESP || mgmt->u.action.u.public_action.action == WLAN_PA_GAS_COMEBACK_RESP)) { const u8 *pos, *end; pos = &mgmt->u.action.u.public_action.action; end = ((const u8 *) mgmt) + len; gas_query_ap_rx(hapd->gas, mgmt->sa, mgmt->u.action.category, pos, end - pos, hapd->iface->freq); return 1; } #endif /* CONFIG_DPP */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq); } if (hapd->public_action_cb2) { hapd->public_action_cb2(hapd->public_action_cb2_ctx, (u8 *) mgmt, len, hapd->iface->freq); } if (hapd->public_action_cb || hapd->public_action_cb2) return 1; break; case WLAN_ACTION_VENDOR_SPECIFIC: if (hapd->vendor_action_cb) { if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq) == 0) return 1; } break; case WLAN_ACTION_RADIO_MEASUREMENT: hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); return 1; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); if (!is_multicast_ether_addr(mgmt->da) && !(mgmt->u.action.category & 0x80) && !is_multicast_ether_addr(mgmt->sa)) { struct ieee80211_mgmt *resp; /* * IEEE 802.11-REVma/D9.0 - 7.3.1.11 * Return the Action frame to the source without change * except that MSB of the Category set to 1. */ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " "frame back to sender"); resp = os_memdup(mgmt, len); if (resp == NULL) return 0; os_memcpy(resp->da, resp->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) { wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " "Action frame"); } os_free(resp); } return 1; } /** * notify_mgmt_frame - Notify of Management frames on the control interface * @hapd: hostapd BSS data structure (the BSS to which the Management frame was * sent to) * @buf: Management frame data (starting from the IEEE 802.11 header) * @len: Length of frame data in octets * * Notify the control interface of any received Management frame. */ static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf, size_t len) { int hex_len = len * 2 + 1; char *hex = os_malloc(hex_len); if (hex) { wpa_snprintf_hex(hex, hex_len, buf, len); wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, AP_MGMT_FRAME_RECEIVED "buf=%s", hex); os_free(hex); } } /** * ieee802_11_mgmt - process incoming IEEE 802.11 management frames * @hapd: hostapd BSS data structure (the BSS to which the management frame was * sent to) * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @fi: meta data about received frame (signal level, etc.) * * Process all incoming IEEE 802.11 management frames. This will be called for * each frame received from the kernel driver through wlan#ap interface. In * addition, it can be called to re-inserted pending frames (e.g., when using * external RADIUS server as an MAC ACL). */ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; u16 fc, stype; int ret = 0; unsigned int freq; int ssi_signal = fi ? fi->ssi_signal : 0; if (len < 24) return 0; if (fi && fi->freq) freq = fi->freq; else freq = hapd->iface->freq; mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); if (is_multicast_ether_addr(mgmt->sa) || is_zero_ether_addr(mgmt->sa) || os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { /* Do not process any frames with unexpected/invalid SA so that * we do not add any state for unexpected STA addresses or end * up sending out frames to unexpected destination. */ wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR " in received frame - ignore this frame silently", MAC2STR(mgmt->sa)); return 0; } if (stype == WLAN_FC_STYPE_BEACON) { handle_beacon(hapd, mgmt, len, fi); return 1; } if (!is_broadcast_ether_addr(mgmt->bssid) && #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && stype == WLAN_FC_STYPE_ACTION) && #endif /* CONFIG_P2P */ #ifdef CONFIG_MESH !(hapd->conf->mesh & MESH_ENABLED) && #endif /* CONFIG_MESH */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", MAC2STR(mgmt->bssid)); return 0; } if (hapd->iface->state != HAPD_IFACE_ENABLED) { wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)", MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype); return 1; } if (stype == WLAN_FC_STYPE_PROBE_REQ) { handle_probe_req(hapd, mgmt, len, ssi_signal); return 1; } if ((!is_broadcast_ether_addr(mgmt->da) || stype != WLAN_FC_STYPE_ACTION) && os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", MAC2STR(mgmt->da)); return 0; } if (hapd->iconf->track_sta_max_num) sta_track_add(hapd->iface, mgmt->sa, ssi_signal); if (hapd->conf->notify_mgmt_frames) notify_mgmt_frame(hapd, buf, len); switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); handle_auth(hapd, mgmt, len, ssi_signal, 0); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); handle_assoc(hapd, mgmt, len, 0, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); handle_assoc(hapd, mgmt, len, 1, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc"); handle_disassoc(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_DEAUTH: wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); ret = 1; break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); ret = handle_action(hapd, mgmt, len, freq); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "unknown mgmt frame subtype %d", stype); break; } return ret; } static void handle_auth_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; bool success_status; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long) len); auth_alg = 0; auth_transaction = 0; status_code = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); goto fail; } if (status_code == WLAN_STATUS_SUCCESS && ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; if (sta->added_unassoc) hostapd_set_sta_flags(hapd, sta); return; } fail: success_status = status_code == WLAN_STATUS_SUCCESS; #ifdef CONFIG_SAE if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1) success_status = sae_status_success(hapd, status_code); #endif /* CONFIG_SAE */ if (!success_status && sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } } static void hostapd_set_wds_encryption(struct hostapd_data *hapd, struct sta_info *sta, char *ifname_wds) { #ifdef CONFIG_WEP int i; struct hostapd_ssid *ssid = &hapd->conf->ssid; if (hapd->conf->ieee802_1x || hapd->conf->wpa) return; for (i = 0; i < 4; i++) { if (ssid->wep.key[i] && hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, 0, i == ssid->wep.idx, NULL, 0, ssid->wep.key[i], ssid->wep.len[i], i == ssid->wep.idx ? KEY_FLAG_GROUP_RX_TX_DEFAULT : KEY_FLAG_GROUP_RX_TX)) { wpa_printf(MSG_WARNING, "Could not set WEP keys for WDS interface; %s", ifname_wds); break; } } #endif /* CONFIG_WEP */ } static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) { u16 status; struct sta_info *sta; int new_assoc = 1; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", reassoc, (unsigned long) len); hostapd_drv_sta_remove(hapd, sta->addr); return; } if (reassoc) status = le_to_host16(mgmt->u.reassoc_resp.status_code); else status = le_to_host16(mgmt->u.assoc_resp.status_code); if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; /* The STA is added only in case of SUCCESS */ if (status == WLAN_STATUS_SUCCESS) hostapd_drv_sta_remove(hapd, sta->addr); return; } if (status != WLAN_STATUS_SUCCESS) return; /* Stop previous accounting session, if one is started, and allocate * new session id for the new session. */ accounting_sta_stop(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated (aid %d)", sta->aid); if (sta->flags & WLAN_STA_ASSOC) new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) || sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK || sta->auth_alg == WLAN_AUTH_FT) { /* * Open, static WEP, FT protocol, or FILS; no separate * authorization step. */ ap_sta_set_authorized(hapd, sta, 1); } if (reassoc) mlme_reassociate_indication(hapd, sta); else mlme_associate_indication(hapd, sta); sta->sa_query_timed_out = 0; if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, * so bind it to the selected VLAN interface now, since the * interface selection is not going to change anymore. */ if (ap_sta_bind_vlan(hapd, sta) < 0) return; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ if (ap_sta_bind_vlan(hapd, sta) < 0) return; } hostapd_set_sta_flags(hapd, sta); if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) { wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA " MACSTR " based on pending request", MAC2STR(sta->addr)); sta->pending_wds_enable = 0; sta->flags |= WLAN_STA_WDS; } if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) { int ret; char ifname_wds[IFNAMSIZ + 1]; wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, sta->aid, 1); if (!ret) hostapd_set_wds_encryption(hapd, sta, ifname_wds); } if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); #ifdef CONFIG_FILS if ((sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK) && fils_set_tk(sta->wpa_sm) < 0) { wpa_printf(MSG_DEBUG, "FILS: TK configuration failed"); ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_UNSPECIFIED); return; } #endif /* CONFIG_FILS */ if (sta->pending_eapol_rx) { struct os_reltime now, age; os_get_reltime(&now); os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age); if (age.sec == 0 && age.usec < 200000) { wpa_printf(MSG_DEBUG, "Process pending EAPOL frame that was received from " MACSTR " just before association notification", MAC2STR(sta->addr)); ieee802_1x_receive( hapd, mgmt->da, wpabuf_head(sta->pending_eapol_rx->buf), wpabuf_len(sta->pending_eapol_rx->buf)); } wpabuf_free(sta->pending_eapol_rx->buf); os_free(sta->pending_eapol_rx); sta->pending_eapol_rx = NULL; } } static void handle_deauth_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (ok) wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth", MAC2STR(sta->addr)); else wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " "deauth", MAC2STR(sta->addr)); ap_sta_deauth_cb(hapd, sta); } static void handle_disassoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (ok) wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc", MAC2STR(sta->addr)); else wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " "disassoc", MAC2STR(sta->addr)); ap_sta_disassoc_cb(hapd, sta); } static void handle_action_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ok) { struct sta_info *sta; const struct rrm_measurement_report_element *report; if (is_multicast_ether_addr(mgmt->da)) return; #ifdef CONFIG_DPP if (len >= IEEE80211_HDRLEN + 6 && mgmt->u.action.category == WLAN_ACTION_PUBLIC && mgmt->u.action.u.vs_public_action.action == WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == OUI_WFA && mgmt->u.action.u.vs_public_action.variable[0] == DPP_OUI_TYPE) { const u8 *pos, *end; pos = &mgmt->u.action.u.vs_public_action.variable[1]; end = ((const u8 *) mgmt) + len; hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok); return; } if (len >= IEEE80211_HDRLEN + 2 && mgmt->u.action.category == WLAN_ACTION_PUBLIC && (mgmt->u.action.u.public_action.action == WLAN_PA_GAS_INITIAL_REQ || mgmt->u.action.u.public_action.action == WLAN_PA_GAS_COMEBACK_REQ)) { const u8 *pos, *end; pos = mgmt->u.action.u.public_action.variable; end = ((const u8 *) mgmt) + len; gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok); return; } #endif /* CONFIG_DPP */ sta = ap_get_sta(hapd, mgmt->da); if (!sta) { wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR " not found", MAC2STR(mgmt->da)); return; } if (len < 24 + 5 + sizeof(*report)) return; report = (const struct rrm_measurement_report_element *) &mgmt->u.action.u.rrm.variable[2]; if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT && mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST && report->eid == WLAN_EID_MEASURE_REQUEST && report->len >= 3 && report->type == MEASURE_TYPE_BEACON) hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok); } /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame * was sent from) * @buf: management frame data (starting from IEEE 802.11 header) * @len: length of frame data in octets * @stype: management frame subtype from frame control field * @ok: Whether the frame was ACK'ed */ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_mgmt_frame_handling) { size_t hex_len = 2 * len + 1; char *hex = os_malloc(hex_len); if (hex) { wpa_snprintf_hex(hex, hex_len, buf, len); wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d buf=%s", stype, ok, hex); os_free(hex); } return; } #endif /* CONFIG_TESTING_OPTIONS */ switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth cb"); handle_auth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ASSOC_RESP: wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 0, ok); break; case WLAN_FC_STYPE_REASSOC_RESP: wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok); break; case WLAN_FC_STYPE_DEAUTH: wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); handle_deauth_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc cb"); handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); handle_action_cb(hapd, mgmt, len, ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); break; } } int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { /* TODO */ return 0; } int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { /* TODO */ return 0; } void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, addr); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, addr); if (sta) break; } } if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) return; if (sta->flags & WLAN_STA_PENDING_POLL) { wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " "activity poll", MAC2STR(sta->addr), ack ? "ACKed" : "did not ACK"); if (ack) sta->flags &= ~WLAN_STA_PENDING_POLL; } ieee802_1x_tx_status(hapd, sta, buf, len, ack); } void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, const u8 *data, size_t len, int ack) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, dst); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, dst); if (sta) break; } } if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA " MACSTR " that is not currently associated", MAC2STR(dst)); return; } ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack); } void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; struct hostapd_iface *iface = hapd->iface; sta = ap_get_sta(hapd, addr); if (sta == NULL && iface->num_bss > 1) { size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; sta = ap_get_sta(hapd, addr); if (sta) break; } } if (sta == NULL) return; wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR, MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_PENDING_POLL)) return; wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending " "activity poll", MAC2STR(sta->addr)); sta->flags &= ~WLAN_STA_PENDING_POLL; } void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds) { struct sta_info *sta; sta = ap_get_sta(hapd, src); if (sta && ((sta->flags & WLAN_STA_ASSOC) || ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) { if (!hapd->conf->wds_sta) return; if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) == WLAN_STA_ASSOC_REQ_OK) { wpa_printf(MSG_DEBUG, "Postpone 4-address WDS mode enabling for STA " MACSTR " since TX status for AssocResp is not yet known", MAC2STR(sta->addr)); sta->pending_wds_enable = 1; return; } if (wds && !(sta->flags & WLAN_STA_WDS)) { int ret; char ifname_wds[IFNAMSIZ + 1]; wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, sta->aid, 1); if (!ret) hostapd_set_wds_encryption(hapd, sta, ifname_wds); } return; } wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) || os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) { /* Broadcast bit set in SA or unexpected SA?! Ignore the frame * silently. */ return; } if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) { wpa_printf(MSG_DEBUG, "Association Response to the STA has " "already been sent, but no TX status yet known - " "ignore Class 3 frame issue with " MACSTR, MAC2STR(src)); return; } if (sta && (sta->flags & WLAN_STA_AUTH)) hostapd_drv_sta_disassoc( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); else hostapd_drv_sta_deauth( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); } u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) { struct hostapd_iface *iface = hapd->iface; struct hostapd_config *iconf = iface->conf; struct hostapd_hw_modes *mode = iface->current_mode; struct hostapd_channel_data *chan; int dfs, i; u8 channel, tx_pwr_count, local_pwr_constraint; int max_tx_power; u8 tx_pwr; if (!mode) return eid; if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) return eid; for (i = 0; i < mode->num_channels; i++) { if (mode->channels[i].freq == iface->freq) break; } if (i == mode->num_channels) return eid; switch (hostapd_get_oper_chwidth(iconf)) { case CHANWIDTH_USE_HT: if (iconf->secondary_channel == 0) { /* Max Transmit Power count = 0 (20 MHz) */ tx_pwr_count = 0; } else { /* Max Transmit Power count = 1 (20, 40 MHz) */ tx_pwr_count = 1; } break; case CHANWIDTH_80MHZ: /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ tx_pwr_count = 2; break; case CHANWIDTH_80P80MHZ: case CHANWIDTH_160MHZ: /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ tx_pwr_count = 3; break; default: return eid; } /* * Below local_pwr_constraint logic is referred from * hostapd_eid_pwr_constraint. * * Check if DFS is required by regulatory. */ dfs = hostapd_is_dfs_required(hapd->iface); if (dfs < 0) dfs = 0; /* * In order to meet regulations when TPC is not implemented using * a transmit power that is below the legal maximum (including any * mitigation factor) should help. In this case, indicate 3 dB below * maximum allowed transmit power. */ if (hapd->iconf->local_pwr_constraint == -1) local_pwr_constraint = (dfs == 0) ? 0 : 3; else local_pwr_constraint = hapd->iconf->local_pwr_constraint; /* * A STA that is not an AP shall use a transmit power less than or * equal to the local maximum transmit power level for the channel. * The local maximum transmit power can be calculated from the formula: * local max TX pwr = max TX pwr - local pwr constraint * Where max TX pwr is maximum transmit power level specified for * channel in Country element and local pwr constraint is specified * for channel in this Power Constraint element. */ chan = &mode->channels[i]; max_tx_power = chan->max_tx_power - local_pwr_constraint; /* * Local Maximum Transmit power is encoded as two's complement * with a 0.5 dB step. */ max_tx_power *= 2; /* in 0.5 dB steps */ if (max_tx_power > 127) { /* 63.5 has special meaning of 63.5 dBm or higher */ max_tx_power = 127; } if (max_tx_power < -128) max_tx_power = -128; if (max_tx_power < 0) tx_pwr = 0x80 + max_tx_power + 128; else tx_pwr = max_tx_power; *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; *eid++ = 2 + tx_pwr_count; /* * Max Transmit Power count and * Max Transmit Power units = 0 (EIRP) */ *eid++ = tx_pwr_count; for (i = 0; i <= tx_pwr_count; i++) *eid++ = tx_pwr; return eid; } u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) { u8 bw, chan1, chan2 = 0; int freq1; if (!hapd->cs_freq_params.channel || (!hapd->cs_freq_params.vht_enabled && !hapd->cs_freq_params.he_enabled)) return eid; /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ switch (hapd->cs_freq_params.bandwidth) { case 40: bw = 0; break; case 80: /* check if it's 80+80 */ if (!hapd->cs_freq_params.center_freq2) bw = 1; else bw = 3; break; case 160: bw = 2; break; default: /* not valid VHT bandwidth or not in CSA */ return eid; } freq1 = hapd->cs_freq_params.center_freq1 ? hapd->cs_freq_params.center_freq1 : hapd->cs_freq_params.freq; if (ieee80211_freq_to_chan(freq1, &chan1) != HOSTAPD_MODE_IEEE80211A) return eid; if (hapd->cs_freq_params.center_freq2 && ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2, &chan2) != HOSTAPD_MODE_IEEE80211A) return eid; *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER; *eid++ = 5; /* Length of Channel Switch Wrapper */ *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH; *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ *eid++ = bw; /* New Channel Width */ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ return eid; } #endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 7ca292530dc1..3e992155395e 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1,1633 +1,1634 @@ /* * hostapd / WPA authenticator glue code * Copyright (c) 2002-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" #include "common/ptksa_cache.h" #include "crypto/sha1.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" #include "eth_p_oui.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" #include "sta_info.h" #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" #include "ieee802_11.h" #include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, struct hostapd_config *iconf, struct wpa_auth_config *wconf) { int sae_pw_id; os_memset(wconf, 0, sizeof(*wconf)); wconf->wpa = conf->wpa; wconf->extended_key_id = conf->extended_key_id; wconf->wpa_key_mgmt = conf->wpa_key_mgmt; wconf->wpa_pairwise = conf->wpa_pairwise; wconf->wpa_group = conf->wpa_group; wconf->wpa_group_rekey = conf->wpa_group_rekey; wconf->wpa_strict_rekey = conf->wpa_strict_rekey; wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; wconf->wpa_group_update_count = conf->wpa_group_update_count; wconf->wpa_disable_eapol_key_retries = conf->wpa_disable_eapol_key_retries; wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; #ifdef CONFIG_MACSEC if (wconf->eapol_version > 2) wconf->eapol_version = 2; #endif /* CONFIG_MACSEC */ wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; #ifdef CONFIG_OCV wconf->ocv = conf->ocv; #endif /* CONFIG_OCV */ wconf->okc = conf->okc; wconf->ieee80211w = conf->ieee80211w; wconf->beacon_prot = conf->beacon_prot; wconf->group_mgmt_cipher = conf->group_mgmt_cipher; wconf->sae_require_mfp = conf->sae_require_mfp; #ifdef CONFIG_IEEE80211R_AP wconf->ssid_len = conf->ssid.ssid_len; if (wconf->ssid_len > SSID_MAX_LEN) wconf->ssid_len = SSID_MAX_LEN; os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); os_memcpy(wconf->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); if (conf->nas_identifier && os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); os_memcpy(wconf->r0_key_holder, conf->nas_identifier, wconf->r0_key_holder_len); } os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); wconf->r0_key_lifetime = conf->r0_key_lifetime; wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime; wconf->reassociation_deadline = conf->reassociation_deadline; wconf->rkh_pos_timeout = conf->rkh_pos_timeout; wconf->rkh_neg_timeout = conf->rkh_neg_timeout; wconf->rkh_pull_timeout = conf->rkh_pull_timeout; wconf->rkh_pull_retries = conf->rkh_pull_retries; wconf->r0kh_list = &conf->r0kh_list; wconf->r1kh_list = &conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; wconf->ft_psk_generate_local = conf->ft_psk_generate_local; #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; if (conf->osen) { wconf->disable_gtk = 1; wconf->wpa = WPA_PROTO_OSEN; wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; wconf->wpa_pairwise = 0; wconf->wpa_group = WPA_CIPHER_CCMP; wconf->rsn_pairwise = WPA_CIPHER_CCMP; wconf->rsn_preauth = 0; wconf->disable_pmksa_caching = 1; wconf->ieee80211w = 1; } #endif /* CONFIG_HS20 */ #ifdef CONFIG_TESTING_OPTIONS wconf->corrupt_gtk_rekey_mic_probability = iconf->corrupt_gtk_rekey_mic_probability; if (conf->own_ie_override && wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) { wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override); os_memcpy(wconf->own_ie_override, wpabuf_head(conf->own_ie_override), wconf->own_ie_override_len); } if (conf->rsne_override_eapol && wpabuf_len(conf->rsne_override_eapol) <= MAX_OWN_IE_OVERRIDE) { wconf->rsne_override_eapol_set = 1; wconf->rsne_override_eapol_len = wpabuf_len(conf->rsne_override_eapol); os_memcpy(wconf->rsne_override_eapol, wpabuf_head(conf->rsne_override_eapol), wconf->rsne_override_eapol_len); } if (conf->rsnxe_override_eapol && wpabuf_len(conf->rsnxe_override_eapol) <= MAX_OWN_IE_OVERRIDE) { wconf->rsnxe_override_eapol_set = 1; wconf->rsnxe_override_eapol_len = wpabuf_len(conf->rsnxe_override_eapol); os_memcpy(wconf->rsnxe_override_eapol, wpabuf_head(conf->rsnxe_override_eapol), wconf->rsnxe_override_eapol_len); } if (conf->rsne_override_ft && wpabuf_len(conf->rsne_override_ft) <= MAX_OWN_IE_OVERRIDE) { wconf->rsne_override_ft_set = 1; wconf->rsne_override_ft_len = wpabuf_len(conf->rsne_override_ft); os_memcpy(wconf->rsne_override_ft, wpabuf_head(conf->rsne_override_ft), wconf->rsne_override_ft_len); } if (conf->rsnxe_override_ft && wpabuf_len(conf->rsnxe_override_ft) <= MAX_OWN_IE_OVERRIDE) { wconf->rsnxe_override_ft_set = 1; wconf->rsnxe_override_ft_len = wpabuf_len(conf->rsnxe_override_ft); os_memcpy(wconf->rsnxe_override_ft, wpabuf_head(conf->rsnxe_override_ft), wconf->rsnxe_override_ft_len); } if (conf->gtk_rsc_override && wpabuf_len(conf->gtk_rsc_override) > 0 && wpabuf_len(conf->gtk_rsc_override) <= WPA_KEY_RSC_LEN) { os_memcpy(wconf->gtk_rsc_override, wpabuf_head(conf->gtk_rsc_override), wpabuf_len(conf->gtk_rsc_override)); wconf->gtk_rsc_override_set = 1; } if (conf->igtk_rsc_override && wpabuf_len(conf->igtk_rsc_override) > 0 && wpabuf_len(conf->igtk_rsc_override) <= WPA_KEY_RSC_LEN) { os_memcpy(wconf->igtk_rsc_override, wpabuf_head(conf->igtk_rsc_override), wpabuf_len(conf->igtk_rsc_override)); wconf->igtk_rsc_override_set = 1; } wconf->ft_rsnxe_used = conf->ft_rsnxe_used; wconf->oci_freq_override_eapol_m3 = conf->oci_freq_override_eapol_m3; wconf->oci_freq_override_eapol_g1 = conf->oci_freq_override_eapol_g1; wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc; wconf->oci_freq_override_fils_assoc = conf->oci_freq_override_fils_assoc; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4); os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); #endif /* CONFIG_P2P */ #ifdef CONFIG_FILS wconf->fils_cache_id_set = conf->fils_cache_id_set; os_memcpy(wconf->fils_cache_id, conf->fils_cache_id, FILS_CACHE_ID_LEN); #endif /* CONFIG_FILS */ wconf->sae_pwe = conf->sae_pwe; sae_pw_id = hostapd_sae_pw_id_in_use(conf); if (sae_pw_id == 2 && wconf->sae_pwe != 3) wconf->sae_pwe = 1; else if (sae_pw_id == 1 && wconf->sae_pwe == 0) wconf->sae_pwe = 2; #ifdef CONFIG_SAE_PK wconf->sae_pk = hostapd_sae_pk_in_use(conf); #endif /* CONFIG_SAE_PK */ #ifdef CONFIG_OWE wconf->owe_ptk_workaround = conf->owe_ptk_workaround; #endif /* CONFIG_OWE */ wconf->transition_disable = conf->transition_disable; #ifdef CONFIG_DPP2 wconf->dpp_pfs = conf->dpp_pfs; #endif /* CONFIG_DPP2 */ #ifdef CONFIG_PASN #ifdef CONFIG_TESTING_OPTIONS wconf->force_kdk_derivation = conf->force_kdk_derivation; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_PASN */ } static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, logger_level level, const char *txt) { #ifndef CONFIG_NO_HOSTAPD_LOGGER struct hostapd_data *hapd = ctx; int hlevel; switch (level) { case LOGGER_WARNING: hlevel = HOSTAPD_LEVEL_WARNING; break; case LOGGER_INFO: hlevel = HOSTAPD_LEVEL_INFO; break; case LOGGER_DEBUG: default: hlevel = HOSTAPD_LEVEL_DEBUG; break; } hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); #endif /* CONFIG_NO_HOSTAPD_LOGGER */ } static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, u16 reason) { struct hostapd_data *hapd = ctx; wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " "STA " MACSTR " reason %d", __func__, MAC2STR(addr), reason); ap_sta_disconnect(hapd, NULL, addr, reason); } static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) { struct hostapd_data *hapd = ctx; return michael_mic_failure(hapd, addr, 0); } static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr) { struct hostapd_data *hapd = ctx; wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR, MAC2STR(addr)); } static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var, int value) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); if (sta == NULL) return; switch (var) { case WPA_EAPOL_portEnabled: ieee802_1x_notify_port_enabled(sta->eapol_sm, value); break; case WPA_EAPOL_portValid: ieee802_1x_notify_port_valid(sta->eapol_sm, value); break; case WPA_EAPOL_authorized: ieee802_1x_set_sta_authorized(hapd, sta, value); break; case WPA_EAPOL_portControl_Auto: if (sta->eapol_sm) sta->eapol_sm->portControl = Auto; break; case WPA_EAPOL_keyRun: if (sta->eapol_sm) sta->eapol_sm->keyRun = value; break; case WPA_EAPOL_keyAvailable: if (sta->eapol_sm) sta->eapol_sm->eap_if->eapKeyAvailable = value; break; case WPA_EAPOL_keyDone: if (sta->eapol_sm) sta->eapol_sm->keyDone = value; break; case WPA_EAPOL_inc_EapolFramesTx: if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapolFramesTx++; break; } } static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); if (sta == NULL || sta->eapol_sm == NULL) return -1; switch (var) { case WPA_EAPOL_keyRun: return sta->eapol_sm->keyRun; case WPA_EAPOL_keyAvailable: return sta->eapol_sm->eap_if->eapKeyAvailable; default: return -1; } } static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk, size_t *psk_len, int *vlan_id) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; if (vlan_id) *vlan_id = 0; if (psk_len) *psk_len = PMK_LEN; #ifdef CONFIG_SAE if (sta && sta->auth_alg == WLAN_AUTH_SAE) { if (!sta->sae || prev_psk) return NULL; return sta->sae->pmk; } if (sta && wpa_auth_uses_sae(sta->wpa_sm)) { wpa_printf(MSG_DEBUG, "No PSK for STA trying to use SAE with PMKSA caching"); return NULL; } #endif /* CONFIG_SAE */ #ifdef CONFIG_OWE if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta && sta->owe_pmk) { if (psk_len) *psk_len = sta->owe_pmk_len; return sta->owe_pmk; } if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) { struct rsn_pmksa_cache_entry *sa; sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (sa && sa->akmp == WPA_KEY_MGMT_OWE) { if (psk_len) *psk_len = sa->pmk_len; return sa->pmk; } } #endif /* CONFIG_OWE */ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk, vlan_id); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. * logic list (all hostapd_get_psk; all sta->psk) */ if (sta && sta->psk && !psk) { struct hostapd_sta_wpa_psk_short *pos; if (vlan_id) *vlan_id = 0; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { if (pos->is_passphrase) { pbkdf2_sha1(pos->passphrase, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len, 4096, pos->psk, PMK_LEN); pos->is_passphrase = 0; } if (pos->psk == prev_psk) { psk = pos->next ? pos->next->psk : NULL; break; } } } return psk; } static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, size_t *len) { struct hostapd_data *hapd = ctx; const u8 *key; size_t keylen; struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (sta == NULL) { wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA"); return -1; } key = ieee802_1x_get_key(sta->eapol_sm, &keylen); if (key == NULL) { wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p", sta->eapol_sm); return -1; } if (keylen > *len) keylen = *len; os_memcpy(msk, key, keylen); *len = keylen; return 0; } static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len, enum key_flag key_flag) { struct hostapd_data *hapd = ctx; const char *ifname = hapd->conf->iface; if (vlan_id > 0) { ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); if (!ifname) { if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) return -1; ifname = hapd->conf->iface; } } #ifdef CONFIG_TESTING_OPTIONS if (key_flag & KEY_FLAG_MODIFY) { /* We are updating an already installed key. Don't overwrite * the already stored key information with zeros. */ } else if (addr && !is_broadcast_ether_addr(addr)) { struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (sta) { sta->last_tk_alg = alg; sta->last_tk_key_idx = idx; if (key) os_memcpy(sta->last_tk, key, key_len); sta->last_tk_len = key_len; } } else if (alg == WPA_ALG_BIP_CMAC_128 || alg == WPA_ALG_BIP_GMAC_128 || alg == WPA_ALG_BIP_GMAC_256 || alg == WPA_ALG_BIP_CMAC_256) { if (idx == 4 || idx == 5) { hapd->last_igtk_alg = alg; hapd->last_igtk_key_idx = idx; if (key) os_memcpy(hapd->last_igtk, key, key_len); hapd->last_igtk_len = key_len; } else if (idx == 6 || idx == 7) { hapd->last_bigtk_alg = alg; hapd->last_bigtk_key_idx = idx; if (key) os_memcpy(hapd->last_bigtk, key, key_len); hapd->last_bigtk_len = key_len; } } else { hapd->last_gtk_alg = alg; hapd->last_gtk_key_idx = idx; if (key) os_memcpy(hapd->last_gtk, key, key_len); hapd->last_gtk_len = key_len; } #endif /* CONFIG_TESTING_OPTIONS */ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1, NULL, 0, key, key_len, key_flag); } static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, u8 *seq) { struct hostapd_data *hapd = ctx; return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); } int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { struct hostapd_data *hapd = ctx; struct sta_info *sta; u32 flags = 0; #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_eapol_frame_io) { size_t hex_len = 2 * data_len + 1; char *hex = os_malloc(hex_len); if (hex == NULL) return -1; wpa_snprintf_hex(hex, hex_len, data, data_len); wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s", MAC2STR(addr), hex); os_free(hex); return 0; } #endif /* CONFIG_TESTING_OPTIONS */ sta = ap_get_sta(hapd, addr); if (sta) flags = hostapd_sta_flags_to_drv(sta->flags); return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len, encrypt, flags); } static int hostapd_wpa_auth_for_each_sta( void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { struct hostapd_data *hapd = ctx; struct sta_info *sta; for (sta = hapd->sta_list; sta; sta = sta->next) { if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) return 1; } return 0; } struct wpa_auth_iface_iter_data { int (*cb)(struct wpa_authenticator *sm, void *ctx); void *cb_ctx; }; static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_iface_iter_data *data = ctx; size_t i; for (i = 0; i < iface->num_bss; i++) { if (iface->bss[i]->wpa_auth && data->cb(iface->bss[i]->wpa_auth, data->cb_ctx)) return 1; } return 0; } static int hostapd_wpa_auth_for_each_auth( void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), void *cb_ctx) { struct hostapd_data *hapd = ctx; struct wpa_auth_iface_iter_data data; if (hapd->iface->interfaces == NULL || hapd->iface->interfaces->for_each_interface == NULL) return -1; data.cb = cb; data.cb_ctx = cb_ctx; return hapd->iface->interfaces->for_each_interface( hapd->iface->interfaces, wpa_auth_iface_iter, &data); } #ifdef CONFIG_IEEE80211R_AP struct wpa_ft_rrb_rx_later_data { struct dl_list list; u8 addr[ETH_ALEN]; size_t data_len; /* followed by data_len octets of data */ }; static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct wpa_ft_rrb_rx_later_data *data, *n; dl_list_for_each_safe(data, n, &hapd->l2_queue, struct wpa_ft_rrb_rx_later_data, list) { if (hapd->wpa_auth) { wpa_ft_rrb_rx(hapd->wpa_auth, data->addr, (const u8 *) (data + 1), data->data_len); } dl_list_del(&data->list); os_free(data); } } struct wpa_auth_ft_iface_iter_data { struct hostapd_data *src_hapd; const u8 *dst; const u8 *data; size_t data_len; }; static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_ft_iface_iter_data *idata = ctx; struct wpa_ft_rrb_rx_later_data *data; struct hostapd_data *hapd; size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; if (hapd == idata->src_hapd || !hapd->wpa_auth || os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0) continue; wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to locally managed BSS " MACSTR "@%s -> " MACSTR "@%s", MAC2STR(idata->src_hapd->own_addr), idata->src_hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->iface); /* Defer wpa_ft_rrb_rx() until next eloop step as this is * when it would be triggered when reading from a socket. * This avoids * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, * that is calling hapd0:recv handler from within * hapd0:send directly. */ data = os_zalloc(sizeof(*data) + idata->data_len); if (!data) return 1; os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN); os_memcpy(data + 1, idata->data, idata->data_len); data->data_len = idata->data_len; dl_list_add(&hapd->l2_queue, &data->list); if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later, hapd, NULL)) eloop_register_timeout(0, 0, hostapd_wpa_ft_rrb_rx_later, hapd, NULL); return 1; } return 0; } #endif /* CONFIG_IEEE80211R_AP */ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len) { struct hostapd_data *hapd = ctx; struct l2_ethhdr *buf; int ret; #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) { size_t hex_len = 2 * data_len + 1; char *hex = os_malloc(hex_len); if (hex == NULL) return -1; wpa_snprintf_hex(hex, hex_len, data, data_len); wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s", MAC2STR(dst), hex); os_free(hex); return 0; } #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_IEEE80211R_AP if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { int res; struct wpa_auth_ft_iface_iter_data idata; idata.src_hapd = hapd; idata.dst = dst; idata.data = data; idata.data_len = data_len; res = hapd->iface->interfaces->for_each_interface( hapd->iface->interfaces, hostapd_wpa_auth_ft_iter, &idata); if (res == 1) return data_len; } #endif /* CONFIG_IEEE80211R_AP */ if (hapd->l2 == NULL) return -1; buf = os_malloc(sizeof(*buf) + data_len); if (buf == NULL) return -1; os_memcpy(buf->h_dest, dst, ETH_ALEN); os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN); buf->h_proto = host_to_be16(proto); os_memcpy(buf + 1, data, data_len); ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf, sizeof(*buf) + data_len); os_free(buf); return ret; } #ifdef CONFIG_ETH_P_OUI static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd, u8 oui_suffix) { switch (oui_suffix) { #ifdef CONFIG_IEEE80211R_AP case FT_PACKET_R0KH_R1KH_PULL: return hapd->oui_pull; case FT_PACKET_R0KH_R1KH_RESP: return hapd->oui_resp; case FT_PACKET_R0KH_R1KH_PUSH: return hapd->oui_push; case FT_PACKET_R0KH_R1KH_SEQ_REQ: return hapd->oui_sreq; case FT_PACKET_R0KH_R1KH_SEQ_RESP: return hapd->oui_sresp; #endif /* CONFIG_IEEE80211R_AP */ default: return NULL; } } #endif /* CONFIG_ETH_P_OUI */ #ifdef CONFIG_IEEE80211R_AP struct oui_deliver_later_data { struct dl_list list; u8 src_addr[ETH_ALEN]; u8 dst_addr[ETH_ALEN]; size_t data_len; u8 oui_suffix; /* followed by data_len octets of data */ }; static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct oui_deliver_later_data *data, *n; struct eth_p_oui_ctx *oui_ctx; dl_list_for_each_safe(data, n, &hapd->l2_oui_queue, struct oui_deliver_later_data, list) { oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix); wpa_printf(MSG_DEBUG, "RRB(%s): %s src=" MACSTR " dst=" MACSTR " oui_suffix=%u data_len=%u data=%p", hapd->conf->iface, __func__, MAC2STR(data->src_addr), MAC2STR(data->dst_addr), data->oui_suffix, (unsigned int) data->data_len, data); if (hapd->wpa_auth && oui_ctx) { eth_p_oui_deliver(oui_ctx, data->src_addr, data->dst_addr, (const u8 *) (data + 1), data->data_len); } dl_list_del(&data->list); os_free(data); } } struct wpa_auth_oui_iface_iter_data { struct hostapd_data *src_hapd; const u8 *dst_addr; const u8 *data; size_t data_len; u8 oui_suffix; }; static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_oui_iface_iter_data *idata = ctx; struct oui_deliver_later_data *data; struct hostapd_data *hapd, *src_hapd = idata->src_hapd; size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; if (hapd == src_hapd) continue; /* don't deliver back to same interface */ if (!wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) || hapd->conf->ssid.ssid_len != src_hapd->conf->ssid.ssid_len || os_memcmp(hapd->conf->ssid.ssid, src_hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len) != 0 || os_memcmp(hapd->conf->mobility_domain, src_hapd->conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) continue; /* no matching FT SSID/mobility domain */ if (!is_multicast_ether_addr(idata->dst_addr) && os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0) continue; /* destination address does not match */ /* defer eth_p_oui_deliver until next eloop step as this is * when it would be triggerd from reading from sock * This avoids * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, * that is calling hapd0:recv handler from within * hapd0:send directly. */ data = os_zalloc(sizeof(*data) + idata->data_len); if (!data) return 1; wpa_printf(MSG_DEBUG, "RRB(%s): local delivery to %s dst=" MACSTR " oui_suffix=%u data_len=%u data=%p", src_hapd->conf->iface, hapd->conf->iface, MAC2STR(idata->dst_addr), idata->oui_suffix, (unsigned int) idata->data_len, data); os_memcpy(data->src_addr, src_hapd->own_addr, ETH_ALEN); os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN); os_memcpy(data + 1, idata->data, idata->data_len); data->data_len = idata->data_len; data->oui_suffix = idata->oui_suffix; dl_list_add_tail(&hapd->l2_oui_queue, &data->list); if (!eloop_is_timeout_registered(hostapd_oui_deliver_later, hapd, NULL)) eloop_register_timeout(0, 0, hostapd_oui_deliver_later, hapd, NULL); /* If dst_addr is a multicast address, do not return any * non-zero value here. Otherwise, the iteration of * for_each_interface() will be stopped. */ if (!is_multicast_ether_addr(idata->dst_addr)) return 1; } return 0; } #endif /* CONFIG_IEEE80211R_AP */ static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, size_t data_len) { #ifdef CONFIG_ETH_P_OUI struct hostapd_data *hapd = ctx; struct eth_p_oui_ctx *oui_ctx; wpa_printf(MSG_DEBUG, "RRB(%s): send to dst=" MACSTR " oui_suffix=%u data_len=%u", hapd->conf->iface, MAC2STR(dst), oui_suffix, (unsigned int) data_len); #ifdef CONFIG_IEEE80211R_AP if (hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { struct wpa_auth_oui_iface_iter_data idata; int res; idata.src_hapd = hapd; idata.dst_addr = dst; idata.data = data; idata.data_len = data_len; idata.oui_suffix = oui_suffix; res = hapd->iface->interfaces->for_each_interface( hapd->iface->interfaces, hostapd_wpa_auth_oui_iter, &idata); if (res == 1) return data_len; } #endif /* CONFIG_IEEE80211R_AP */ oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix); if (!oui_ctx) return -1; return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len); #else /* CONFIG_ETH_P_OUI */ return -1; #endif /* CONFIG_ETH_P_OUI */ } static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci) { struct hostapd_data *hapd = ctx; return hostapd_drv_channel_info(hapd, ci); } #ifdef CONFIG_PASN static void hostapd_store_ptksa(void *ctx, const u8 *addr,int cipher, u32 life_time, const struct wpa_ptk *ptk) { struct hostapd_data *hapd = ctx; ptksa_cache_add(hapd->ptksa, addr, cipher, life_time, ptk); } static void hostapd_clear_ptksa(void *ctx, const u8 *addr, int cipher) { struct hostapd_data *hapd = ctx; ptksa_cache_flush(hapd->ptksa, addr, cipher); } #endif /* CONFIG_PASN */ static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id) { #ifndef CONFIG_NO_VLAN struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (!sta) return -1; if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) { struct vlan_description vlan_desc; os_memset(&vlan_desc, 0, sizeof(vlan_desc)); vlan_desc.notempty = 1; vlan_desc.untagged = vlan_id; if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file", vlan_id); return -1; } if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) { wpa_printf(MSG_INFO, "Failed to assign VLAN ID %d from wpa_psk_file to " MACSTR, vlan_id, MAC2STR(sta->addr)); return -1; } } else { sta->vlan_id = vlan_id; } wpa_printf(MSG_INFO, "Assigned VLAN ID %d from wpa_psk_file to " MACSTR, vlan_id, MAC2STR(sta->addr)); if ((sta->flags & WLAN_STA_ASSOC) && ap_sta_bind_vlan(hapd, sta) < 0) return -1; #endif /* CONFIG_NO_VLAN */ return 0; } #ifdef CONFIG_OCV static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr, int ap_max_chanwidth, int ap_seg1_idx, int *bandwidth, int *seg1_idx) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, addr); if (!sta) { hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO, "Failed to get STA info to validate received OCI"); return -1; } return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth, seg1_idx); } #endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, const u8 *data, size_t data_len) { struct hostapd_data *hapd = ctx; int res; struct ieee80211_mgmt *m; size_t mlen; struct sta_info *sta; sta = ap_get_sta(hapd, dst); if (sta == NULL || sta->wpa_sm == NULL) return -1; m = os_zalloc(sizeof(*m) + data_len); if (m == NULL) return -1; mlen = ((u8 *) &m->u - (u8 *) m) + data_len; m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); os_memcpy(m->da, dst, ETH_ALEN); os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); os_memcpy(&m->u, data, data_len); res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0, NULL, 0, 0); os_free(m); return res; } static struct wpa_state_machine * hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; int ret; wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR " based on WPA authenticator callback", MAC2STR(sta_addr)); ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT); /* * The expected return values from hostapd_add_sta_node() are * 0: successfully added STA entry * -EOPNOTSUPP: driver or driver wrapper does not support/need this * operations * any other negative value: error in adding the STA entry */ if (ret < 0 && ret != -EOPNOTSUPP) return NULL; sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; if (ret == 0) sta->added_unassoc = 1; sta->ft_over_ds = 1; if (sta->wpa_sm) { sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; } sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { ap_free_sta(hapd, sta); return NULL; } sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; } static int hostapd_wpa_auth_add_sta_ft(void *ctx, const u8 *sta_addr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, sta_addr); if (!sta) return -1; if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && (sta->flags & WLAN_STA_MFP) && ap_sta_is_authorized(sta) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) { /* We could not do this in handle_auth() since there was a * PMF-enabled association for the STA and the new * authentication attempt was not yet fully processed. Now that * we are ready to configure the TK to the driver, * authentication has succeeded and we can clean up the driver * STA entry to avoid issues with any maintained state from the * previous association. */ wpa_printf(MSG_DEBUG, "FT: Remove and re-add driver STA entry after successful FT authentication"); return ap_sta_re_add(hapd, sta); } return 0; } static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr, struct vlan_description *vlan) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, sta_addr); if (!sta || !sta->wpa_sm) return -1; if (vlan->notempty && !hostapd_vlan_valid(hapd->conf->vlan, vlan)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Invalid VLAN %d%s received from FT", vlan->untagged, vlan->tagged[0] ? "+" : ""); return -1; } if (ap_sta_set_vlan(hapd, sta, vlan) < 0) return -1; /* Configure wpa_group for GTK but ignore error due to driver not * knowing this STA. */ ap_sta_bind_vlan(hapd, sta); if (sta->vlan_id) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); return 0; } static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr, struct vlan_description *vlan) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, sta_addr); if (!sta) return -1; if (sta->vlan_desc) *vlan = *sta->vlan_desc; else os_memset(vlan, 0, sizeof(*vlan)); return 0; } static int hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr, const u8 *identity, size_t identity_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, sta_addr); if (!sta) return -1; os_free(sta->identity); sta->identity = NULL; if (sta->eapol_sm) { os_free(sta->eapol_sm->identity); sta->eapol_sm->identity = NULL; sta->eapol_sm->identity_len = 0; } if (!identity_len) return 0; /* sta->identity is NULL terminated */ sta->identity = os_zalloc(identity_len + 1); if (!sta->identity) return -1; os_memcpy(sta->identity, identity, identity_len); if (sta->eapol_sm) { sta->eapol_sm->identity = os_zalloc(identity_len); if (!sta->eapol_sm->identity) return -1; os_memcpy(sta->eapol_sm->identity, identity, identity_len); sta->eapol_sm->identity_len = identity_len; } return 0; } static size_t hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf) { struct hostapd_data *hapd = ctx; struct sta_info *sta; size_t len; char *identity; sta = ap_get_sta(hapd, sta_addr); if (!sta) return 0; *buf = ieee802_1x_get_identity(sta->eapol_sm, &len); if (*buf && len) return len; if (!sta->identity) { *buf = NULL; return 0; } identity = sta->identity; len = os_strlen(identity); *buf = (u8 *) identity; return len; } static int hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr, const u8 *radius_cui, size_t radius_cui_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, sta_addr); if (!sta) return -1; os_free(sta->radius_cui); sta->radius_cui = NULL; if (sta->eapol_sm) { wpabuf_free(sta->eapol_sm->radius_cui); sta->eapol_sm->radius_cui = NULL; } if (!radius_cui) return 0; /* sta->radius_cui is NULL terminated */ sta->radius_cui = os_zalloc(radius_cui_len + 1); if (!sta->radius_cui) return -1; os_memcpy(sta->radius_cui, radius_cui, radius_cui_len); if (sta->eapol_sm) { sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui, radius_cui_len); if (!sta->eapol_sm->radius_cui) return -1; } return 0; } static size_t hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf) { struct hostapd_data *hapd = ctx; struct sta_info *sta; struct wpabuf *b; size_t len; char *radius_cui; sta = ap_get_sta(hapd, sta_addr); if (!sta) return 0; b = ieee802_1x_get_radius_cui(sta->eapol_sm); if (b) { len = wpabuf_len(b); *buf = wpabuf_head(b); return len; } if (!sta->radius_cui) { *buf = NULL; return 0; } radius_cui = sta->radius_cui; len = os_strlen(radius_cui); *buf = (u8 *) radius_cui; return len; } static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr, int session_timeout) { struct hostapd_data *hapd = ctx; struct sta_info *sta; sta = ap_get_sta(hapd, sta_addr); if (!sta) return; if (session_timeout) { os_get_reltime(&sta->session_timeout); sta->session_timeout.sec += session_timeout; sta->session_timeout_set = 1; ap_sta_session_timeout(hapd, sta, session_timeout); } else { sta->session_timeout_set = 0; ap_sta_no_session_timeout(hapd, sta); } } static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; struct os_reltime now, remaining; sta = ap_get_sta(hapd, sta_addr); if (!sta || !sta->session_timeout_set) return 0; os_get_reltime(&now); if (os_reltime_before(&sta->session_timeout, &now)) { /* already expired, return >0 as timeout was set */ return 1; } os_reltime_sub(&sta->session_timeout, &now, &remaining); return (remaining.sec > 0) ? remaining.sec : 1; } static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; struct l2_ethhdr *ethhdr; if (len < sizeof(*ethhdr)) return; ethhdr = (struct l2_ethhdr *) buf; wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); if (!is_multicast_ether_addr(ethhdr->h_dest) && os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0) return; wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), len - sizeof(*ethhdr)); } static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, const u8 *dst_addr, u8 oui_suffix, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); if (!is_multicast_ether_addr(dst_addr) && os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0) return; wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf, len); } static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { struct hostapd_data *hapd = ctx; return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); } static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, const char *ft_iface) { hapd->oui_pull = eth_p_oui_register(hapd, ft_iface, FT_PACKET_R0KH_R1KH_PULL, hostapd_rrb_oui_receive, hapd); if (!hapd->oui_pull) return -1; hapd->oui_resp = eth_p_oui_register(hapd, ft_iface, FT_PACKET_R0KH_R1KH_RESP, hostapd_rrb_oui_receive, hapd); if (!hapd->oui_resp) return -1; hapd->oui_push = eth_p_oui_register(hapd, ft_iface, FT_PACKET_R0KH_R1KH_PUSH, hostapd_rrb_oui_receive, hapd); if (!hapd->oui_push) return -1; hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface, FT_PACKET_R0KH_R1KH_SEQ_REQ, hostapd_rrb_oui_receive, hapd); if (!hapd->oui_sreq) return -1; hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface, FT_PACKET_R0KH_R1KH_SEQ_RESP, hostapd_rrb_oui_receive, hapd); if (!hapd->oui_sresp) return -1; return 0; } static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) { eth_p_oui_unregister(hapd->oui_pull); hapd->oui_pull = NULL; eth_p_oui_unregister(hapd->oui_resp); hapd->oui_resp = NULL; eth_p_oui_unregister(hapd->oui_push); hapd->oui_push = NULL; eth_p_oui_unregister(hapd->oui_sreq); hapd->oui_sreq = NULL; eth_p_oui_unregister(hapd->oui_sresp); hapd->oui_sresp = NULL; } #endif /* CONFIG_IEEE80211R_AP */ int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; static const struct wpa_auth_callbacks cb = { .logger = hostapd_wpa_auth_logger, .disconnect = hostapd_wpa_auth_disconnect, .mic_failure_report = hostapd_wpa_auth_mic_failure_report, .psk_failure_report = hostapd_wpa_auth_psk_failure_report, .set_eapol = hostapd_wpa_auth_set_eapol, .get_eapol = hostapd_wpa_auth_get_eapol, .get_psk = hostapd_wpa_auth_get_psk, .get_msk = hostapd_wpa_auth_get_msk, .set_key = hostapd_wpa_auth_set_key, .get_seqnum = hostapd_wpa_auth_get_seqnum, .send_eapol = hostapd_wpa_auth_send_eapol, .for_each_sta = hostapd_wpa_auth_for_each_sta, .for_each_auth = hostapd_wpa_auth_for_each_auth, .send_ether = hostapd_wpa_auth_send_ether, .send_oui = hostapd_wpa_auth_send_oui, .channel_info = hostapd_channel_info, .update_vlan = hostapd_wpa_auth_update_vlan, #ifdef CONFIG_PASN .store_ptksa = hostapd_store_ptksa, .clear_ptksa = hostapd_clear_ptksa, #endif /* CONFIG_PASN */ #ifdef CONFIG_OCV .get_sta_tx_params = hostapd_get_sta_tx_params, #endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP .send_ft_action = hostapd_wpa_auth_send_ft_action, .add_sta = hostapd_wpa_auth_add_sta, .add_sta_ft = hostapd_wpa_auth_add_sta_ft, .add_tspec = hostapd_wpa_auth_add_tspec, .set_vlan = hostapd_wpa_auth_set_vlan, .get_vlan = hostapd_wpa_auth_get_vlan, .set_identity = hostapd_wpa_auth_set_identity, .get_identity = hostapd_wpa_auth_get_identity, .set_radius_cui = hostapd_wpa_auth_set_radius_cui, .get_radius_cui = hostapd_wpa_auth_get_radius_cui, .set_session_timeout = hostapd_wpa_auth_set_session_timeout, .get_session_timeout = hostapd_wpa_auth_get_session_timeout, #endif /* CONFIG_IEEE80211R_AP */ }; const u8 *wpa_ie; size_t wpa_ie_len; hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); _conf.msg_ctx = hapd->msg_ctx; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) _conf.tx_status = 1; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) _conf.ap_mlme = 1; if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) && (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER || (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK && !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) { wpa_msg(hapd->msg_ctx, MSG_INFO, "Disable PTK0 rekey support - replaced with disconnect"); _conf.wpa_deny_ptk0_rekey = 1; } if (_conf.extended_key_id && (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported"); else _conf.extended_key_id = 0; if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) _conf.beacon_prot = 0; #ifdef CONFIG_OCV if (!(hapd->iface->drv_flags2 & (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV))) _conf.ocv = 0; #endif /* CONFIG_OCV */ _conf.secure_ltf = !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF); _conf.secure_rtt = !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT); _conf.prot_range_neg = !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG); hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); return -1; } if (hostapd_set_privacy(hapd, 1)) { wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " "for interface %s", hapd->conf->iface); return -1; } wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " "the kernel driver."); return -1; } if (rsn_preauth_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Initialization of RSN " "pre-authentication failed."); return -1; } - hapd->ptksa = ptksa_cache_init(); + if (!hapd->ptksa) + hapd->ptksa = ptksa_cache_init(); if (!hapd->ptksa) { wpa_printf(MSG_ERROR, "Failed to allocate PTKSA cache"); return -1; } #ifdef CONFIG_IEEE80211R_AP if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { const char *ft_iface; ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface; hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); if (!hapd->l2) { wpa_printf(MSG_ERROR, "Failed to open l2_packet " "interface"); return -1; } if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) { wpa_printf(MSG_ERROR, "Failed to open ETH_P_OUI interface"); return -1; } } #endif /* CONFIG_IEEE80211R_AP */ return 0; } void hostapd_reconfig_wpa(struct hostapd_data *hapd) { struct wpa_auth_config wpa_auth_conf; hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf); wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); } void hostapd_deinit_wpa(struct hostapd_data *hapd) { ieee80211_tkip_countermeasures_deinit(hapd); ptksa_cache_deinit(hapd->ptksa); hapd->ptksa = NULL; rsn_preauth_iface_deinit(hapd); if (hapd->wpa_auth) { wpa_deinit(hapd->wpa_auth); hapd->wpa_auth = NULL; if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) { wpa_printf(MSG_DEBUG, "Could not disable " "PrivacyInvoked for interface %s", hapd->conf->iface); } if (hapd->drv_priv && hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { wpa_printf(MSG_DEBUG, "Could not remove generic " "information element from interface %s", hapd->conf->iface); } } ieee802_1x_deinit(hapd); #ifdef CONFIG_IEEE80211R_AP eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX); hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */ eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX); hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */ l2_packet_deinit(hapd->l2); hapd->l2 = NULL; hostapd_wpa_unregister_ft_oui(hapd); #endif /* CONFIG_IEEE80211R_AP */ } diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index e97dbf996749..9f22e39a2e6a 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1,2272 +1,2279 @@ /* * hostapd / WPS integration * Copyright (c) 2008-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "wps/wps.h" #include "wps/wps_defs.h" #include "wps/wps_dev_attr.h" #include "wps/wps_attr_parse.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "beacon.h" #include "sta_info.h" #include "wps_hostapd.h" #ifdef CONFIG_WPS_UPNP #include "wps/wps_upnp.h" static int hostapd_wps_upnp_init(struct hostapd_data *hapd, struct wps_context *wps); static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); #endif /* CONFIG_WPS_UPNP */ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); static void hostapd_wps_nfc_clear(struct wps_context *wps); struct wps_for_each_data { int (*func)(struct hostapd_data *h, void *ctx); void *ctx; struct hostapd_data *calling_hapd; }; static int wps_for_each(struct hostapd_iface *iface, void *ctx) { struct wps_for_each_data *data = ctx; size_t j; if (iface == NULL) return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; int ret; if (hapd != data->calling_hapd && (hapd->conf->wps_independent || data->calling_hapd->conf->wps_independent)) continue; ret = data->func(hapd, data->ctx); if (ret) return ret; } return 0; } static int hostapd_wps_for_each(struct hostapd_data *hapd, int (*func)(struct hostapd_data *h, void *ctx), void *ctx) { struct hostapd_iface *iface = hapd->iface; struct wps_for_each_data data; data.func = func; data.ctx = ctx; data.calling_hapd = hapd; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return wps_for_each(iface, &data); return iface->interfaces->for_each_interface(iface->interfaces, wps_for_each, &data); } static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { struct hostapd_data *hapd = ctx; struct hostapd_wpa_psk *p; struct hostapd_ssid *ssid = &hapd->conf->ssid; if (is_zero_ether_addr(p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, MAC2STR(mac_addr)); } else { wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " MACSTR " P2P Device Addr " MACSTR, MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); if (psk_len != PMK_LEN) { wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", (unsigned long) psk_len); return -1; } /* Add the new PSK to runtime PSK list */ p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; os_memcpy(p->addr, mac_addr, ETH_ALEN); os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); os_memcpy(p->psk, psk, PMK_LEN); p->wps = 1; if (hapd->new_psk_cb) { hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, psk, psk_len); } p->next = ssid->wpa_psk; ssid->wpa_psk = p; if (ssid->wpa_psk_file) { FILE *f; char hex[PMK_LEN * 2 + 1]; /* Add the new PSK to PSK list file */ f = fopen(ssid->wpa_psk_file, "a"); if (!f) { wpa_printf(MSG_DEBUG, "Failed to add the PSK to '%s'", ssid->wpa_psk_file); return -1; } wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); fprintf(f, "wps=1 " MACSTR " %s\n", MAC2STR(mac_addr), hex); fclose(f); } return 0; } static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, struct wpabuf *probe_resp_ie) { struct hostapd_data *hapd = ctx; wpabuf_free(hapd->wps_beacon_ie); hapd->wps_beacon_ie = beacon_ie; wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = probe_resp_ie; if (hapd->beacon_set_done) ieee802_11_set_beacon(hapd); return hostapd_set_ap_wps_ie(hapd); } static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev) { struct hostapd_data *hapd = ctx; char uuid[40], txt[400]; int len; char devtype[WPS_DEV_TYPE_BUFSIZE]; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED "%s " MACSTR " [%s|%s|%s|%s|%s|%s]", uuid, MAC2STR(dev->mac_addr), dev->device_name, dev->manufacturer, dev->model_name, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); if (!os_snprintf_error(sizeof(txt), len)) wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt); if (hapd->conf->wps_pin_requests) { FILE *f; struct os_time t; f = fopen(hapd->conf->wps_pin_requests, "a"); if (f == NULL) return; os_get_time(&t); fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" "\t%s\n", t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, dev->manufacturer, dev->model_name, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); fclose(f); } } struct wps_stop_reg_data { struct hostapd_data *current_hapd; const u8 *uuid_e; const u8 *dev_pw; size_t dev_pw_len; }; static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) { struct wps_stop_reg_data *data = ctx; if (hapd != data->current_hapd && hapd->wps != NULL) wps_registrar_complete(hapd->wps->registrar, data->uuid_e, data->dev_pw, data->dev_pw_len); return 0; } static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len) { struct hostapd_data *hapd = ctx; char uuid[40]; struct wps_stop_reg_data data; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", MAC2STR(mac_addr), uuid); if (hapd->wps_reg_success_cb) hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx, mac_addr, uuid_e); data.current_hapd = hapd; data.uuid_e = uuid_e; data.dev_pw = dev_pw; data.dev_pw_len = dev_pw_len; hostapd_wps_for_each(hapd, wps_stop_registrar, &data); } static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr, const u8 *uuid_e, const u8 *pri_dev_type, u16 config_methods, u16 dev_password_id, u8 request_type, const char *dev_name) { struct hostapd_data *hapd = ctx; char uuid[40]; char devtype[WPS_DEV_TYPE_BUFSIZE]; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; if (dev_name == NULL) dev_name = ""; wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR " %s %s 0x%x %u %u [%s]", MAC2STR(addr), uuid, wps_dev_type_bin2str(pri_dev_type, devtype, sizeof(devtype)), config_methods, dev_password_id, request_type, dev_name); } static int hostapd_wps_lookup_pskfile_cb(void *ctx, const u8 *mac_addr, const u8 **psk) { const struct hostapd_data *hapd = ctx; const struct hostapd_wpa_psk *wpa_psk; const u8 *any_psk = NULL; const u8 *dev_psk = NULL; for (wpa_psk = hapd->conf->ssid.wpa_psk; wpa_psk; wpa_psk = wpa_psk->next) { if (!wpa_psk->wps) continue; if (!any_psk && is_zero_ether_addr(wpa_psk->addr)) any_psk = wpa_psk->psk; if (mac_addr && !dev_psk && os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) { dev_psk = wpa_psk->psk; break; } } if (dev_psk) { *psk = dev_psk; } else if (any_psk) { *psk = any_psk; } else { *psk = NULL; wpa_printf(MSG_DEBUG, "WPS: No appropriate PSK in wpa_psk_file"); return 0; } return 1; } static void wps_reload_config(void *eloop_data, void *user_ctx) { struct hostapd_iface *iface = eloop_data; wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); if (iface->interfaces == NULL || iface->interfaces->reload_config(iface) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " "configuration"); } } void hostapd_wps_eap_completed(struct hostapd_data *hapd) { /* * Reduce race condition of the station trying to reconnect immediately * after AP reconfiguration through WPS by rescheduling the reload * timeout to happen after EAP completion rather than the originally * scheduled 100 ms after new configuration became known. */ if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) == 1) wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload"); } static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, size_t attr_len) { size_t blen = attr_len * 2 + 1; char *buf = os_malloc(blen); if (buf) { wpa_snprintf_hex(buf, blen, attr, attr_len); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS "%s", buf); os_free(buf); } } static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, const struct wps_credential *cred) { struct hostapd_bss_config *bss = hapd->conf; wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); bss->wps_state = 2; if (cred->ssid_len <= SSID_MAX_LEN) { os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); bss->ssid.ssid_len = cred->ssid_len; bss->ssid.ssid_set = 1; } #ifdef CONFIG_NO_TKIP if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) bss->wpa = 2; else bss->wpa = 0; #else /* CONFIG_NO_TKIP */ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) bss->wpa = 3; else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) bss->wpa = 2; else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) bss->wpa = 1; else bss->wpa = 0; #endif /* CONFIG_NO_TKIP */ if (bss->wpa) { if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = 0; if (cred->encr_type & WPS_ENCR_AES) { if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) bss->wpa_pairwise |= WPA_CIPHER_GCMP; else bss->wpa_pairwise |= WPA_CIPHER_CCMP; } #ifndef CONFIG_NO_TKIP if (cred->encr_type & WPS_ENCR_TKIP) bss->wpa_pairwise |= WPA_CIPHER_TKIP; #endif /* CONFIG_NO_TKIP */ bss->rsn_pairwise = bss->wpa_pairwise; bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, bss->rsn_pairwise); if (hapd->conf->wps_cred_add_sae && (cred->auth_type & WPS_AUTH_WPA2PSK) && cred->key_len != 2 * PMK_LEN) { bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE; if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) bss->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; bss->sae_require_mfp = 1; } if (cred->key_len >= 8 && cred->key_len < 64) { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); if (bss->ssid.wpa_passphrase) os_memcpy(bss->ssid.wpa_passphrase, cred->key, cred->key_len); hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); } else if (cred->key_len == 64) { hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk && hexstr2bin((const char *) cred->key, bss->ssid.wpa_psk->psk, PMK_LEN) == 0) { bss->ssid.wpa_psk->group = 1; os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = NULL; } } bss->auth_algs = 1; } else { /* * WPS 2.0 does not allow WEP to be configured, so no need to * process that option here either. */ bss->auth_algs = 1; } /* Schedule configuration reload after short period of time to allow * EAP-WSC to be finished. */ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, NULL); return 0; } static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) { const struct wps_credential *cred = ctx; FILE *oconf, *nconf; size_t len, i; char *tmp_fname; char buf[1024]; int multi_bss; int wpa; int pmf_changed = 0; if (hapd->wps == NULL) return 0; wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", cred->cred_attr, cred->cred_attr_len); wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", cred->auth_type); wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", cred->key, cred->key_len); wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(cred->mac_addr)); if ((hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len); } else if (hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) { struct wpabuf *attr; attr = wpabuf_alloc(200); if (attr && wps_build_credential_wrap(attr, cred) == 0) hapd_new_ap_event(hapd, wpabuf_head_u8(attr), wpabuf_len(attr)); wpabuf_free(attr); } else wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); if (hapd->conf->wps_cred_processing == 1) return 0; os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); hapd->wps->ssid_len = cred->ssid_len; hapd->wps->encr_types = cred->encr_type; hapd->wps->encr_types_rsn = cred->encr_type; hapd->wps->encr_types_wpa = cred->encr_type; hapd->wps->auth_types = cred->auth_type; hapd->wps->ap_encr_type = cred->encr_type; hapd->wps->ap_auth_type = cred->auth_type; if (cred->key_len == 0) { os_free(hapd->wps->network_key); hapd->wps->network_key = NULL; hapd->wps->network_key_len = 0; } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) && (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) { wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2", (unsigned long) cred->key_len); return -1; } else { if (hapd->wps->network_key == NULL || hapd->wps->network_key_len < cred->key_len) { hapd->wps->network_key_len = 0; os_free(hapd->wps->network_key); hapd->wps->network_key = os_malloc(cred->key_len); if (hapd->wps->network_key == NULL) return -1; } hapd->wps->network_key_len = cred->key_len; os_memcpy(hapd->wps->network_key, cred->key, cred->key_len); } hapd->wps->wps_state = WPS_STATE_CONFIGURED; if (hapd->iface->config_fname == NULL) return hapd_wps_reconfig_in_memory(hapd, cred); len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) return -1; os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname); oconf = fopen(hapd->iface->config_fname, "r"); if (oconf == NULL) { wpa_printf(MSG_WARNING, "WPS: Could not open current " "configuration file"); os_free(tmp_fname); return -1; } nconf = fopen(tmp_fname, "w"); if (nconf == NULL) { wpa_printf(MSG_WARNING, "WPS: Could not write updated " "configuration file"); os_free(tmp_fname); fclose(oconf); return -1; } fprintf(nconf, "# WPS configuration - START\n"); fprintf(nconf, "wps_state=2\n"); if (is_hex(cred->ssid, cred->ssid_len)) { fprintf(nconf, "ssid2="); for (i = 0; i < cred->ssid_len; i++) fprintf(nconf, "%02x", cred->ssid[i]); fprintf(nconf, "\n"); } else { fprintf(nconf, "ssid="); for (i = 0; i < cred->ssid_len; i++) fputc(cred->ssid[i], nconf); fprintf(nconf, "\n"); } #ifdef CONFIG_NO_TKIP if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) wpa = 2; else wpa = 0; #else /* CONFIG_NO_TKIP */ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) wpa = 3; else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) wpa = 2; else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) wpa = 1; else wpa = 0; #endif /* CONFIG_NO_TKIP */ if (wpa) { char *prefix; int sae = 0; fprintf(nconf, "wpa=%d\n", wpa); fprintf(nconf, "wpa_key_mgmt="); prefix = ""; if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) { fprintf(nconf, "WPA-EAP"); prefix = " "; } if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { fprintf(nconf, "%sWPA-PSK", prefix); prefix = " "; } if (hapd->conf->wps_cred_add_sae && (cred->auth_type & WPS_AUTH_WPA2PSK) && cred->key_len != 2 * PMK_LEN) { fprintf(nconf, "%sSAE", prefix); sae = 1; } fprintf(nconf, "\n"); if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { fprintf(nconf, "ieee80211w=%d\n", MGMT_FRAME_PROTECTION_OPTIONAL); pmf_changed = 1; } if (sae) fprintf(nconf, "sae_require_mfp=1\n"); fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) fprintf(nconf, "GCMP"); else fprintf(nconf, "CCMP"); prefix = " "; } #ifndef CONFIG_NO_TKIP if (cred->encr_type & WPS_ENCR_TKIP) { fprintf(nconf, "%sTKIP", prefix); } #endif /* CONFIG_NO_TKIP */ fprintf(nconf, "\n"); if (cred->key_len >= 8 && cred->key_len < 64) { fprintf(nconf, "wpa_passphrase="); for (i = 0; i < cred->key_len; i++) fputc(cred->key[i], nconf); fprintf(nconf, "\n"); } else if (cred->key_len == 64) { fprintf(nconf, "wpa_psk="); for (i = 0; i < cred->key_len; i++) fputc(cred->key[i], nconf); fprintf(nconf, "\n"); } else { wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu " "for WPA/WPA2", (unsigned long) cred->key_len); } fprintf(nconf, "auth_algs=1\n"); } else { /* * WPS 2.0 does not allow WEP to be configured, so no need to * process that option here either. */ fprintf(nconf, "auth_algs=1\n"); } fprintf(nconf, "# WPS configuration - END\n"); multi_bss = 0; while (fgets(buf, sizeof(buf), oconf)) { if (os_strncmp(buf, "bss=", 4) == 0) multi_bss = 1; if (!multi_bss && (str_starts(buf, "ssid=") || str_starts(buf, "ssid2=") || str_starts(buf, "auth_algs=") || #ifdef CONFIG_WEP str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || #endif /* CONFIG_WEP */ str_starts(buf, "wps_state=") || (pmf_changed && str_starts(buf, "ieee80211w=")) || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || str_starts(buf, "wpa_pairwise=") || str_starts(buf, "rsn_pairwise=") || str_starts(buf, "wpa_key_mgmt=") || str_starts(buf, "wpa_passphrase="))) { fprintf(nconf, "#WPS# %s", buf); } else fprintf(nconf, "%s", buf); } fclose(nconf); fclose(oconf); if (rename(tmp_fname, hapd->iface->config_fname) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated " "configuration file: %s", strerror(errno)); os_free(tmp_fname); return -1; } os_free(tmp_fname); /* Schedule configuration reload after short period of time to allow * EAP-WSC to be finished. */ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, NULL); wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); return 0; } static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) { struct hostapd_data *hapd = ctx; return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred); } static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = eloop_data; if (hapd->conf->ap_setup_locked) return; if (hapd->ap_pin_failures_consecutive >= 10) return; wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); hapd->wps->ap_setup_locked = 0; wps_registrar_update_ie(hapd->wps->registrar); } static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) { struct wps_event_pwd_auth_fail *data = ctx; if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) return 0; /* * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup * for some time if this happens multiple times to slow down brute * force attacks. */ hapd->ap_pin_failures++; hapd->ap_pin_failures_consecutive++; wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " "(%u consecutive)", hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); if (hapd->ap_pin_failures < 3) return 0; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); hapd->wps->ap_setup_locked = 1; wps_registrar_update_ie(hapd->wps->registrar); if (!hapd->conf->ap_setup_locked && hapd->ap_pin_failures_consecutive >= 10) { /* * In indefinite lockdown - disable automatic AP PIN * reenablement. */ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); } else if (!hapd->conf->ap_setup_locked) { if (hapd->ap_pin_lockout_time == 0) hapd->ap_pin_lockout_time = 60; else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && (hapd->ap_pin_failures % 3) == 0) hapd->ap_pin_lockout_time *= 2; wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds", hapd->ap_pin_lockout_time); eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); eloop_register_timeout(hapd->ap_pin_lockout_time, 0, hostapd_wps_reenable_ap_pin, hapd, NULL); } return 0; } static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, struct wps_event_pwd_auth_fail *data) { /* Update WPS Status - Authentication Failure */ wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); hapd->wps_stats.status = WPS_STATUS_FAILURE; hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE; os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); } static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) { if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) return 0; if (hapd->ap_pin_failures_consecutive == 0) return 0; wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " "- total validation failures %u (%u consecutive)", hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); hapd->ap_pin_failures_consecutive = 0; return 0; } static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) { hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL); } static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) { /* Update WPS Status - PBC Overlap */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; } static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) { /* Update WPS PBC Status:PBC Timeout */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; } static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) { /* Update WPS PBC status - Active */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; } static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) { /* Update WPS PBC status - Active */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; } static void hostapd_wps_event_success(struct hostapd_data *hapd, struct wps_event_success *success) { /* Update WPS status - Success */ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; hapd->wps_stats.status = WPS_STATUS_SUCCESS; os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); } static void hostapd_wps_event_fail(struct hostapd_data *hapd, struct wps_event_fail *fail) { /* Update WPS status - Failure */ hapd->wps_stats.status = WPS_STATUS_FAILURE; os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); hapd->wps_stats.failure_reason = fail->error_indication; if (fail->error_indication > 0 && fail->error_indication < NUM_WPS_EI_VALUES) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } } static void hostapd_wps_event_cb(void *ctx, enum wps_event event, union wps_event_data *data) { struct hostapd_data *hapd = ctx; switch (event) { case WPS_EV_M2D: wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); break; case WPS_EV_FAIL: hostapd_wps_event_fail(hapd, &data->fail); break; case WPS_EV_SUCCESS: hostapd_wps_event_success(hapd, &data->success); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); break; case WPS_EV_PWD_AUTH_FAIL: hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); break; case WPS_EV_PBC_OVERLAP: hostapd_wps_event_pbc_overlap(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); break; case WPS_EV_PBC_TIMEOUT: hostapd_wps_event_pbc_timeout(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); break; case WPS_EV_PBC_ACTIVE: hostapd_wps_event_pbc_active(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); break; case WPS_EV_PBC_DISABLE: hostapd_wps_event_pbc_disable(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); break; case WPS_EV_ER_AP_ADD: break; case WPS_EV_ER_AP_REMOVE: break; case WPS_EV_ER_ENROLLEE_ADD: break; case WPS_EV_ER_ENROLLEE_REMOVE: break; case WPS_EV_ER_AP_SETTINGS: break; case WPS_EV_ER_SET_SELECTED_REGISTRAR: break; case WPS_EV_AP_PIN_SUCCESS: hostapd_wps_ap_pin_success(hapd); break; } if (hapd->wps_event_cb) hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data); } static int hostapd_wps_rf_band_cb(void *ctx) { struct hostapd_data *hapd = ctx; return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? WPS_RF_50GHZ : hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) { wpabuf_free(hapd->wps_beacon_ie); hapd->wps_beacon_ie = NULL; wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; if (deinit_only) { if (hapd->drv_priv) hostapd_reset_ap_wps_ie(hapd); return; } hostapd_set_ap_wps_ie(hapd); } static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) { const u8 **uuid = ctx; size_t j; if (iface == NULL) return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; if (hapd->wps && !hapd->conf->wps_independent && !is_nil_uuid(hapd->wps->uuid)) { *uuid = hapd->wps->uuid; return 1; } } return 0; } static const u8 * get_own_uuid(struct hostapd_iface *iface) { const u8 *uuid; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return NULL; uuid = NULL; iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, &uuid); return uuid; } static int count_interface_cb(struct hostapd_iface *iface, void *ctx) { int *count= ctx; (*count)++; return 0; } static int interface_count(struct hostapd_iface *iface) { int count = 0; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return 0; iface->interfaces->for_each_interface(iface->interfaces, count_interface_cb, &count); return count; } static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, struct wps_context *wps) { int i; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { wpabuf_free(wps->dev.vendor_ext[i]); wps->dev.vendor_ext[i] = NULL; if (hapd->conf->wps_vendor_ext[i] == NULL) continue; wps->dev.vendor_ext[i] = wpabuf_dup(hapd->conf->wps_vendor_ext[i]); if (wps->dev.vendor_ext[i] == NULL) { while (--i >= 0) wpabuf_free(wps->dev.vendor_ext[i]); return -1; } } return 0; } static int hostapd_wps_set_application_ext(struct hostapd_data *hapd, struct wps_context *wps) { wpabuf_free(wps->dev.application_ext); if (!hapd->conf->wps_application_ext) { wps->dev.application_ext = NULL; return 0; } wps->dev.application_ext = wpabuf_dup(hapd->conf->wps_application_ext); return wps->dev.application_ext ? 0 : -1; } static void hostapd_free_wps(struct wps_context *wps) { int i; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) wpabuf_free(wps->dev.vendor_ext[i]); wps_device_data_free(&wps->dev); os_free(wps->network_key); hostapd_wps_nfc_clear(wps); wpabuf_free(wps->dh_pubkey); wpabuf_free(wps->dh_privkey); os_free(wps); } int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf) { struct wps_context *wps; struct wps_registrar_config cfg; u8 *multi_ap_netw_key = NULL; if (conf->wps_state == 0) { hostapd_wps_clear_ies(hapd, 0); return 0; } wps = os_zalloc(sizeof(*wps)); if (wps == NULL) return -1; wps->cred_cb = hostapd_wps_cred_cb; wps->event_cb = hostapd_wps_event_cb; wps->rf_band_cb = hostapd_wps_rf_band_cb; wps->cb_ctx = hapd; os_memset(&cfg, 0, sizeof(cfg)); wps->wps_state = hapd->conf->wps_state; wps->ap_setup_locked = hapd->conf->ap_setup_locked; if (is_nil_uuid(hapd->conf->uuid)) { const u8 *uuid; uuid = get_own_uuid(hapd->iface); if (uuid && !conf->wps_independent) { os_memcpy(wps->uuid, uuid, UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " "interface", wps->uuid, UUID_LEN); } else { uuid_gen_mac_addr(hapd->own_addr, wps->uuid); wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " "address", wps->uuid, UUID_LEN); } } else { os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID", wps->uuid, UUID_LEN); } wps->ssid_len = hapd->conf->ssid.ssid_len; os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); wps->ap = 1; os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); wps->dev.device_name = hapd->conf->device_name ? os_strdup(hapd->conf->device_name) : NULL; wps->dev.manufacturer = hapd->conf->manufacturer ? os_strdup(hapd->conf->manufacturer) : NULL; wps->dev.model_name = hapd->conf->model_name ? os_strdup(hapd->conf->model_name) : NULL; wps->dev.model_number = hapd->conf->model_number ? os_strdup(hapd->conf->model_number) : NULL; wps->dev.serial_number = hapd->conf->serial_number ? os_strdup(hapd->conf->serial_number) : NULL; wps->config_methods = wps_config_methods_str2bin(hapd->conf->config_methods); if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { wpa_printf(MSG_INFO, "WPS: Converting display to " "virtual_display for WPS 2.0 compliance"); wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY; } if ((wps->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { wpa_printf(MSG_INFO, "WPS: Converting push_button to " "virtual_push_button for WPS 2.0 compliance"); wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; } os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, WPS_DEV_TYPE_LEN); if (hostapd_wps_set_vendor_ext(hapd, wps) < 0 || hostapd_wps_set_application_ext(hapd, wps) < 0) goto fail; wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); if (conf->wps_rf_bands) { wps->dev.rf_bands = conf->wps_rf_bands; } else { wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? WPS_RF_50GHZ : hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } if (conf->wpa & WPA_PROTO_RSN) { if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) wps->auth_types |= WPS_AUTH_WPA2PSK; if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) + wps->auth_types |= WPS_AUTH_WPA2PSK; if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)) { wps->encr_types |= WPS_ENCR_AES; wps->encr_types_rsn |= WPS_ENCR_AES; } if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { #ifdef CONFIG_NO_TKIP wpa_printf(MSG_INFO, "WPS: TKIP not supported"); goto fail; #else /* CONFIG_NO_TKIP */ wps->encr_types |= WPS_ENCR_TKIP; wps->encr_types_rsn |= WPS_ENCR_TKIP; #endif /* CONFIG_NO_TKIP */ } } if (conf->wpa & WPA_PROTO_WPA) { #ifdef CONFIG_NO_TKIP if (!(conf->wpa & WPA_PROTO_RSN)) { wpa_printf(MSG_INFO, "WPS: WPA(v1) not supported"); goto fail; } conf->wpa &= ~WPA_PROTO_WPA; #else /* CONFIG_NO_TKIP */ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) wps->auth_types |= WPS_AUTH_WPAPSK; if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA; if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { wps->encr_types |= WPS_ENCR_AES; wps->encr_types_wpa |= WPS_ENCR_AES; } if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; wps->encr_types_wpa |= WPS_ENCR_TKIP; } #endif /* CONFIG_NO_TKIP */ } if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { wps->encr_types |= WPS_ENCR_NONE; wps->auth_types |= WPS_AUTH_OPEN; } if (conf->ssid.wpa_psk_file) { /* Use per-device PSKs */ } else if (conf->ssid.wpa_passphrase) { wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); } else if (conf->ssid.wpa_psk) { wps->network_key = os_malloc(2 * PMK_LEN + 1); if (wps->network_key == NULL) goto fail; wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, conf->ssid.wpa_psk->psk, PMK_LEN); wps->network_key_len = 2 * PMK_LEN; #ifdef CONFIG_WEP } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { wps->network_key = os_malloc(conf->ssid.wep.len[0]); if (wps->network_key == NULL) goto fail; os_memcpy(wps->network_key, conf->ssid.wep.key[0], conf->ssid.wep.len[0]); wps->network_key_len = conf->ssid.wep.len[0]; #endif /* CONFIG_WEP */ } if (conf->ssid.wpa_psk) { os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN); wps->psk_set = 1; } wps->ap_auth_type = wps->auth_types; wps->ap_encr_type = wps->encr_types; if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { /* Override parameters to enable security by default */ #ifdef CONFIG_NO_TKIP wps->auth_types = WPS_AUTH_WPA2PSK; wps->encr_types = WPS_ENCR_AES; wps->encr_types_rsn = WPS_ENCR_AES; wps->encr_types_wpa = WPS_ENCR_AES; #else /* CONFIG_NO_TKIP */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP; wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; #endif /* CONFIG_NO_TKIP */ } if ((hapd->conf->multi_ap & FRONTHAUL_BSS) && hapd->conf->multi_ap_backhaul_ssid.ssid_len) { cfg.multi_ap_backhaul_ssid_len = hapd->conf->multi_ap_backhaul_ssid.ssid_len; cfg.multi_ap_backhaul_ssid = hapd->conf->multi_ap_backhaul_ssid.ssid; if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { cfg.multi_ap_backhaul_network_key = (const u8 *) conf->multi_ap_backhaul_ssid.wpa_passphrase; cfg.multi_ap_backhaul_network_key_len = os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1); if (!multi_ap_netw_key) goto fail; wpa_snprintf_hex((char *) multi_ap_netw_key, 2 * PMK_LEN + 1, conf->multi_ap_backhaul_ssid.wpa_psk->psk, PMK_LEN); cfg.multi_ap_backhaul_network_key = multi_ap_netw_key; cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN; } } wps->ap_settings = conf->ap_settings; wps->ap_settings_len = conf->ap_settings_len; cfg.new_psk_cb = hostapd_wps_new_psk_cb; cfg.set_ie_cb = hostapd_wps_set_ie_cb; cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; cfg.reg_success_cb = hostapd_wps_reg_success_cb; cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb; cfg.lookup_pskfile_cb = hostapd_wps_lookup_pskfile_cb; cfg.cb_ctx = hapd; cfg.skip_cred_build = conf->skip_cred_build; cfg.extra_cred = conf->extra_cred; cfg.extra_cred_len = conf->extra_cred_len; cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && conf->skip_cred_build; cfg.dualband = interface_count(hapd->iface) > 1; if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == (WPS_RF_50GHZ | WPS_RF_24GHZ)) cfg.dualband = 1; if (cfg.dualband) wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk; wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); goto fail; } #ifdef CONFIG_WPS_UPNP wps->friendly_name = hapd->conf->friendly_name; wps->manufacturer_url = hapd->conf->manufacturer_url; wps->model_description = hapd->conf->model_description; wps->model_url = hapd->conf->model_url; wps->upc = hapd->conf->upc; #endif /* CONFIG_WPS_UPNP */ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && + is_6ghz_op_class(hapd->iconf->op_class)) + wps->use_passphrase = true; +#endif /* CONFIG_P2P */ hapd->wps = wps; bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); return 0; fail: bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); hostapd_free_wps(wps); return -1; } int hostapd_init_wps_complete(struct hostapd_data *hapd) { struct wps_context *wps = hapd->wps; if (wps == NULL) return 0; #ifdef CONFIG_WPS_UPNP if (hostapd_wps_upnp_init(hapd, wps) < 0) { wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); wps_registrar_deinit(wps->registrar); hostapd_free_wps(wps); hapd->wps = NULL; return -1; } #endif /* CONFIG_WPS_UPNP */ return 0; } static void hostapd_wps_nfc_clear(struct wps_context *wps) { #ifdef CONFIG_WPS_NFC wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps); wps->ap_nfc_dev_pw_id = 0; wpabuf_free(wps->ap_nfc_dh_pubkey); wps->ap_nfc_dh_pubkey = NULL; wpabuf_free(wps->ap_nfc_dh_privkey); wps->ap_nfc_dh_privkey = NULL; wpabuf_free(wps->ap_nfc_dev_pw); wps->ap_nfc_dev_pw = NULL; #endif /* CONFIG_WPS_NFC */ } static int hostapd_wps_update_multi_ap(struct hostapd_data *hapd, struct wps_registrar *reg) { struct hostapd_bss_config *conf = hapd->conf; u8 *multi_ap_backhaul_network_key = NULL; size_t multi_ap_backhaul_network_key_len = 0; int ret; if (!(conf->multi_ap & FRONTHAUL_BSS) || !conf->multi_ap_backhaul_ssid.ssid_len) return 0; if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { multi_ap_backhaul_network_key = (u8 *) os_strdup( conf->multi_ap_backhaul_ssid.wpa_passphrase); if (!multi_ap_backhaul_network_key) return -1; multi_ap_backhaul_network_key_len = os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { multi_ap_backhaul_network_key = os_malloc(2 * PMK_LEN + 1); if (!multi_ap_backhaul_network_key) return -1; wpa_snprintf_hex((char *) multi_ap_backhaul_network_key, 2 * PMK_LEN + 1, conf->multi_ap_backhaul_ssid.wpa_psk->psk, PMK_LEN); multi_ap_backhaul_network_key_len = 2 * PMK_LEN; } ret = wps_registrar_update_multi_ap( reg, conf->multi_ap_backhaul_ssid.ssid, conf->multi_ap_backhaul_ssid.ssid_len, multi_ap_backhaul_network_key, multi_ap_backhaul_network_key_len); os_free(multi_ap_backhaul_network_key); return ret; } void hostapd_deinit_wps(struct hostapd_data *hapd) { eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL); if (hapd->wps == NULL) { hostapd_wps_clear_ies(hapd, 1); return; } #ifdef CONFIG_WPS_UPNP hostapd_wps_upnp_deinit(hapd); #endif /* CONFIG_WPS_UPNP */ wps_registrar_deinit(hapd->wps->registrar); wps_free_pending_msgs(hapd->wps->upnp_msgs); hostapd_free_wps(hapd->wps); hapd->wps = NULL; hostapd_wps_clear_ies(hapd, 1); } void hostapd_update_wps(struct hostapd_data *hapd) { struct wps_context *wps = hapd->wps; struct hostapd_bss_config *conf = hapd->conf; if (!wps) return; #ifdef CONFIG_WPS_UPNP wps->friendly_name = conf->friendly_name; wps->manufacturer_url = conf->manufacturer_url; wps->model_description = conf->model_description; wps->model_url = conf->model_url; wps->upc = conf->upc; #endif /* CONFIG_WPS_UPNP */ os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len); wps->ssid_len = conf->ssid.ssid_len; /* Clear WPS settings, then fill them again */ os_free(wps->network_key); wps->network_key = NULL; wps->network_key_len = 0; wps->psk_set = 0; if (conf->ssid.wpa_psk_file) { /* Use per-device PSKs */ } else if (conf->ssid.wpa_passphrase) { wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); if (!wps->network_key) return; wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); } else if (conf->ssid.wpa_psk) { wps->network_key = os_malloc(2 * PMK_LEN + 1); if (!wps->network_key) return; wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, conf->ssid.wpa_psk->psk, PMK_LEN); wps->network_key_len = 2 * PMK_LEN; #ifdef CONFIG_WEP } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { wps->network_key = os_malloc(conf->ssid.wep.len[0]); if (!wps->network_key) return; os_memcpy(wps->network_key, conf->ssid.wep.key[0], conf->ssid.wep.len[0]); wps->network_key_len = conf->ssid.wep.len[0]; #endif /* CONFIG_WEP */ } if (conf->ssid.wpa_psk) { os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN); wps->psk_set = 1; } hostapd_wps_update_multi_ap(hapd, wps->registrar); hostapd_wps_set_vendor_ext(hapd, wps); hostapd_wps_set_application_ext(hapd, wps); if (conf->wps_state) wps_registrar_update_ie(wps->registrar); else hostapd_deinit_wps(hapd); } struct wps_add_pin_data { const u8 *addr; const u8 *uuid; const u8 *pin; size_t pin_len; int timeout; int added; }; static int wps_add_pin(struct hostapd_data *hapd, void *ctx) { struct wps_add_pin_data *data = ctx; int ret; if (hapd->wps == NULL) return 0; ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr, data->uuid, data->pin, data->pin_len, data->timeout); if (ret == 0) data->added++; return ret; } int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, const char *uuid, const char *pin, int timeout) { u8 u[UUID_LEN]; struct wps_add_pin_data data; data.addr = addr; data.uuid = u; data.pin = (const u8 *) pin; data.pin_len = os_strlen(pin); data.timeout = timeout; data.added = 0; if (os_strcmp(uuid, "any") == 0) data.uuid = NULL; else { if (uuid_str2bin(uuid, u)) return -1; data.uuid = u; } if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0) return -1; return data.added ? 0 : -1; } struct wps_button_pushed_ctx { const u8 *p2p_dev_addr; unsigned int count; }; static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { struct wps_button_pushed_ctx *data = ctx; if (hapd->wps) { data->count++; return wps_registrar_button_pushed(hapd->wps->registrar, data->p2p_dev_addr); } return 0; } int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr) { struct wps_button_pushed_ctx ctx; int ret; os_memset(&ctx, 0, sizeof(ctx)); ctx.p2p_dev_addr = p2p_dev_addr; ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx); if (ret == 0 && !ctx.count) ret = -1; return ret; } struct wps_cancel_ctx { unsigned int count; }; static int wps_cancel(struct hostapd_data *hapd, void *ctx) { struct wps_cancel_ctx *data = ctx; if (hapd->wps) { data->count++; wps_registrar_wps_cancel(hapd->wps->registrar); ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL); } return 0; } int hostapd_wps_cancel(struct hostapd_data *hapd) { struct wps_cancel_ctx ctx; int ret; os_memset(&ctx, 0, sizeof(ctx)); ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx); if (ret == 0 && !ctx.count) ret = -1; return ret; } static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal) { struct hostapd_data *hapd = ctx; struct wpabuf *wps_ie; struct ieee802_11_elems elems; if (hapd->wps == NULL) return 0; if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from " MACSTR, MAC2STR(addr)); return 0; } if (elems.ssid && elems.ssid_len > 0 && (elems.ssid_len != hapd->conf->ssid.ssid_len || os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0)) return 0; /* Not for us */ wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps_ie == NULL) return 0; if (wps_validate_probe_req(wps_ie, addr) < 0) { wpabuf_free(wps_ie); return 0; } if (wpabuf_len(wps_ie) > 0) { int p2p_wildcard = 0; #ifdef CONFIG_P2P if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN && os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) p2p_wildcard = 1; #endif /* CONFIG_P2P */ wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie, p2p_wildcard); #ifdef CONFIG_WPS_UPNP /* FIX: what exactly should be included in the WLANEvent? * WPS attributes? Full ProbeReq frame? */ if (!p2p_wildcard) upnp_wps_device_send_wlan_event( hapd->wps_upnp, addr, UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie); #endif /* CONFIG_WPS_UPNP */ } wpabuf_free(wps_ie); return 0; } #ifdef CONFIG_WPS_UPNP static int hostapd_rx_req_put_wlan_response( void *priv, enum upnp_wps_wlanevent_type ev_type, const u8 *mac_addr, const struct wpabuf *msg, enum wps_msg_type msg_type) { struct hostapd_data *hapd = priv; struct sta_info *sta; struct upnp_pending_message *p; wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" MACSTR, ev_type, MAC2STR(mac_addr)); wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", wpabuf_head(msg), wpabuf_len(msg)); if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " "PutWLANResponse WLANEventType %d", ev_type); return -1; } /* * EAP response to ongoing to WPS Registration. Send it to EAP-WSC * server implementation for delivery to the peer. */ sta = ap_get_sta(hapd, mac_addr); #ifndef CONFIG_WPS_STRICT if (!sta) { /* * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: * Pick STA that is in an ongoing WPS registration without * checking the MAC address. */ wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " "on NewWLANEventMAC; try wildcard match"); for (sta = hapd->sta_list; sta; sta = sta->next) { if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) break; } } #endif /* CONFIG_WPS_STRICT */ if (!sta || !(sta->flags & WLAN_STA_WPS)) { wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); return 0; } if (!sta->eapol_sm) { /* * This can happen, e.g., if an ER sends an extra message after * the station has disassociated (but not fully * deauthenticated). */ wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized"); return 0; } p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; os_memcpy(p->addr, sta->addr, ETH_ALEN); p->msg = wpabuf_dup(msg); p->type = msg_type; p->next = hapd->wps->upnp_msgs; hapd->wps->upnp_msgs = p; return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); } static int hostapd_wps_upnp_init(struct hostapd_data *hapd, struct wps_context *wps) { struct upnp_wps_device_ctx *ctx; if (!hapd->conf->upnp_iface) return 0; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return -1; ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; if (hapd->conf->ap_pin) ctx->ap_pin = os_strdup(hapd->conf->ap_pin); hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd, hapd->conf->upnp_iface); if (hapd->wps_upnp == NULL) return -1; wps->wps_upnp = hapd->wps_upnp; return 0; } static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) { upnp_wps_device_deinit(hapd->wps_upnp, hapd); } #endif /* CONFIG_WPS_UPNP */ int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen) { if (hapd->wps == NULL) return 0; return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen); } static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = eloop_data; wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); hostapd_wps_ap_pin_disable(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); } static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) { wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); hapd->ap_pin_failures = 0; hapd->ap_pin_failures_consecutive = 0; hapd->conf->ap_setup_locked = 0; if (hapd->wps->ap_setup_locked) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); hapd->wps->ap_setup_locked = 0; wps_registrar_update_ie(hapd->wps->registrar); } eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); if (timeout > 0) eloop_register_timeout(timeout, 0, hostapd_wps_ap_pin_timeout, hapd, NULL); } static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx) { os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = NULL; #ifdef CONFIG_WPS_UPNP upnp_wps_set_ap_pin(hapd->wps_upnp, NULL); #endif /* CONFIG_WPS_UPNP */ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); return 0; } void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL); } struct wps_ap_pin_data { char pin_txt[9]; int timeout; }; static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) { struct wps_ap_pin_data *data = ctx; if (!hapd->wps) return 0; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt); #endif /* CONFIG_WPS_UPNP */ hostapd_wps_ap_pin_enable(hapd, data->timeout); return 0; } const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) { unsigned int pin; struct wps_ap_pin_data data; if (wps_generate_pin(&pin) < 0) return NULL; os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); data.timeout = timeout; hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); return hapd->conf->ap_pin; } const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) { return hapd->conf->ap_pin; } int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout) { struct wps_ap_pin_data data; int ret; ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); if (os_snprintf_error(sizeof(data.pin_txt), ret)) return -1; data.timeout = timeout; return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); } static int wps_update_ie(struct hostapd_data *hapd, void *ctx) { if (hapd->wps) wps_registrar_update_ie(hapd->wps->registrar); return 0; } void hostapd_wps_update_ie(struct hostapd_data *hapd) { hostapd_wps_for_each(hapd, wps_update_ie, NULL); } int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, const char *auth, const char *encr, const char *key) { struct wps_credential cred; size_t len; os_memset(&cred, 0, sizeof(cred)); len = os_strlen(ssid); if ((len & 1) || len > 2 * sizeof(cred.ssid) || hexstr2bin(ssid, cred.ssid, len / 2)) return -1; cred.ssid_len = len / 2; if (os_strncmp(auth, "OPEN", 4) == 0) cred.auth_type = WPS_AUTH_OPEN; #ifndef CONFIG_NO_TKIP else if (os_strncmp(auth, "WPAPSK", 6) == 0) cred.auth_type = WPS_AUTH_WPAPSK; #endif /* CONFIG_NO_TKIP */ else if (os_strncmp(auth, "WPA2PSK", 7) == 0) cred.auth_type = WPS_AUTH_WPA2PSK; else return -1; if (encr) { if (os_strncmp(encr, "NONE", 4) == 0) cred.encr_type = WPS_ENCR_NONE; #ifndef CONFIG_NO_TKIP else if (os_strncmp(encr, "TKIP", 4) == 0) cred.encr_type = WPS_ENCR_TKIP; #endif /* CONFIG_NO_TKIP */ else if (os_strncmp(encr, "CCMP", 4) == 0) cred.encr_type = WPS_ENCR_AES; else return -1; } else cred.encr_type = WPS_ENCR_NONE; if (key) { len = os_strlen(key); if ((len & 1) || len > 2 * sizeof(cred.key) || hexstr2bin(key, cred.key, len / 2)) return -1; cred.key_len = len / 2; } return wps_registrar_config_ap(hapd->wps->registrar, &cred); } #ifdef CONFIG_WPS_NFC struct wps_nfc_password_token_data { const u8 *oob_dev_pw; size_t oob_dev_pw_len; int added; }; static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx) { struct wps_nfc_password_token_data *data = ctx; int ret; if (hapd->wps == NULL) return 0; ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar, data->oob_dev_pw, data->oob_dev_pw_len); if (ret == 0) data->added++; return ret; } static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd, struct wps_parse_attr *attr) { struct wps_nfc_password_token_data data; data.oob_dev_pw = attr->oob_dev_password; data.oob_dev_pw_len = attr->oob_dev_password_len; data.added = 0; if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0) return -1; return data.added ? 0 : -1; } static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd, const struct wpabuf *wps) { struct wps_parse_attr attr; wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); if (wps_parse_msg(wps, &attr)) { wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); return -1; } if (attr.oob_dev_password) return hostapd_wps_add_nfc_password_token(hapd, &attr); wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); return -1; } int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, const struct wpabuf *data) { const struct wpabuf *wps = data; struct wpabuf *tmp = NULL; int ret; if (wpabuf_len(data) < 4) return -1; if (*wpabuf_head_u8(data) != 0x10) { /* Assume this contains full NDEF record */ tmp = ndef_parse_wifi(data); if (tmp == NULL) { wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); return -1; } wps = tmp; } ret = hostapd_wps_nfc_tag_process(hapd, wps); wpabuf_free(tmp); return ret; } struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, int ndef) { struct wpabuf *ret; if (hapd->wps == NULL) return NULL; ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd), hapd->iconf->channel); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef) { struct wpabuf *ret; if (hapd->wps == NULL) return NULL; if (hapd->conf->wps_nfc_dh_pubkey == NULL) { struct wps_context *wps = hapd->wps; if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey, &hapd->conf->wps_nfc_dh_privkey) < 0) return NULL; hostapd_wps_nfc_clear(wps); wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { hostapd_wps_nfc_clear(wps); return NULL; } } ret = wps_build_nfc_handover_sel(hapd->wps, hapd->conf->wps_nfc_dh_pubkey, hapd->own_addr, hapd->iface->freq); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, const struct wpabuf *req, const struct wpabuf *sel) { struct wpabuf *wps; int ret = -1; u16 wsc_len; const u8 *pos; struct wpabuf msg; struct wps_parse_attr attr; u16 dev_pw_id; /* * Enrollee/station is always initiator of the NFC connection handover, * so use the request message here to find Enrollee public key hash. */ wps = ndef_parse_wifi(req); if (wps == NULL) return -1; wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " "payload from NFC connection handover"); wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); if (wpabuf_len(wps) < 2) { wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " "Message"); goto out; } pos = wpabuf_head(wps); wsc_len = WPA_GET_BE16(pos); if (wsc_len > wpabuf_len(wps) - 2) { wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " "in rt Wi-Fi Handover Request Message", wsc_len); goto out; } pos += 2; wpa_hexdump(MSG_DEBUG, "WPS: WSC attributes in Wi-Fi Handover Request Message", pos, wsc_len); if (wsc_len < wpabuf_len(wps) - 2) { wpa_hexdump(MSG_DEBUG, "WPS: Ignore extra data after WSC attributes", pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); } wpabuf_set(&msg, pos, wsc_len); ret = wps_parse_msg(&msg, &attr); if (ret < 0) { wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " "Wi-Fi Handover Request Message"); goto out; } if (attr.oob_dev_password == NULL || attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " "included in Wi-Fi Handover Request Message"); ret = -1; goto out; } if (attr.uuid_e == NULL) { wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " "Handover Request Message"); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", attr.oob_dev_password, attr.oob_dev_password_len); dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + WPS_OOB_PUBKEY_HASH_LEN); if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " "%u in Wi-Fi Handover Request Message", dev_pw_id); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar, attr.oob_dev_password, DEV_PW_NFC_CONNECTION_HANDOVER, NULL, 0, 1); out: wpabuf_free(wps); return ret; } struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) { if (hapd->conf->wps_nfc_pw_from_config) { return wps_nfc_token_build(ndef, hapd->conf->wps_nfc_dev_pw_id, hapd->conf->wps_nfc_dh_pubkey, hapd->conf->wps_nfc_dev_pw); } return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, &hapd->conf->wps_nfc_dh_pubkey, &hapd->conf->wps_nfc_dh_privkey, &hapd->conf->wps_nfc_dev_pw); } int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) { struct wps_context *wps = hapd->wps; struct wpabuf *pw; if (wps == NULL) return -1; if (!hapd->conf->wps_nfc_dh_pubkey || !hapd->conf->wps_nfc_dh_privkey || !hapd->conf->wps_nfc_dev_pw || !hapd->conf->wps_nfc_dev_pw_id) return -1; hostapd_wps_nfc_clear(wps); wpa_printf(MSG_DEBUG, "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)", hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps); wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); pw = hapd->conf->wps_nfc_dev_pw; wps->ap_nfc_dev_pw = wpabuf_alloc( wpabuf_len(pw) * 2 + 1); if (wps->ap_nfc_dev_pw) { wpa_snprintf_hex_uppercase( (char *) wpabuf_put(wps->ap_nfc_dev_pw, wpabuf_len(pw) * 2), wpabuf_len(pw) * 2 + 1, wpabuf_head(pw), wpabuf_len(pw)); } if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || !wps->ap_nfc_dev_pw) { hostapd_wps_nfc_clear(wps); return -1; } return 0; } void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s", hapd->conf->iface); hostapd_wps_nfc_clear(hapd->wps); } #endif /* CONFIG_WPS_NFC */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 96681843e258..3e5cfb01d565 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -1,2701 +1,2701 @@ /* * IEEE 802.11 Common routines * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "defs.h" #include "wpa_common.h" #include "drivers/driver.h" #include "qca-vendor.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, struct ieee802_11_elems *elems, int show_errors) { unsigned int oui; /* first 3 bytes in vendor specific information element are the IEEE * OUI of the vendor. The following byte is used a vendor specific * sub-type. */ if (elen < 4) { if (show_errors) { wpa_printf(MSG_MSGDUMP, "short vendor specific " "information element ignored (len=%lu)", (unsigned long) elen); } return -1; } oui = WPA_GET_BE24(pos); switch (oui) { case OUI_MICROSOFT: /* Microsoft/Wi-Fi information elements are further typed and * subtyped */ switch (pos[3]) { case 1: /* Microsoft OUI (00:50:F2) with OUI Type 1: * real WPA information element */ elems->wpa_ie = pos; elems->wpa_ie_len = elen; break; case WMM_OUI_TYPE: /* WMM information element */ if (elen < 5) { wpa_printf(MSG_MSGDUMP, "short WMM " "information element ignored " "(len=%lu)", (unsigned long) elen); return -1; } switch (pos[4]) { case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: /* * Share same pointer since only one of these * is used and they start with same data. * Length field can be used to distinguish the * IEs. */ elems->wmm = pos; elems->wmm_len = elen; break; case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: elems->wmm_tspec = pos; elems->wmm_tspec_len = elen; break; default: wpa_printf(MSG_EXCESSIVE, "unknown WMM " "information element ignored " "(subtype=%d len=%lu)", pos[4], (unsigned long) elen); return -1; } break; case 4: /* Wi-Fi Protected Setup (WPS) IE */ elems->wps_ie = pos; elems->wps_ie_len = elen; break; default: wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " "information element ignored " "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; case OUI_WFA: switch (pos[3]) { case P2P_OUI_TYPE: /* Wi-Fi Alliance - P2P IE */ elems->p2p = pos; elems->p2p_len = elen; break; case WFD_OUI_TYPE: /* Wi-Fi Alliance - WFD IE */ elems->wfd = pos; elems->wfd_len = elen; break; case HS20_INDICATION_OUI_TYPE: /* Hotspot 2.0 */ elems->hs20 = pos; elems->hs20_len = elen; break; case HS20_OSEN_OUI_TYPE: /* Hotspot 2.0 OSEN */ elems->osen = pos; elems->osen_len = elen; break; case MBO_OUI_TYPE: /* MBO-OCE */ elems->mbo = pos; elems->mbo_len = elen; break; case HS20_ROAMING_CONS_SEL_OUI_TYPE: /* Hotspot 2.0 Roaming Consortium Selection */ elems->roaming_cons_sel = pos; elems->roaming_cons_sel_len = elen; break; case MULTI_AP_OUI_TYPE: elems->multi_ap = pos; elems->multi_ap_len = elen; break; case OWE_OUI_TYPE: /* OWE Transition Mode element */ break; case DPP_CC_OUI_TYPE: /* DPP Configurator Connectivity element */ break; case SAE_PK_OUI_TYPE: elems->sae_pk = pos + 4; elems->sae_pk_len = elen - 4; break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; case OUI_BROADCOM: switch (pos[3]) { case VENDOR_HT_CAPAB_OUI_TYPE: elems->vendor_ht_cap = pos; elems->vendor_ht_cap_len = elen; break; case VENDOR_VHT_TYPE: if (elen > 4 && (pos[4] == VENDOR_VHT_SUBTYPE || pos[4] == VENDOR_VHT_SUBTYPE2)) { elems->vendor_vht = pos; elems->vendor_vht_len = elen; } else return -1; break; default: wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; case OUI_QCA: switch (pos[3]) { case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: elems->pref_freq_list = pos; elems->pref_freq_list_len = elen; break; default: wpa_printf(MSG_EXCESSIVE, "Unknown QCA information element ignored (type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; default: wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " "information element ignored (vendor OUI " "%02x:%02x:%02x len=%lu)", pos[0], pos[1], pos[2], (unsigned long) elen); return -1; } return 0; } static int ieee802_11_parse_extension(const u8 *pos, size_t elen, struct ieee802_11_elems *elems, int show_errors) { u8 ext_id; if (elen < 1) { if (show_errors) { wpa_printf(MSG_MSGDUMP, "short information element (Ext)"); } return -1; } ext_id = *pos++; elen--; elems->frag_ies.last_eid_ext = 0; switch (ext_id) { case WLAN_EID_EXT_ASSOC_DELAY_INFO: if (elen != 1) break; elems->assoc_delay_info = pos; break; case WLAN_EID_EXT_FILS_REQ_PARAMS: if (elen < 3) break; elems->fils_req_params = pos; elems->fils_req_params_len = elen; break; case WLAN_EID_EXT_FILS_KEY_CONFIRM: elems->fils_key_confirm = pos; elems->fils_key_confirm_len = elen; break; case WLAN_EID_EXT_FILS_SESSION: if (elen != FILS_SESSION_LEN) break; elems->fils_session = pos; break; case WLAN_EID_EXT_FILS_HLP_CONTAINER: if (elen < 2 * ETH_ALEN) break; elems->fils_hlp = pos; elems->fils_hlp_len = elen; break; case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN: if (elen < 1) break; elems->fils_ip_addr_assign = pos; elems->fils_ip_addr_assign_len = elen; break; case WLAN_EID_EXT_KEY_DELIVERY: if (elen < WPA_KEY_RSC_LEN) break; elems->key_delivery = pos; elems->key_delivery_len = elen; break; case WLAN_EID_EXT_WRAPPED_DATA: elems->wrapped_data = pos; elems->wrapped_data_len = elen; break; case WLAN_EID_EXT_FILS_PUBLIC_KEY: if (elen < 1) break; elems->fils_pk = pos; elems->fils_pk_len = elen; break; case WLAN_EID_EXT_FILS_NONCE: if (elen != FILS_NONCE_LEN) break; elems->fils_nonce = pos; break; case WLAN_EID_EXT_OWE_DH_PARAM: if (elen < 2) break; elems->owe_dh = pos; elems->owe_dh_len = elen; break; case WLAN_EID_EXT_PASSWORD_IDENTIFIER: elems->password_id = pos; elems->password_id_len = elen; break; case WLAN_EID_EXT_HE_CAPABILITIES: elems->he_capabilities = pos; elems->he_capabilities_len = elen; break; case WLAN_EID_EXT_HE_OPERATION: elems->he_operation = pos; elems->he_operation_len = elen; break; case WLAN_EID_EXT_OCV_OCI: elems->oci = pos; elems->oci_len = elen; break; case WLAN_EID_EXT_SHORT_SSID_LIST: elems->short_ssid_list = pos; elems->short_ssid_list_len = elen; break; case WLAN_EID_EXT_HE_6GHZ_BAND_CAP: if (elen < sizeof(struct ieee80211_he_6ghz_band_cap)) break; elems->he_6ghz_band_cap = pos; break; case WLAN_EID_EXT_PASN_PARAMS: elems->pasn_params = pos; elems->pasn_params_len = elen; break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)", ext_id, (unsigned int) elen); } return -1; } if (elen == 254) elems->frag_ies.last_eid_ext = ext_id; return 0; } static void ieee802_11_parse_fragment(struct frag_ies_info *frag_ies, const u8 *pos, u8 elen) { if (frag_ies->n_frags >= MAX_NUM_FRAG_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, "Too many element fragments - skip"); return; } /* * Note: while EID == 0 is a valid ID (SSID IE), it should not be * fragmented. */ if (!frag_ies->last_eid) { wpa_printf(MSG_MSGDUMP, "Fragment without a valid last element - skip"); return; } frag_ies->frags[frag_ies->n_frags].ie = pos; frag_ies->frags[frag_ies->n_frags].ie_len = elen; frag_ies->frags[frag_ies->n_frags].eid = frag_ies->last_eid; frag_ies->frags[frag_ies->n_frags].eid_ext = frag_ies->last_eid_ext; frag_ies->n_frags++; } /** * ieee802_11_parse_elems - Parse information elements in management frames * @start: Pointer to the start of IEs * @len: Length of IE buffer in octets * @elems: Data structure for parsed elements * @show_errors: Whether to show parsing errors in debug log * Returns: Parsing result */ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { const struct element *elem; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); if (!start) return ParseOK; for_each_element(elem, start, len) { u8 id = elem->id, elen = elem->datalen; const u8 *pos = elem->data; switch (id) { case WLAN_EID_SSID: if (elen > SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "Ignored too long SSID element (elen=%u)", elen); break; } if (elems->ssid) { wpa_printf(MSG_MSGDUMP, "Ignored duplicated SSID element"); break; } elems->ssid = pos; elems->ssid_len = elen; break; case WLAN_EID_SUPP_RATES: elems->supp_rates = pos; elems->supp_rates_len = elen; break; case WLAN_EID_DS_PARAMS: if (elen < 1) break; elems->ds_params = pos; break; case WLAN_EID_CF_PARAMS: case WLAN_EID_TIM: break; case WLAN_EID_CHALLENGE: elems->challenge = pos; elems->challenge_len = elen; break; case WLAN_EID_ERP_INFO: if (elen < 1) break; elems->erp_info = pos; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; elems->ext_supp_rates_len = elen; break; case WLAN_EID_VENDOR_SPECIFIC: if (ieee802_11_parse_vendor_specific(pos, elen, elems, show_errors)) unknown++; break; case WLAN_EID_RSN: elems->rsn_ie = pos; elems->rsn_ie_len = elen; break; case WLAN_EID_RSNX: elems->rsnxe = pos; elems->rsnxe_len = elen; break; case WLAN_EID_PWR_CAPABILITY: if (elen < 2) break; elems->power_capab = pos; elems->power_capab_len = elen; break; case WLAN_EID_SUPPORTED_CHANNELS: elems->supp_channels = pos; elems->supp_channels_len = elen; break; case WLAN_EID_MOBILITY_DOMAIN: if (elen < sizeof(struct rsn_mdie)) break; elems->mdie = pos; elems->mdie_len = elen; break; case WLAN_EID_FAST_BSS_TRANSITION: if (elen < sizeof(struct rsn_ftie)) break; elems->ftie = pos; elems->ftie_len = elen; break; case WLAN_EID_TIMEOUT_INTERVAL: if (elen != 5) break; elems->timeout_int = pos; break; case WLAN_EID_HT_CAP: if (elen < sizeof(struct ieee80211_ht_capabilities)) break; elems->ht_capabilities = pos; break; case WLAN_EID_HT_OPERATION: if (elen < sizeof(struct ieee80211_ht_operation)) break; elems->ht_operation = pos; break; case WLAN_EID_MESH_CONFIG: elems->mesh_config = pos; elems->mesh_config_len = elen; break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; elems->mesh_id_len = elen; break; case WLAN_EID_PEER_MGMT: elems->peer_mgmt = pos; elems->peer_mgmt_len = elen; break; case WLAN_EID_VHT_CAP: if (elen < sizeof(struct ieee80211_vht_capabilities)) break; elems->vht_capabilities = pos; break; case WLAN_EID_VHT_OPERATION: if (elen < sizeof(struct ieee80211_vht_operation)) break; elems->vht_operation = pos; break; case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: if (elen != 1) break; elems->vht_opmode_notif = pos; break; case WLAN_EID_LINK_ID: if (elen < 18) break; elems->link_id = pos; break; case WLAN_EID_INTERWORKING: elems->interworking = pos; elems->interworking_len = elen; break; case WLAN_EID_QOS_MAP_SET: if (elen < 16) break; elems->qos_map_set = pos; elems->qos_map_set_len = elen; break; case WLAN_EID_EXT_CAPAB: elems->ext_capab = pos; elems->ext_capab_len = elen; break; case WLAN_EID_BSS_MAX_IDLE_PERIOD: if (elen < 3) break; elems->bss_max_idle_period = pos; break; case WLAN_EID_SSID_LIST: elems->ssid_list = pos; elems->ssid_list_len = elen; break; case WLAN_EID_AMPE: elems->ampe = pos; elems->ampe_len = elen; break; case WLAN_EID_MIC: elems->mic = pos; elems->mic_len = elen; /* after mic everything is encrypted, so stop. */ goto done; case WLAN_EID_MULTI_BAND: if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)", id, elen); break; } elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos; elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; elems->mb_ies.nof_ies++; break; case WLAN_EID_SUPPORTED_OPERATING_CLASSES: elems->supp_op_classes = pos; elems->supp_op_classes_len = elen; break; case WLAN_EID_RRM_ENABLED_CAPABILITIES: elems->rrm_enabled = pos; elems->rrm_enabled_len = elen; break; case WLAN_EID_CAG_NUMBER: elems->cag_number = pos; elems->cag_number_len = elen; break; case WLAN_EID_AP_CSN: if (elen < 1) break; elems->ap_csn = pos; break; case WLAN_EID_FILS_INDICATION: if (elen < 2) break; elems->fils_indic = pos; elems->fils_indic_len = elen; break; case WLAN_EID_DILS: if (elen < 2) break; elems->dils = pos; elems->dils_len = elen; break; case WLAN_EID_S1G_CAPABILITIES: if (elen < 15) break; elems->s1g_capab = pos; break; case WLAN_EID_FRAGMENT: ieee802_11_parse_fragment(&elems->frag_ies, pos, elen); break; case WLAN_EID_EXTENSION: if (ieee802_11_parse_extension(pos, elen, elems, show_errors)) unknown++; break; default: unknown++; if (!show_errors) break; wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " "ignored unknown element (id=%d elen=%d)", id, elen); break; } if (id != WLAN_EID_FRAGMENT && elen == 255) elems->frag_ies.last_eid = id; if (id == WLAN_EID_EXTENSION && !elems->frag_ies.last_eid_ext) elems->frag_ies.last_eid = 0; } if (!for_each_element_completed(elem, start, len)) { if (show_errors) { wpa_printf(MSG_DEBUG, "IEEE 802.11 element parse failed @%d", (int) (start + len - (const u8 *) elem)); wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); } return ParseFailed; } done: return unknown ? ParseUnknown : ParseOK; } int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { const struct element *elem; int count = 0; if (ies == NULL) return 0; for_each_element(elem, ies, ies_len) count++; return count; } struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type) { struct wpabuf *buf; const struct element *elem, *found = NULL; for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) { found = elem; break; } } if (!found) return NULL; /* No specified vendor IE found */ buf = wpabuf_alloc(ies_len); if (buf == NULL) return NULL; /* * There may be multiple vendor IEs in the message, so need to * concatenate their data fields. */ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4); } return buf; } const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) { u16 fc, type, stype; /* * PS-Poll frames are 16 bytes. All other frames are * 24 bytes or longer. */ if (len < 16) return NULL; fc = le_to_host16(hdr->frame_control); type = WLAN_FC_GET_TYPE(fc); stype = WLAN_FC_GET_STYPE(fc); switch (type) { case WLAN_FC_TYPE_DATA: if (len < 24) return NULL; switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { case WLAN_FC_FROMDS | WLAN_FC_TODS: case WLAN_FC_TODS: return hdr->addr1; case WLAN_FC_FROMDS: return hdr->addr2; default: return NULL; } case WLAN_FC_TYPE_CTRL: if (stype != WLAN_FC_STYPE_PSPOLL) return NULL; return hdr->addr1; case WLAN_FC_TYPE_MGMT: return hdr->addr3; default: return NULL; } } int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val) { int num, v; const char *pos; struct hostapd_wmm_ac_params *ac; /* skip 'wme_ac_' or 'wmm_ac_' prefix */ pos = name + 7; if (os_strncmp(pos, "be_", 3) == 0) { num = 0; pos += 3; } else if (os_strncmp(pos, "bk_", 3) == 0) { num = 1; pos += 3; } else if (os_strncmp(pos, "vi_", 3) == 0) { num = 2; pos += 3; } else if (os_strncmp(pos, "vo_", 3) == 0) { num = 3; pos += 3; } else { wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); return -1; } ac = &wmm_ac_params[num]; if (os_strcmp(pos, "aifs") == 0) { v = atoi(val); if (v < 1 || v > 255) { wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); return -1; } ac->aifs = v; } else if (os_strcmp(pos, "cwmin") == 0) { v = atoi(val); if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); return -1; } ac->cwmin = v; } else if (os_strcmp(pos, "cwmax") == 0) { v = atoi(val); if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); return -1; } ac->cwmax = v; } else if (os_strcmp(pos, "txop_limit") == 0) { v = atoi(val); if (v < 0 || v > 0xffff) { wpa_printf(MSG_ERROR, "Invalid txop value %d", v); return -1; } ac->txop_limit = v; } else if (os_strcmp(pos, "acm") == 0) { v = atoi(val); if (v < 0 || v > 1) { wpa_printf(MSG_ERROR, "Invalid acm value %d", v); return -1; } ac->admission_control_mandatory = v; } else { wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); return -1; } return 0; } /* convert floats with one decimal place to value*10 int, i.e., * "1.5" will return 15 */ static int hostapd_config_read_int10(const char *value) { int i, d; char *pos; i = atoi(value); pos = os_strchr(value, '.'); d = 0; if (pos) { pos++; if (*pos >= '0' && *pos <= '9') d = *pos - '0'; } return i * 10 + d; } static int valid_cw(int cw) { return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 || cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 || cw == 32767); } int hostapd_config_tx_queue(struct hostapd_tx_queue_params tx_queue[], const char *name, const char *val) { int num; const char *pos; struct hostapd_tx_queue_params *queue; /* skip 'tx_queue_' prefix */ pos = name + 9; if (os_strncmp(pos, "data", 4) == 0 && pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { num = pos[4] - '0'; pos += 6; } else if (os_strncmp(pos, "after_beacon_", 13) == 0 || os_strncmp(pos, "beacon_", 7) == 0) { wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); return 0; } else { wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos); return -1; } if (num >= NUM_TX_QUEUES) { /* for backwards compatibility, do not trigger failure */ wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); return 0; } queue = &tx_queue[num]; if (os_strcmp(pos, "aifs") == 0) { queue->aifs = atoi(val); if (queue->aifs < 0 || queue->aifs > 255) { wpa_printf(MSG_ERROR, "Invalid AIFS value %d", queue->aifs); return -1; } } else if (os_strcmp(pos, "cwmin") == 0) { queue->cwmin = atoi(val); if (!valid_cw(queue->cwmin)) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", queue->cwmin); return -1; } } else if (os_strcmp(pos, "cwmax") == 0) { queue->cwmax = atoi(val); if (!valid_cw(queue->cwmax)) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", queue->cwmax); return -1; } } else if (os_strcmp(pos, "burst") == 0) { queue->burst = hostapd_config_read_int10(val); } else { wpa_printf(MSG_ERROR, "Unknown queue field '%s'", pos); return -1; } return 0; } enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { u8 op_class; return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT, &op_class, channel); } /** * ieee80211_freq_to_channel_ext - Convert frequency into channel info * for HT40, VHT, and HE. DFS channels are not covered. * @freq: Frequency (MHz) to convert * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below * @chanwidth: VHT/EDMG channel width (CHANWIDTH_*) * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure */ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int chanwidth, u8 *op_class, u8 *channel) { u8 vht_opclass; /* TODO: more operating classes */ if (sec_channel > 1 || sec_channel < -1) return NUM_HOSTAPD_MODES; if (freq >= 2412 && freq <= 2472) { if ((freq - 2407) % 5) return NUM_HOSTAPD_MODES; if (chanwidth) return NUM_HOSTAPD_MODES; /* 2.407 GHz, channels 1..13 */ if (sec_channel == 1) *op_class = 83; else if (sec_channel == -1) *op_class = 84; else *op_class = 81; *channel = (freq - 2407) / 5; return HOSTAPD_MODE_IEEE80211G; } if (freq == 2484) { if (sec_channel || chanwidth) return NUM_HOSTAPD_MODES; *op_class = 82; /* channel 14 */ *channel = 14; return HOSTAPD_MODE_IEEE80211B; } if (freq >= 4900 && freq < 5000) { if ((freq - 4000) % 5) return NUM_HOSTAPD_MODES; *channel = (freq - 4000) / 5; *op_class = 0; /* TODO */ return HOSTAPD_MODE_IEEE80211A; } switch (chanwidth) { case CHANWIDTH_80MHZ: vht_opclass = 128; break; case CHANWIDTH_160MHZ: vht_opclass = 129; break; case CHANWIDTH_80P80MHZ: vht_opclass = 130; break; default: vht_opclass = 0; break; } /* 5 GHz, channels 36..48 */ if (freq >= 5180 && freq <= 5240) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; if (vht_opclass) *op_class = vht_opclass; else if (sec_channel == 1) *op_class = 116; else if (sec_channel == -1) *op_class = 117; else *op_class = 115; *channel = (freq - 5000) / 5; return HOSTAPD_MODE_IEEE80211A; } /* 5 GHz, channels 52..64 */ if (freq >= 5260 && freq <= 5320) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; if (vht_opclass) *op_class = vht_opclass; else if (sec_channel == 1) *op_class = 119; else if (sec_channel == -1) *op_class = 120; else *op_class = 118; *channel = (freq - 5000) / 5; return HOSTAPD_MODE_IEEE80211A; } /* 5 GHz, channels 149..177 */ if (freq >= 5745 && freq <= 5885) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; if (vht_opclass) *op_class = vht_opclass; else if (sec_channel == 1) *op_class = 126; else if (sec_channel == -1) *op_class = 127; else if (freq <= 5805) *op_class = 124; else *op_class = 125; *channel = (freq - 5000) / 5; return HOSTAPD_MODE_IEEE80211A; } /* 5 GHz, channels 100..140 */ if (freq >= 5000 && freq <= 5700) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; if (vht_opclass) *op_class = vht_opclass; else if (sec_channel == 1) *op_class = 122; else if (sec_channel == -1) *op_class = 123; else *op_class = 121; *channel = (freq - 5000) / 5; return HOSTAPD_MODE_IEEE80211A; } if (freq >= 5000 && freq < 5900) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; *channel = (freq - 5000) / 5; *op_class = 0; /* TODO */ return HOSTAPD_MODE_IEEE80211A; } if (freq > 5950 && freq <= 7115) { if ((freq - 5950) % 5) return NUM_HOSTAPD_MODES; switch (chanwidth) { case CHANWIDTH_80MHZ: *op_class = 133; break; case CHANWIDTH_160MHZ: *op_class = 134; break; case CHANWIDTH_80P80MHZ: *op_class = 135; break; default: if (sec_channel) *op_class = 132; else *op_class = 131; break; } *channel = (freq - 5950) / 5; return HOSTAPD_MODE_IEEE80211A; } if (freq == 5935) { *op_class = 136; *channel = (freq - 5925) / 5; return HOSTAPD_MODE_IEEE80211A; } /* 56.16 GHz, channel 1..6 */ if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) { if (sec_channel) return NUM_HOSTAPD_MODES; switch (chanwidth) { case CHANWIDTH_USE_HT: case CHANWIDTH_2160MHZ: *channel = (freq - 56160) / 2160; *op_class = 180; break; case CHANWIDTH_4320MHZ: /* EDMG channels 9 - 13 */ if (freq > 56160 + 2160 * 5) return NUM_HOSTAPD_MODES; *channel = (freq - 56160) / 2160 + 8; *op_class = 181; break; case CHANWIDTH_6480MHZ: /* EDMG channels 17 - 20 */ if (freq > 56160 + 2160 * 4) return NUM_HOSTAPD_MODES; *channel = (freq - 56160) / 2160 + 16; *op_class = 182; break; case CHANWIDTH_8640MHZ: /* EDMG channels 25 - 27 */ if (freq > 56160 + 2160 * 3) return NUM_HOSTAPD_MODES; *channel = (freq - 56160) / 2160 + 24; *op_class = 183; break; default: return NUM_HOSTAPD_MODES; } return HOSTAPD_MODE_IEEE80211AD; } return NUM_HOSTAPD_MODES; } int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, int sec_channel, u8 *op_class, u8 *channel) { int cw = CHAN_WIDTH_UNKNOWN; switch (chanwidth) { case CHAN_WIDTH_UNKNOWN: case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: case CHAN_WIDTH_40: cw = CHANWIDTH_USE_HT; break; case CHAN_WIDTH_80: cw = CHANWIDTH_80MHZ; break; case CHAN_WIDTH_80P80: cw = CHANWIDTH_80P80MHZ; break; case CHAN_WIDTH_160: cw = CHANWIDTH_160MHZ; break; case CHAN_WIDTH_2160: cw = CHANWIDTH_2160MHZ; break; case CHAN_WIDTH_4320: cw = CHANWIDTH_4320MHZ; break; case CHAN_WIDTH_6480: cw = CHANWIDTH_6480MHZ; break; case CHAN_WIDTH_8640: cw = CHANWIDTH_8640MHZ; break; } if (ieee80211_freq_to_channel_ext(freq, sec_channel, cw, op_class, channel) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_WARNING, "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)", freq, chanwidth, sec_channel); return -1; } return 0; } static const char *const us_op_class_cc[] = { "US", "CA", NULL }; static const char *const eu_op_class_cc[] = { "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL }; static const char *const jp_op_class_cc[] = { "JP", NULL }; static const char *const cn_op_class_cc[] = { "CN", NULL }; static int country_match(const char *const cc[], const char *const country) { int i; if (country == NULL) return 0; for (i = 0; cc[i]; i++) { if (cc[i][0] == country[0] && cc[i][1] == country[1]) return 1; } return 0; } static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) { switch (op_class) { case 12: /* channels 1..11 */ case 32: /* channels 1..7; 40 MHz */ case 33: /* channels 5..11; 40 MHz */ if (chan < 1 || chan > 11) return -1; return 2407 + 5 * chan; case 1: /* channels 36,40,44,48 */ case 2: /* channels 52,56,60,64; dfs */ case 22: /* channels 36,44; 40 MHz */ case 23: /* channels 52,60; 40 MHz */ case 27: /* channels 40,48; 40 MHz */ case 28: /* channels 56,64; 40 MHz */ if (chan < 36 || chan > 64) return -1; return 5000 + 5 * chan; case 4: /* channels 100-144 */ case 24: /* channels 100-140; 40 MHz */ if (chan < 100 || chan > 144) return -1; return 5000 + 5 * chan; case 3: /* channels 149,153,157,161 */ case 25: /* channels 149,157; 40 MHz */ case 26: /* channels 149,157; 40 MHz */ case 30: /* channels 153,161; 40 MHz */ case 31: /* channels 153,161; 40 MHz */ if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; case 5: /* channels 149,153,157,161,165 */ if (chan < 149 || chan > 165) return -1; return 5000 + 5 * chan; case 34: /* 60 GHz band, channels 1..8 */ if (chan < 1 || chan > 8) return -1; return 56160 + 2160 * chan; case 37: /* 60 GHz band, EDMG CB2, channels 9..15 */ if (chan < 9 || chan > 15) return -1; return 56160 + 2160 * (chan - 8); case 38: /* 60 GHz band, EDMG CB3, channels 17..22 */ if (chan < 17 || chan > 22) return -1; return 56160 + 2160 * (chan - 16); case 39: /* 60 GHz band, EDMG CB4, channels 25..29 */ if (chan < 25 || chan > 29) return -1; return 56160 + 2160 * (chan - 24); } return -1; } static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan) { switch (op_class) { case 4: /* channels 1..13 */ case 11: /* channels 1..9; 40 MHz */ case 12: /* channels 5..13; 40 MHz */ if (chan < 1 || chan > 13) return -1; return 2407 + 5 * chan; case 1: /* channels 36,40,44,48 */ case 2: /* channels 52,56,60,64; dfs */ case 5: /* channels 36,44; 40 MHz */ case 6: /* channels 52,60; 40 MHz */ case 8: /* channels 40,48; 40 MHz */ case 9: /* channels 56,64; 40 MHz */ if (chan < 36 || chan > 64) return -1; return 5000 + 5 * chan; case 3: /* channels 100-140 */ case 7: /* channels 100-132; 40 MHz */ case 10: /* channels 104-136; 40 MHz */ case 16: /* channels 100-140 */ if (chan < 100 || chan > 140) return -1; return 5000 + 5 * chan; case 17: /* channels 149,153,157,161,165,169 */ if (chan < 149 || chan > 169) return -1; return 5000 + 5 * chan; case 18: /* 60 GHz band, channels 1..6 */ if (chan < 1 || chan > 6) return -1; return 56160 + 2160 * chan; case 21: /* 60 GHz band, EDMG CB2, channels 9..11 */ if (chan < 9 || chan > 11) return -1; return 56160 + 2160 * (chan - 8); case 22: /* 60 GHz band, EDMG CB3, channels 17..18 */ if (chan < 17 || chan > 18) return -1; return 56160 + 2160 * (chan - 16); case 23: /* 60 GHz band, EDMG CB4, channels 25 */ if (chan != 25) return -1; return 56160 + 2160 * (chan - 24); } return -1; } static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan) { switch (op_class) { case 30: /* channels 1..13 */ case 56: /* channels 1..9; 40 MHz */ case 57: /* channels 5..13; 40 MHz */ if (chan < 1 || chan > 13) return -1; return 2407 + 5 * chan; case 31: /* channel 14 */ if (chan != 14) return -1; return 2414 + 5 * chan; case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */ case 32: /* channels 52,56,60,64 */ case 33: /* channels 52,56,60,64 */ case 36: /* channels 36,44; 40 MHz */ case 37: /* channels 52,60; 40 MHz */ case 38: /* channels 52,60; 40 MHz */ case 41: /* channels 40,48; 40 MHz */ case 42: /* channels 56,64; 40 MHz */ case 43: /* channels 56,64; 40 MHz */ if (chan < 34 || chan > 64) return -1; return 5000 + 5 * chan; case 34: /* channels 100-140 */ case 35: /* channels 100-140 */ case 39: /* channels 100-132; 40 MHz */ case 40: /* channels 100-132; 40 MHz */ case 44: /* channels 104-136; 40 MHz */ case 45: /* channels 104-136; 40 MHz */ case 58: /* channels 100-140 */ if (chan < 100 || chan > 140) return -1; return 5000 + 5 * chan; case 59: /* 60 GHz band, channels 1..6 */ if (chan < 1 || chan > 6) return -1; return 56160 + 2160 * chan; case 62: /* 60 GHz band, EDMG CB2, channels 9..11 */ if (chan < 9 || chan > 11) return -1; return 56160 + 2160 * (chan - 8); case 63: /* 60 GHz band, EDMG CB3, channels 17..18 */ if (chan < 17 || chan > 18) return -1; return 56160 + 2160 * (chan - 16); case 64: /* 60 GHz band, EDMG CB4, channel 25 */ if (chan != 25) return -1; return 56160 + 2160 * (chan - 24); } return -1; } static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan) { switch (op_class) { case 7: /* channels 1..13 */ case 8: /* channels 1..9; 40 MHz */ case 9: /* channels 5..13; 40 MHz */ if (chan < 1 || chan > 13) return -1; return 2407 + 5 * chan; case 1: /* channels 36,40,44,48 */ case 2: /* channels 52,56,60,64; dfs */ case 4: /* channels 36,44; 40 MHz */ case 5: /* channels 52,60; 40 MHz */ if (chan < 36 || chan > 64) return -1; return 5000 + 5 * chan; case 3: /* channels 149,153,157,161,165 */ case 6: /* channels 149,157; 40 MHz */ if (chan < 149 || chan > 165) return -1; return 5000 + 5 * chan; } return -1; } static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) { /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ switch (op_class) { case 81: /* channels 1..13 */ if (chan < 1 || chan > 13) return -1; return 2407 + 5 * chan; case 82: /* channel 14 */ if (chan != 14) return -1; return 2414 + 5 * chan; case 83: /* channels 1..9; 40 MHz */ case 84: /* channels 5..13; 40 MHz */ if (chan < 1 || chan > 13) return -1; return 2407 + 5 * chan; case 115: /* channels 36,40,44,48; indoor only */ case 116: /* channels 36,44; 40 MHz; indoor only */ case 117: /* channels 40,48; 40 MHz; indoor only */ case 118: /* channels 52,56,60,64; dfs */ case 119: /* channels 52,60; 40 MHz; dfs */ case 120: /* channels 56,64; 40 MHz; dfs */ if (chan < 36 || chan > 64) return -1; return 5000 + 5 * chan; case 121: /* channels 100-140 */ case 122: /* channels 100-142; 40 MHz */ case 123: /* channels 104-136; 40 MHz */ if (chan < 100 || chan > 140) return -1; return 5000 + 5 * chan; case 124: /* channels 149,153,157,161 */ if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; case 125: /* channels 149,153,157,161,165,169,173,177 */ case 126: /* channels 149,157,165,173; 40 MHz */ case 127: /* channels 153,161,169,177; 40 MHz */ if (chan < 149 || chan > 177) return -1; return 5000 + 5 * chan; case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ if (chan < 36 || chan > 177) return -1; return 5000 + 5 * chan; case 129: /* center freqs 50, 114, 163; 160 MHz */ if (chan < 36 || chan > 177) return -1; return 5000 + 5 * chan; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */ case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */ case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */ case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */ if (chan < 1 || chan > 233) return -1; return 5950 + chan * 5; case 136: /* UHB channels, 20 MHz: 2 */ if (chan == 2) return 5935; return -1; case 180: /* 60 GHz band, channels 1..8 */ if (chan < 1 || chan > 8) return -1; return 56160 + 2160 * chan; case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */ if (chan < 9 || chan > 15) return -1; return 56160 + 2160 * (chan - 8); case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */ if (chan < 17 || chan > 22) return -1; return 56160 + 2160 * (chan - 16); case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */ if (chan < 25 || chan > 29) return -1; return 56160 + 2160 * (chan - 24); } return -1; } /** * ieee80211_chan_to_freq - Convert channel info to frequency * @country: Country code, if known; otherwise, global operating class is used * @op_class: Operating class * @chan: Channel number * Returns: Frequency in MHz or -1 if the specified channel is unknown */ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan) { int freq; if (country_match(us_op_class_cc, country)) { freq = ieee80211_chan_to_freq_us(op_class, chan); if (freq > 0) return freq; } if (country_match(eu_op_class_cc, country)) { freq = ieee80211_chan_to_freq_eu(op_class, chan); if (freq > 0) return freq; } if (country_match(jp_op_class_cc, country)) { freq = ieee80211_chan_to_freq_jp(op_class, chan); if (freq > 0) return freq; } if (country_match(cn_op_class_cc, country)) { freq = ieee80211_chan_to_freq_cn(op_class, chan); if (freq > 0) return freq; } return ieee80211_chan_to_freq_global(op_class, chan); } int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, u16 num_modes) { int i, j; if (!modes || !num_modes) return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700); for (i = 0; i < num_modes; i++) { for (j = 0; j < modes[i].num_channels; j++) { if (modes[i].channels[j].freq == freq && (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR)) return 1; } } return 0; } static int is_11b(u8 rate) { return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; } int supp_rates_11b_only(struct ieee802_11_elems *elems) { int num_11b = 0, num_others = 0; int i; if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) return 0; for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { if (is_11b(elems->supp_rates[i])) num_11b++; else num_others++; } for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; i++) { if (is_11b(elems->ext_supp_rates[i])) num_11b++; else num_others++; } return num_11b > 0 && num_others == 0; } const char * fc2str(u16 fc) { u16 stype = WLAN_FC_GET_STYPE(fc); #define C2S(x) case x: return #x; switch (WLAN_FC_GET_TYPE(fc)) { case WLAN_FC_TYPE_MGMT: switch (stype) { C2S(WLAN_FC_STYPE_ASSOC_REQ) C2S(WLAN_FC_STYPE_ASSOC_RESP) C2S(WLAN_FC_STYPE_REASSOC_REQ) C2S(WLAN_FC_STYPE_REASSOC_RESP) C2S(WLAN_FC_STYPE_PROBE_REQ) C2S(WLAN_FC_STYPE_PROBE_RESP) C2S(WLAN_FC_STYPE_BEACON) C2S(WLAN_FC_STYPE_ATIM) C2S(WLAN_FC_STYPE_DISASSOC) C2S(WLAN_FC_STYPE_AUTH) C2S(WLAN_FC_STYPE_DEAUTH) C2S(WLAN_FC_STYPE_ACTION) } break; case WLAN_FC_TYPE_CTRL: switch (stype) { C2S(WLAN_FC_STYPE_PSPOLL) C2S(WLAN_FC_STYPE_RTS) C2S(WLAN_FC_STYPE_CTS) C2S(WLAN_FC_STYPE_ACK) C2S(WLAN_FC_STYPE_CFEND) C2S(WLAN_FC_STYPE_CFENDACK) } break; case WLAN_FC_TYPE_DATA: switch (stype) { C2S(WLAN_FC_STYPE_DATA) C2S(WLAN_FC_STYPE_DATA_CFACK) C2S(WLAN_FC_STYPE_DATA_CFPOLL) C2S(WLAN_FC_STYPE_DATA_CFACKPOLL) C2S(WLAN_FC_STYPE_NULLFUNC) C2S(WLAN_FC_STYPE_CFACK) C2S(WLAN_FC_STYPE_CFPOLL) C2S(WLAN_FC_STYPE_CFACKPOLL) C2S(WLAN_FC_STYPE_QOS_DATA) C2S(WLAN_FC_STYPE_QOS_DATA_CFACK) C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL) C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL) C2S(WLAN_FC_STYPE_QOS_NULL) C2S(WLAN_FC_STYPE_QOS_CFPOLL) C2S(WLAN_FC_STYPE_QOS_CFACKPOLL) } break; } return "WLAN_FC_TYPE_UNKNOWN"; #undef C2S } const char * reason2str(u16 reason) { #define R2S(r) case WLAN_REASON_ ## r: return #r; switch (reason) { R2S(UNSPECIFIED) R2S(PREV_AUTH_NOT_VALID) R2S(DEAUTH_LEAVING) R2S(DISASSOC_DUE_TO_INACTIVITY) R2S(DISASSOC_AP_BUSY) R2S(CLASS2_FRAME_FROM_NONAUTH_STA) R2S(CLASS3_FRAME_FROM_NONASSOC_STA) R2S(DISASSOC_STA_HAS_LEFT) R2S(STA_REQ_ASSOC_WITHOUT_AUTH) R2S(PWR_CAPABILITY_NOT_VALID) R2S(SUPPORTED_CHANNEL_NOT_VALID) R2S(BSS_TRANSITION_DISASSOC) R2S(INVALID_IE) R2S(MICHAEL_MIC_FAILURE) R2S(4WAY_HANDSHAKE_TIMEOUT) R2S(GROUP_KEY_UPDATE_TIMEOUT) R2S(IE_IN_4WAY_DIFFERS) R2S(GROUP_CIPHER_NOT_VALID) R2S(PAIRWISE_CIPHER_NOT_VALID) R2S(AKMP_NOT_VALID) R2S(UNSUPPORTED_RSN_IE_VERSION) R2S(INVALID_RSN_IE_CAPAB) R2S(IEEE_802_1X_AUTH_FAILED) R2S(CIPHER_SUITE_REJECTED) R2S(TDLS_TEARDOWN_UNREACHABLE) R2S(TDLS_TEARDOWN_UNSPECIFIED) R2S(SSP_REQUESTED_DISASSOC) R2S(NO_SSP_ROAMING_AGREEMENT) R2S(BAD_CIPHER_OR_AKM) R2S(NOT_AUTHORIZED_THIS_LOCATION) R2S(SERVICE_CHANGE_PRECLUDES_TS) R2S(UNSPECIFIED_QOS_REASON) R2S(NOT_ENOUGH_BANDWIDTH) R2S(DISASSOC_LOW_ACK) R2S(EXCEEDED_TXOP) R2S(STA_LEAVING) R2S(END_TS_BA_DLS) R2S(UNKNOWN_TS_BA) R2S(TIMEOUT) R2S(PEERKEY_MISMATCH) R2S(AUTHORIZED_ACCESS_LIMIT_REACHED) R2S(EXTERNAL_SERVICE_REQUIREMENTS) R2S(INVALID_FT_ACTION_FRAME_COUNT) R2S(INVALID_PMKID) R2S(INVALID_MDE) R2S(INVALID_FTE) R2S(MESH_PEERING_CANCELLED) R2S(MESH_MAX_PEERS) R2S(MESH_CONFIG_POLICY_VIOLATION) R2S(MESH_CLOSE_RCVD) R2S(MESH_MAX_RETRIES) R2S(MESH_CONFIRM_TIMEOUT) R2S(MESH_INVALID_GTK) R2S(MESH_INCONSISTENT_PARAMS) R2S(MESH_INVALID_SECURITY_CAP) R2S(MESH_PATH_ERROR_NO_PROXY_INFO) R2S(MESH_PATH_ERROR_NO_FORWARDING_INFO) R2S(MESH_PATH_ERROR_DEST_UNREACHABLE) R2S(MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS) R2S(MESH_CHANNEL_SWITCH_REGULATORY_REQ) R2S(MESH_CHANNEL_SWITCH_UNSPECIFIED) } return "UNKNOWN"; #undef R2S } const char * status2str(u16 status) { #define S2S(s) case WLAN_STATUS_ ## s: return #s; switch (status) { S2S(SUCCESS) S2S(UNSPECIFIED_FAILURE) S2S(TDLS_WAKEUP_ALTERNATE) S2S(TDLS_WAKEUP_REJECT) S2S(SECURITY_DISABLED) S2S(UNACCEPTABLE_LIFETIME) S2S(NOT_IN_SAME_BSS) S2S(CAPS_UNSUPPORTED) S2S(REASSOC_NO_ASSOC) S2S(ASSOC_DENIED_UNSPEC) S2S(NOT_SUPPORTED_AUTH_ALG) S2S(UNKNOWN_AUTH_TRANSACTION) S2S(CHALLENGE_FAIL) S2S(AUTH_TIMEOUT) S2S(AP_UNABLE_TO_HANDLE_NEW_STA) S2S(ASSOC_DENIED_RATES) S2S(ASSOC_DENIED_NOSHORT) S2S(SPEC_MGMT_REQUIRED) S2S(PWR_CAPABILITY_NOT_VALID) S2S(SUPPORTED_CHANNEL_NOT_VALID) S2S(ASSOC_DENIED_NO_SHORT_SLOT_TIME) S2S(ASSOC_DENIED_NO_HT) S2S(R0KH_UNREACHABLE) S2S(ASSOC_DENIED_NO_PCO) S2S(ASSOC_REJECTED_TEMPORARILY) S2S(ROBUST_MGMT_FRAME_POLICY_VIOLATION) S2S(UNSPECIFIED_QOS_FAILURE) S2S(DENIED_INSUFFICIENT_BANDWIDTH) S2S(DENIED_POOR_CHANNEL_CONDITIONS) S2S(DENIED_QOS_NOT_SUPPORTED) S2S(REQUEST_DECLINED) S2S(INVALID_PARAMETERS) S2S(REJECTED_WITH_SUGGESTED_CHANGES) S2S(INVALID_IE) S2S(GROUP_CIPHER_NOT_VALID) S2S(PAIRWISE_CIPHER_NOT_VALID) S2S(AKMP_NOT_VALID) S2S(UNSUPPORTED_RSN_IE_VERSION) S2S(INVALID_RSN_IE_CAPAB) S2S(CIPHER_REJECTED_PER_POLICY) S2S(TS_NOT_CREATED) S2S(DIRECT_LINK_NOT_ALLOWED) S2S(DEST_STA_NOT_PRESENT) S2S(DEST_STA_NOT_QOS_STA) S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE) S2S(INVALID_FT_ACTION_FRAME_COUNT) S2S(INVALID_PMKID) S2S(INVALID_MDIE) S2S(INVALID_FTIE) S2S(REQUESTED_TCLAS_NOT_SUPPORTED) S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES) S2S(TRY_ANOTHER_BSS) S2S(GAS_ADV_PROTO_NOT_SUPPORTED) S2S(NO_OUTSTANDING_GAS_REQ) S2S(GAS_RESP_NOT_RECEIVED) S2S(STA_TIMED_OUT_WAITING_FOR_GAS_RESP) S2S(GAS_RESP_LARGER_THAN_LIMIT) S2S(REQ_REFUSED_HOME) S2S(ADV_SRV_UNREACHABLE) S2S(REQ_REFUSED_SSPN) S2S(REQ_REFUSED_UNAUTH_ACCESS) S2S(INVALID_RSNIE) S2S(U_APSD_COEX_NOT_SUPPORTED) S2S(U_APSD_COEX_MODE_NOT_SUPPORTED) S2S(BAD_INTERVAL_WITH_U_APSD_COEX) S2S(ANTI_CLOGGING_TOKEN_REQ) S2S(FINITE_CYCLIC_GROUP_NOT_SUPPORTED) S2S(CANNOT_FIND_ALT_TBTT) S2S(TRANSMISSION_FAILURE) S2S(REQ_TCLAS_NOT_SUPPORTED) S2S(TCLAS_RESOURCES_EXCHAUSTED) S2S(REJECTED_WITH_SUGGESTED_BSS_TRANSITION) S2S(REJECT_WITH_SCHEDULE) S2S(REJECT_NO_WAKEUP_SPECIFIED) S2S(SUCCESS_POWER_SAVE_MODE) S2S(PENDING_ADMITTING_FST_SESSION) S2S(PERFORMING_FST_NOW) S2S(PENDING_GAP_IN_BA_WINDOW) S2S(REJECT_U_PID_SETTING) S2S(REFUSED_EXTERNAL_REASON) S2S(REFUSED_AP_OUT_OF_MEMORY) S2S(REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED) S2S(QUERY_RESP_OUTSTANDING) S2S(REJECT_DSE_BAND) S2S(TCLAS_PROCESSING_TERMINATED) S2S(TS_SCHEDULE_CONFLICT) S2S(DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL) S2S(MCCAOP_RESERVATION_CONFLICT) S2S(MAF_LIMIT_EXCEEDED) S2S(MCCA_TRACK_LIMIT_EXCEEDED) S2S(DENIED_DUE_TO_SPECTRUM_MANAGEMENT) S2S(ASSOC_DENIED_NO_VHT) S2S(ENABLEMENT_DENIED) S2S(RESTRICTION_FROM_AUTHORIZED_GDB) S2S(AUTHORIZATION_DEENABLED) S2S(FILS_AUTHENTICATION_FAILURE) S2S(UNKNOWN_AUTHENTICATION_SERVER) S2S(UNKNOWN_PASSWORD_IDENTIFIER) S2S(DENIED_HE_NOT_SUPPORTED) S2S(SAE_HASH_TO_ELEMENT) S2S(SAE_PK) } return "UNKNOWN"; #undef S2S } int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, size_t ies_len) { const struct element *elem; os_memset(info, 0, sizeof(*info)); if (!ies_buf) return 0; for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) { if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED) return 0; wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", elem->datalen + 2); info->ies[info->nof_ies].ie = elem->data; info->ies[info->nof_ies].ie_len = elem->datalen; info->nof_ies++; } if (!for_each_element_completed(elem, ies_buf, ies_len)) { wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len); return -1; } return 0; } struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) { struct wpabuf *mb_ies = NULL; WPA_ASSERT(info != NULL); if (info->nof_ies) { u8 i; size_t mb_ies_size = 0; for (i = 0; i < info->nof_ies; i++) mb_ies_size += 2 + info->ies[i].ie_len; mb_ies = wpabuf_alloc(mb_ies_size); if (mb_ies) { for (i = 0; i < info->nof_ies; i++) { wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND); wpabuf_put_u8(mb_ies, info->ies[i].ie_len); wpabuf_put_data(mb_ies, info->ies[i].ie, info->ies[i].ie_len); } } } return mb_ies; } const struct oper_class_map global_op_class[] = { { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP }, /* Do not enable HT40 on 2.4 GHz for P2P use for now */ { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 125, 149, 177, 4, BW20, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 126, 149, 173, 8, BW40PLUS, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 127, 153, 177, 8, BW40MINUS, P2P_SUPP }, /* * IEEE P802.11ax/D8.0 Table E-4 actually talks about channel center * frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing * of 80 MHz, but currently use the following definition for simplicity * (these center frequencies are not actual channels, which makes * wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take * care of removing invalid channels. */ { HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP }, - { HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 134, 1, 233, 32, BW160, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 135, 1, 233, 16, BW80P80, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 136, 2, 2, 4, BW20, NO_P2P_SUPP }, /* * IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes. * Class 180 has the legacy channels 1-6. Classes 181-183 include * channels which implement channel bonding features. */ { HOSTAPD_MODE_IEEE80211AD, 180, 1, 6, 1, BW2160, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211AD, 181, 9, 13, 1, BW4320, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP }, /* Keep the operating class 130 as the last entry as a workaround for * the OneHundredAndThirty Delimiter value used in the Supported * Operating Classes element to indicate the end of the Operating * Classes field. */ { HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP }, { -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP } }; static enum phy_type ieee80211_phy_type_by_freq(int freq) { enum hostapd_hw_mode hw_mode; u8 channel; hw_mode = ieee80211_freq_to_chan(freq, &channel); switch (hw_mode) { case HOSTAPD_MODE_IEEE80211A: return PHY_TYPE_OFDM; case HOSTAPD_MODE_IEEE80211B: return PHY_TYPE_HRDSSS; case HOSTAPD_MODE_IEEE80211G: return PHY_TYPE_ERP; case HOSTAPD_MODE_IEEE80211AD: return PHY_TYPE_DMG; default: return PHY_TYPE_UNSPECIFIED; }; } /* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */ enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht) { if (vht) return PHY_TYPE_VHT; if (ht) return PHY_TYPE_HT; return ieee80211_phy_type_by_freq(freq); } size_t global_op_class_size = ARRAY_SIZE(global_op_class); /** * get_ie - Fetch a specified information element from IEs buffer * @ies: Information elements buffer * @len: Information elements buffer length * @eid: Information element identifier (WLAN_EID_*) * Returns: Pointer to the information element (id field) or %NULL if not found * * This function returns the first matching information element in the IEs * buffer or %NULL in case the element is not found. */ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) { const struct element *elem; if (!ies) return NULL; for_each_element_id(elem, eid, ies, len) return &elem->id; return NULL; } /** * get_ie_ext - Fetch a specified extended information element from IEs buffer * @ies: Information elements buffer * @len: Information elements buffer length * @ext: Information element extension identifier (WLAN_EID_EXT_*) * Returns: Pointer to the information element (id field) or %NULL if not found * * This function returns the first matching information element in the IEs * buffer or %NULL in case the element is not found. */ const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) { const struct element *elem; if (!ies) return NULL; for_each_element_extid(elem, ext, ies, len) return &elem->id; return NULL; } const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type) { const struct element *elem; for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) { if (elem->datalen >= 4 && vendor_type == WPA_GET_BE32(elem->data)) return &elem->id; } return NULL; } size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) { /* * MBO IE requires 6 bytes without the attributes: EID (1), length (1), * OUI (3), OUI type (1). */ if (len < 6 + attr_len) { wpa_printf(MSG_DEBUG, "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu", len, attr_len); return 0; } *buf++ = WLAN_EID_VENDOR_SPECIFIC; *buf++ = attr_len + 4; WPA_PUT_BE24(buf, OUI_WFA); buf += 3; *buf++ = MBO_OUI_TYPE; os_memcpy(buf, attr, attr_len); return 6 + attr_len; } size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value) { u8 *pos = buf; if (len < 9) return 0; *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 7; /* len */ WPA_PUT_BE24(pos, OUI_WFA); pos += 3; *pos++ = MULTI_AP_OUI_TYPE; *pos++ = MULTI_AP_SUB_ELEM_TYPE; *pos++ = 1; /* len */ *pos++ = value; return pos - buf; } static const struct country_op_class us_op_class[] = { { 1, 115 }, { 2, 118 }, { 3, 124 }, { 4, 121 }, { 5, 125 }, { 12, 81 }, { 22, 116 }, { 23, 119 }, { 24, 122 }, { 25, 126 }, { 26, 126 }, { 27, 117 }, { 28, 120 }, { 29, 123 }, { 30, 127 }, { 31, 127 }, { 32, 83 }, { 33, 84 }, { 34, 180 }, }; static const struct country_op_class eu_op_class[] = { { 1, 115 }, { 2, 118 }, { 3, 121 }, { 4, 81 }, { 5, 116 }, { 6, 119 }, { 7, 122 }, { 8, 117 }, { 9, 120 }, { 10, 123 }, { 11, 83 }, { 12, 84 }, { 17, 125 }, { 18, 180 }, }; static const struct country_op_class jp_op_class[] = { { 1, 115 }, { 30, 81 }, { 31, 82 }, { 32, 118 }, { 33, 118 }, { 34, 121 }, { 35, 121 }, { 36, 116 }, { 37, 119 }, { 38, 119 }, { 39, 122 }, { 40, 122 }, { 41, 117 }, { 42, 120 }, { 43, 120 }, { 44, 123 }, { 45, 123 }, { 56, 83 }, { 57, 84 }, { 58, 121 }, { 59, 180 }, }; static const struct country_op_class cn_op_class[] = { { 1, 115 }, { 2, 118 }, { 3, 125 }, { 4, 116 }, { 5, 119 }, { 6, 126 }, { 7, 81 }, { 8, 83 }, { 9, 84 }, }; static u8 global_op_class_from_country_array(u8 op_class, size_t array_size, const struct country_op_class *country_array) { size_t i; for (i = 0; i < array_size; i++) { if (country_array[i].country_op_class == op_class) return country_array[i].global_op_class; } return 0; } u8 country_to_global_op_class(const char *country, u8 op_class) { const struct country_op_class *country_array; size_t size; u8 g_op_class; if (country_match(us_op_class_cc, country)) { country_array = us_op_class; size = ARRAY_SIZE(us_op_class); } else if (country_match(eu_op_class_cc, country)) { country_array = eu_op_class; size = ARRAY_SIZE(eu_op_class); } else if (country_match(jp_op_class_cc, country)) { country_array = jp_op_class; size = ARRAY_SIZE(jp_op_class); } else if (country_match(cn_op_class_cc, country)) { country_array = cn_op_class; size = ARRAY_SIZE(cn_op_class); } else { /* * Countries that do not match any of the above countries use * global operating classes */ return op_class; } g_op_class = global_op_class_from_country_array(op_class, size, country_array); /* * If the given operating class did not match any of the country's * operating classes, assume that global operating class is used. */ return g_op_class ? g_op_class : op_class; } const struct oper_class_map * get_oper_class(const char *country, u8 op_class) { const struct oper_class_map *op; if (country) op_class = country_to_global_op_class(country, op_class); op = &global_op_class[0]; while (op->op_class && op->op_class != op_class) op++; if (!op->op_class) return NULL; return op; } int oper_class_bw_to_int(const struct oper_class_map *map) { switch (map->bw) { case BW20: return 20; case BW40: case BW40PLUS: case BW40MINUS: return 40; case BW80: return 80; case BW80P80: case BW160: return 160; case BW2160: return 2160; default: return 0; } } int center_idx_to_bw_6ghz(u8 idx) { /* Channel: 2 */ if (idx == 2) return 0; /* 20 MHz */ /* channels: 1, 5, 9, 13... */ if ((idx & 0x3) == 0x1) return 0; /* 20 MHz */ /* channels 3, 11, 19... */ if ((idx & 0x7) == 0x3) return 1; /* 40 MHz */ /* channels 7, 23, 39.. */ if ((idx & 0xf) == 0x7) return 2; /* 80 MHz */ /* channels 15, 47, 79...*/ if ((idx & 0x1f) == 0xf) return 3; /* 160 MHz */ return -1; } bool is_6ghz_freq(int freq) { if (freq < 5935 || freq > 7115) return false; if (freq == 5935) return true; if (center_idx_to_bw_6ghz((freq - 5950) / 5) < 0) return false; return true; } bool is_6ghz_op_class(u8 op_class) { return op_class >= 131 && op_class <= 136; } bool is_6ghz_psc_frequency(int freq) { int i; if (!is_6ghz_freq(freq) || freq == 5935) return false; if ((((freq - 5950) / 5) & 0x3) != 0x1) return false; i = (freq - 5950 + 55) % 80; if (i == 0) i = (freq - 5950 + 55) / 80; if (i >= 1 && i <= 15) return true; return false; } int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len) { u8 *nei_pos = nei_rep; const char *end; /* * BSS Transition Candidate List Entries - Neighbor Report elements * neighbor=,,, * ,[,] */ while (pos) { u8 *nei_start; long int val; char *endptr, *tmp; pos = os_strstr(pos, " neighbor="); if (!pos) break; if (nei_pos + 15 > nei_rep + nei_rep_len) { wpa_printf(MSG_DEBUG, "Not enough room for additional neighbor"); return -1; } pos += 10; nei_start = nei_pos; *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; nei_pos++; /* length to be filled in */ if (hwaddr_aton(pos, nei_pos)) { wpa_printf(MSG_DEBUG, "Invalid BSSID"); return -1; } nei_pos += ETH_ALEN; pos += 17; if (*pos != ',') { wpa_printf(MSG_DEBUG, "Missing BSSID Information"); return -1; } pos++; val = strtol(pos, &endptr, 0); WPA_PUT_LE32(nei_pos, val); nei_pos += 4; if (*endptr != ',') { wpa_printf(MSG_DEBUG, "Missing Operating Class"); return -1; } pos = endptr + 1; *nei_pos++ = atoi(pos); /* Operating Class */ pos = os_strchr(pos, ','); if (pos == NULL) { wpa_printf(MSG_DEBUG, "Missing Channel Number"); return -1; } pos++; *nei_pos++ = atoi(pos); /* Channel Number */ pos = os_strchr(pos, ','); if (pos == NULL) { wpa_printf(MSG_DEBUG, "Missing PHY Type"); return -1; } pos++; *nei_pos++ = atoi(pos); /* PHY Type */ end = os_strchr(pos, ' '); tmp = os_strchr(pos, ','); if (tmp && (!end || tmp < end)) { /* Optional Subelements (hexdump) */ size_t len; pos = tmp + 1; end = os_strchr(pos, ' '); if (end) len = end - pos; else len = os_strlen(pos); if (nei_pos + len / 2 > nei_rep + nei_rep_len) { wpa_printf(MSG_DEBUG, "Not enough room for neighbor subelements"); return -1; } if (len & 0x01 || hexstr2bin(pos, nei_pos, len / 2) < 0) { wpa_printf(MSG_DEBUG, "Invalid neighbor subelement info"); return -1; } nei_pos += len / 2; pos = end; } nei_start[1] = nei_pos - nei_start - 2; } return nei_pos - nei_rep; } int ieee802_11_ext_capab(const u8 *ie, unsigned int capab) { if (!ie || ie[1] <= capab / 8) return 0; return !!(ie[2 + capab / 8] & BIT(capab % 8)); } bool ieee802_11_rsnx_capab_len(const u8 *rsnxe, size_t rsnxe_len, unsigned int capab) { const u8 *end; size_t flen, i; u32 capabs = 0; if (!rsnxe || rsnxe_len == 0) return false; end = rsnxe + rsnxe_len; flen = (rsnxe[0] & 0x0f) + 1; if (rsnxe + flen > end) return false; if (flen > 4) flen = 4; for (i = 0; i < flen; i++) capabs |= rsnxe[i] << (8 * i); return capabs & BIT(capab); } bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab) { return ieee802_11_rsnx_capab_len(rsnxe ? rsnxe + 2 : NULL, rsnxe ? rsnxe[1] : 0, capab); } void hostapd_encode_edmg_chan(int edmg_enable, u8 edmg_channel, int primary_channel, struct ieee80211_edmg_config *edmg) { if (!edmg_enable) { edmg->channels = 0; edmg->bw_config = 0; return; } /* Only EDMG CB1 and EDMG CB2 contiguous channels supported for now */ switch (edmg_channel) { case EDMG_CHANNEL_9: edmg->channels = EDMG_CHANNEL_9_SUBCHANNELS; edmg->bw_config = EDMG_BW_CONFIG_5; return; case EDMG_CHANNEL_10: edmg->channels = EDMG_CHANNEL_10_SUBCHANNELS; edmg->bw_config = EDMG_BW_CONFIG_5; return; case EDMG_CHANNEL_11: edmg->channels = EDMG_CHANNEL_11_SUBCHANNELS; edmg->bw_config = EDMG_BW_CONFIG_5; return; case EDMG_CHANNEL_12: edmg->channels = EDMG_CHANNEL_12_SUBCHANNELS; edmg->bw_config = EDMG_BW_CONFIG_5; return; case EDMG_CHANNEL_13: edmg->channels = EDMG_CHANNEL_13_SUBCHANNELS; edmg->bw_config = EDMG_BW_CONFIG_5; return; default: if (primary_channel > 0 && primary_channel < 7) { edmg->channels = BIT(primary_channel - 1); edmg->bw_config = EDMG_BW_CONFIG_4; } else { edmg->channels = 0; edmg->bw_config = 0; } break; } } /* Check if the requested EDMG configuration is a subset of the allowed * EDMG configuration. */ int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed, struct ieee80211_edmg_config requested) { /* * The validation check if the requested EDMG configuration * is a subset of the allowed EDMG configuration: * 1. Check that the requested channels are part (set) of the allowed * channels. * 2. P802.11ay defines the values of bw_config between 4 and 15. * (bw config % 4) will give us 4 groups inside bw_config definition, * inside each group we can check the subset just by comparing the * bw_config value. * Between this 4 groups, there is no subset relation - as a result of * the P802.11ay definition. * bw_config defined by IEEE P802.11ay/D4.0, 9.4.2.251, Table 13. */ if (((requested.channels & allowed.channels) != requested.channels) || ((requested.bw_config % 4) > (allowed.bw_config % 4)) || requested.bw_config > allowed.bw_config) return 0; return 1; } int op_class_to_bandwidth(u8 op_class) { switch (op_class) { case 81: case 82: return 20; case 83: /* channels 1..9; 40 MHz */ case 84: /* channels 5..13; 40 MHz */ return 40; case 115: /* channels 36,40,44,48; indoor only */ return 20; case 116: /* channels 36,44; 40 MHz; indoor only */ case 117: /* channels 40,48; 40 MHz; indoor only */ return 40; case 118: /* channels 52,56,60,64; dfs */ return 20; case 119: /* channels 52,60; 40 MHz; dfs */ case 120: /* channels 56,64; 40 MHz; dfs */ return 40; case 121: /* channels 100-140 */ return 20; case 122: /* channels 100-142; 40 MHz */ case 123: /* channels 104-136; 40 MHz */ return 40; case 124: /* channels 149,153,157,161 */ case 125: /* channels 149,153,157,161,165,169,173,177 */ return 20; case 126: /* channels 149,157,161,165,169,173; 40 MHz */ case 127: /* channels 153..177; 40 MHz */ return 40; case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ return 80; case 129: /* center freqs 50, 114, 163; 160 MHz */ return 160; case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */ return 80; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ return 20; case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */ return 40; case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */ return 80; case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */ case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */ return 160; case 136: /* UHB channels, 20 MHz: 2 */ return 20; case 180: /* 60 GHz band, channels 1..8 */ return 2160; case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */ return 4320; case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */ return 6480; case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */ return 8640; } return 20; } int op_class_to_ch_width(u8 op_class) { switch (op_class) { case 81: case 82: return CHANWIDTH_USE_HT; case 83: /* channels 1..9; 40 MHz */ case 84: /* channels 5..13; 40 MHz */ return CHANWIDTH_USE_HT; case 115: /* channels 36,40,44,48; indoor only */ return CHANWIDTH_USE_HT; case 116: /* channels 36,44; 40 MHz; indoor only */ case 117: /* channels 40,48; 40 MHz; indoor only */ return CHANWIDTH_USE_HT; case 118: /* channels 52,56,60,64; dfs */ return CHANWIDTH_USE_HT; case 119: /* channels 52,60; 40 MHz; dfs */ case 120: /* channels 56,64; 40 MHz; dfs */ return CHANWIDTH_USE_HT; case 121: /* channels 100-140 */ return CHANWIDTH_USE_HT; case 122: /* channels 100-142; 40 MHz */ case 123: /* channels 104-136; 40 MHz */ return CHANWIDTH_USE_HT; case 124: /* channels 149,153,157,161 */ case 125: /* channels 149,153,157,161,165,169,171 */ return CHANWIDTH_USE_HT; case 126: /* channels 149,157,165, 173; 40 MHz */ case 127: /* channels 153,161,169,177; 40 MHz */ return CHANWIDTH_USE_HT; case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ return CHANWIDTH_80MHZ; case 129: /* center freqs 50, 114, 163; 160 MHz */ return CHANWIDTH_160MHZ; case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */ return CHANWIDTH_80P80MHZ; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ return CHANWIDTH_USE_HT; case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */ return CHANWIDTH_USE_HT; case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */ return CHANWIDTH_80MHZ; case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */ return CHANWIDTH_160MHZ; case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */ return CHANWIDTH_80P80MHZ; case 136: /* UHB channels, 20 MHz: 2 */ return CHANWIDTH_USE_HT; case 180: /* 60 GHz band, channels 1..8 */ return CHANWIDTH_2160MHZ; case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */ return CHANWIDTH_4320MHZ; case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */ return CHANWIDTH_6480MHZ; case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */ return CHANWIDTH_8640MHZ; } return CHANWIDTH_USE_HT; } struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems, u8 eid, u8 eid_ext, const u8 *data, u8 len) { struct frag_ies_info *frag_ies = &elems->frag_ies; struct wpabuf *buf; unsigned int i; if (!elems || !data || !len) return NULL; buf = wpabuf_alloc_copy(data, len); if (!buf) return NULL; for (i = 0; i < frag_ies->n_frags; i++) { int ret; if (frag_ies->frags[i].eid != eid || frag_ies->frags[i].eid_ext != eid_ext) continue; ret = wpabuf_resize(&buf, frag_ies->frags[i].ie_len); if (ret < 0) { wpabuf_free(buf); return NULL; } /* Copy only the fragment data (without the EID and length) */ wpabuf_put_data(buf, frag_ies->frags[i].ie, frag_ies->frags[i].ie_len); } return buf; } struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems, u8 eid, u8 eid_ext) { const u8 *data; u8 len; /* * TODO: Defragmentation mechanism can be supported for all IEs. For now * handle only those that are used (or use ieee802_11_defrag_data()). */ switch (eid) { case WLAN_EID_EXTENSION: switch (eid_ext) { case WLAN_EID_EXT_FILS_HLP_CONTAINER: data = elems->fils_hlp; len = elems->fils_hlp_len; break; case WLAN_EID_EXT_WRAPPED_DATA: data = elems->wrapped_data; len = elems->wrapped_data_len; break; default: wpa_printf(MSG_DEBUG, "Defragmentation not supported. eid_ext=%u", eid_ext); return NULL; } break; default: wpa_printf(MSG_DEBUG, "Defragmentation not supported. eid=%u", eid); return NULL; } return ieee802_11_defrag_data(elems, eid, eid_ext, data, len); } diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 7d2f36b8f1e6..519a13b1d064 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1,2447 +1,2448 @@ /* * IEEE 802.11 Frame type definitions * Copyright (c) 2002-2019, Jouni Malinen * Copyright (c) 2007-2008 Intel Corporation * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef IEEE802_11_DEFS_H #define IEEE802_11_DEFS_H #include /* IEEE 802.11 defines */ #define WLAN_FC_PVER 0x0003 #define WLAN_FC_TODS 0x0100 #define WLAN_FC_FROMDS 0x0200 #define WLAN_FC_MOREFRAG 0x0400 #define WLAN_FC_RETRY 0x0800 #define WLAN_FC_PWRMGT 0x1000 #define WLAN_FC_MOREDATA 0x2000 #define WLAN_FC_ISWEP 0x4000 #define WLAN_FC_HTC 0x8000 #define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) #define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) #define WLAN_INVALID_MGMT_SEQ 0xFFFF #define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) #define WLAN_GET_SEQ_SEQ(seq) \ (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) #define WLAN_FC_TYPE_MGMT 0 #define WLAN_FC_TYPE_CTRL 1 #define WLAN_FC_TYPE_DATA 2 /* management */ #define WLAN_FC_STYPE_ASSOC_REQ 0 #define WLAN_FC_STYPE_ASSOC_RESP 1 #define WLAN_FC_STYPE_REASSOC_REQ 2 #define WLAN_FC_STYPE_REASSOC_RESP 3 #define WLAN_FC_STYPE_PROBE_REQ 4 #define WLAN_FC_STYPE_PROBE_RESP 5 #define WLAN_FC_STYPE_BEACON 8 #define WLAN_FC_STYPE_ATIM 9 #define WLAN_FC_STYPE_DISASSOC 10 #define WLAN_FC_STYPE_AUTH 11 #define WLAN_FC_STYPE_DEAUTH 12 #define WLAN_FC_STYPE_ACTION 13 #define WLAN_FC_STYPE_ACTION_NO_ACK 14 /* control */ #define WLAN_FC_STYPE_PSPOLL 10 #define WLAN_FC_STYPE_RTS 11 #define WLAN_FC_STYPE_CTS 12 #define WLAN_FC_STYPE_ACK 13 #define WLAN_FC_STYPE_CFEND 14 #define WLAN_FC_STYPE_CFENDACK 15 /* data */ #define WLAN_FC_STYPE_DATA 0 #define WLAN_FC_STYPE_DATA_CFACK 1 #define WLAN_FC_STYPE_DATA_CFPOLL 2 #define WLAN_FC_STYPE_DATA_CFACKPOLL 3 #define WLAN_FC_STYPE_NULLFUNC 4 #define WLAN_FC_STYPE_CFACK 5 #define WLAN_FC_STYPE_CFPOLL 6 #define WLAN_FC_STYPE_CFACKPOLL 7 #define WLAN_FC_STYPE_QOS_DATA 8 #define WLAN_FC_STYPE_QOS_DATA_CFACK 9 #define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 #define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 #define WLAN_FC_STYPE_QOS_NULL 12 #define WLAN_FC_STYPE_QOS_CFPOLL 14 #define WLAN_FC_STYPE_QOS_CFACKPOLL 15 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 #define WLAN_AUTH_FILS_SK 4 #define WLAN_AUTH_FILS_SK_PFS 5 #define WLAN_AUTH_FILS_PK 6 #define WLAN_AUTH_PASN 7 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 #define WLAN_CAPABILITY_ESS BIT(0) #define WLAN_CAPABILITY_IBSS BIT(1) #define WLAN_CAPABILITY_CF_POLLABLE BIT(2) #define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) #define WLAN_CAPABILITY_PRIVACY BIT(4) #define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) #define WLAN_CAPABILITY_PBCC BIT(6) #define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) #define WLAN_CAPABILITY_QOS BIT(9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) #define WLAN_CAPABILITY_APSD BIT(11) #define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12) #define WLAN_CAPABILITY_DSSS_OFDM BIT(13) #define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14) #define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15) /* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 #define WLAN_STATUS_TDLS_WAKEUP_REJECT 3 #define WLAN_STATUS_SECURITY_DISABLED 5 #define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6 #define WLAN_STATUS_NOT_IN_SAME_BSS 7 #define WLAN_STATUS_CAPS_UNSUPPORTED 10 #define WLAN_STATUS_REASSOC_NO_ASSOC 11 #define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 #define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 #define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 #define WLAN_STATUS_CHALLENGE_FAIL 15 #define WLAN_STATUS_AUTH_TIMEOUT 16 #define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 #define WLAN_STATUS_ASSOC_DENIED_RATES 18 #define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 #define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 #define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 #define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 #define WLAN_STATUS_R0KH_UNREACHABLE 28 #define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 #define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 #define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33 #define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34 #define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35 #define WLAN_STATUS_REQUEST_DECLINED 37 #define WLAN_STATUS_INVALID_PARAMETERS 38 #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39 #define WLAN_STATUS_INVALID_IE 40 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 #define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 #define WLAN_STATUS_AKMP_NOT_VALID 43 #define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 #define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 #define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 #define WLAN_STATUS_TS_NOT_CREATED 47 #define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 #define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 #define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56 #define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57 #define WLAN_STATUS_TRY_ANOTHER_BSS 58 #define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 #define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 #define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 #define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62 #define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63 #define WLAN_STATUS_REQ_REFUSED_HOME 64 #define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 #define WLAN_STATUS_REQ_REFUSED_SSPN 67 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 #define WLAN_STATUS_INVALID_RSNIE 72 #define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73 #define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74 #define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 #define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 #define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80 #define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81 #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 #define WLAN_STATUS_REJECT_WITH_SCHEDULE 83 #define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84 #define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85 #define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 #define WLAN_STATUS_PERFORMING_FST_NOW 87 #define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88 #define WLAN_STATUS_REJECT_U_PID_SETTING 89 #define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92 #define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93 #define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 #define WLAN_STATUS_REJECT_DSE_BAND 96 #define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97 #define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98 #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 #define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100 #define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101 #define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102 #define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 #define WLAN_STATUS_ENABLEMENT_DENIED 105 #define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106 #define WLAN_STATUS_AUTHORIZATION_DEENABLED 107 #define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112 #define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113 #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123 #define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124 #define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126 #define WLAN_STATUS_SAE_PK 127 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */ #define WLAN_REASON_UNSPECIFIED 1 #define WLAN_REASON_PREV_AUTH_NOT_VALID 2 #define WLAN_REASON_DEAUTH_LEAVING 3 #define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 #define WLAN_REASON_DISASSOC_AP_BUSY 5 #define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 #define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 #define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 #define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 #define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 #define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 #define WLAN_REASON_BSS_TRANSITION_DISASSOC 12 #define WLAN_REASON_INVALID_IE 13 #define WLAN_REASON_MICHAEL_MIC_FAILURE 14 #define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 #define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 #define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 #define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 #define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 #define WLAN_REASON_AKMP_NOT_VALID 20 #define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 #define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 #define WLAN_REASON_SSP_REQUESTED_DISASSOC 27 #define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28 #define WLAN_REASON_BAD_CIPHER_OR_AKM 29 #define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30 #define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31 #define WLAN_REASON_UNSPECIFIED_QOS_REASON 32 #define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33 #define WLAN_REASON_DISASSOC_LOW_ACK 34 #define WLAN_REASON_EXCEEDED_TXOP 35 #define WLAN_REASON_STA_LEAVING 36 #define WLAN_REASON_END_TS_BA_DLS 37 #define WLAN_REASON_UNKNOWN_TS_BA 38 #define WLAN_REASON_TIMEOUT 39 #define WLAN_REASON_PEERKEY_MISMATCH 45 #define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46 #define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47 #define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48 #define WLAN_REASON_INVALID_PMKID 49 #define WLAN_REASON_INVALID_MDE 50 #define WLAN_REASON_INVALID_FTE 51 #define WLAN_REASON_MESH_PEERING_CANCELLED 52 #define WLAN_REASON_MESH_MAX_PEERS 53 #define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54 #define WLAN_REASON_MESH_CLOSE_RCVD 55 #define WLAN_REASON_MESH_MAX_RETRIES 56 #define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57 #define WLAN_REASON_MESH_INVALID_GTK 58 #define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59 #define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60 #define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61 #define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62 #define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63 #define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64 #define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65 #define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66 /* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */ #define WLAN_EID_SSID 0 #define WLAN_EID_SUPP_RATES 1 #define WLAN_EID_DS_PARAMS 3 #define WLAN_EID_CF_PARAMS 4 #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 #define WLAN_EID_REQUEST 10 #define WLAN_EID_BSS_LOAD 11 #define WLAN_EID_EDCA_PARAM_SET 12 #define WLAN_EID_TSPEC 13 #define WLAN_EID_TCLAS 14 #define WLAN_EID_SCHEDULE 15 #define WLAN_EID_CHALLENGE 16 #define WLAN_EID_PWR_CONSTRAINT 32 #define WLAN_EID_PWR_CAPABILITY 33 #define WLAN_EID_TPC_REQUEST 34 #define WLAN_EID_TPC_REPORT 35 #define WLAN_EID_SUPPORTED_CHANNELS 36 #define WLAN_EID_CHANNEL_SWITCH 37 #define WLAN_EID_MEASURE_REQUEST 38 #define WLAN_EID_MEASURE_REPORT 39 #define WLAN_EID_QUIET 40 #define WLAN_EID_IBSS_DFS 41 #define WLAN_EID_ERP_INFO 42 #define WLAN_EID_TS_DELAY 43 #define WLAN_EID_TCLAS_PROCESSING 44 #define WLAN_EID_HT_CAP 45 #define WLAN_EID_QOS 46 #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_SUPP_RATES 50 #define WLAN_EID_AP_CHANNEL_REPORT 51 #define WLAN_EID_NEIGHBOR_REPORT 52 #define WLAN_EID_RCPI 53 #define WLAN_EID_MOBILITY_DOMAIN 54 #define WLAN_EID_FAST_BSS_TRANSITION 55 #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 #define WLAN_EID_DSE_REGISTERED_LOCATION 58 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 #define WLAN_EID_EXT_CHANSWITCH_ANN 60 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 #define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63 #define WLAN_EID_ANTENNA 64 #define WLAN_EID_RSNI 65 #define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66 #define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67 #define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */ #define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_RRM_ENABLED_CAPABILITIES 70 #define WLAN_EID_MULTIPLE_BSSID 71 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_RIC_DESCRIPTOR 75 #define WLAN_EID_MMIE 76 #define WLAN_EID_EVENT_REQUEST 78 #define WLAN_EID_EVENT_REPORT 79 #define WLAN_EID_DIAGNOSTIC_REQUEST 80 #define WLAN_EID_DIAGNOSTIC_REPORT 81 #define WLAN_EID_LOCATION_PARAMETERS 82 #define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83 #define WLAN_EID_SSID_LIST 84 #define WLAN_EID_MULTIPLE_BSSID_INDEX 85 #define WLAN_EID_FMS_DESCRIPTOR 86 #define WLAN_EID_FMS_REQUEST 87 #define WLAN_EID_FMS_RESPONSE 88 #define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 #define WLAN_EID_TFS_REQ 91 #define WLAN_EID_TFS_RESP 92 #define WLAN_EID_WNMSLEEP 93 #define WLAN_EID_TIM_BROADCAST_REQUEST 94 #define WLAN_EID_TIM_BROADCAST_RESPONSE 95 #define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96 #define WLAN_EID_CHANNEL_USAGE 97 #define WLAN_EID_TIME_ZONE 98 #define WLAN_EID_DMS_REQUEST 99 #define WLAN_EID_DMS_RESPONSE 100 #define WLAN_EID_LINK_ID 101 #define WLAN_EID_WAKEUP_SCHEDULE 102 #define WLAN_EID_CHANNEL_SWITCH_TIMING 104 #define WLAN_EID_PTI_CONTROL 105 #define WLAN_EID_TPU_BUFFER_STATUS 106 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 #define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109 #define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 #define WLAN_EID_EMERGENCY_ALERT_ID 112 #define WLAN_EID_MESH_CONFIG 113 #define WLAN_EID_MESH_ID 114 #define WLAN_EID_MESH_LINK_METRIC_REPORT 115 #define WLAN_EID_CONGESTION_NOTIFICATION 116 #define WLAN_EID_PEER_MGMT 117 #define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118 #define WLAN_EID_MESH_AWAKE_WINDOW 119 #define WLAN_EID_BEACON_TIMING 120 #define WLAN_EID_MCCAOP_SETUP_REQUEST 121 #define WLAN_EID_MCCAOP_SETUP_REPLY 122 #define WLAN_EID_MCCAOP_ADVERTISEMENT 123 #define WLAN_EID_MCCAOP_TEARDOWN 124 #define WLAN_EID_GANN 125 #define WLAN_EID_RANN 126 #define WLAN_EID_EXT_CAPAB 127 #define WLAN_EID_PREQ 130 #define WLAN_EID_PREP 131 #define WLAN_EID_PERR 132 #define WLAN_EID_PXU 137 #define WLAN_EID_PXUC 138 #define WLAN_EID_AMPE 139 #define WLAN_EID_MIC 140 #define WLAN_EID_DESTINATION_URI 141 #define WLAN_EID_U_APSD_COEX 142 #define WLAN_EID_DMG_WAKEUP_SCHEDULE 143 #define WLAN_EID_EXTENDED_SCHEDULE 144 #define WLAN_EID_STA_AVAILABILITY 145 #define WLAN_EID_DMG_TSPEC 146 #define WLAN_EID_NEXT_DMG_ATI 147 #define WLAN_EID_DMG_CAPABILITIES 148 #define WLAN_EID_DMG_OPERATION 151 #define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152 #define WLAN_EID_DMG_BEAM_REFINEMENT 153 #define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154 #define WLAN_EID_CCKM 156 #define WLAN_EID_AWAKE_WINDOW 157 #define WLAN_EID_MULTI_BAND 158 #define WLAN_EID_ADDBA_EXTENSION 159 #define WLAN_EID_NEXTPCP_LIST 160 #define WLAN_EID_PCP_HANDOVER 161 #define WLAN_EID_DMG_LINK_MARGIN 162 #define WLAN_EID_SWITCHING_STREAM 163 #define WLAN_EID_SESSION_TRANSITION 164 #define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165 #define WLAN_EID_CLUSTER_REPORT 166 #define WLAN_EID_REPLAY_CAPABILITIES 167 #define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168 #define WLAN_EID_BEAMLINK_MAINTENANCE 169 #define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170 #define WLAN_EID_U_PID 171 #define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172 #define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174 #define WLAN_EID_QUIET_PERIOD_REQUEST 175 #define WLAN_EID_QUIET_PERIOD_RESPONSE 177 #define WLAN_EID_QMF_POLICY 181 #define WLAN_EID_ECAPC_POLICY 182 #define WLAN_EID_CLUSTER_TIME_OFFSET 183 #define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184 #define WLAN_EID_SCS_DESCRIPTOR 185 #define WLAN_EID_QLOAD_REPORT 186 #define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187 #define WLAN_EID_HIGHER_LAYER_STREAM_ID 188 #define WLAN_EID_GCR_GROUP_ADDRESS 189 #define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 #define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 #define WLAN_EID_TRANSMIT_POWER_ENVELOPE 195 #define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 #define WLAN_EID_VHT_AID 197 #define WLAN_EID_VHT_QUIET_CHANNEL 198 #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 #define WLAN_EID_UPSIM 200 #define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201 #define WLAN_EID_TVHT_OPERATION 202 #define WLAN_EID_DEVICE_LOCATION 204 #define WLAN_EID_WHITE_SPACE_MAP 205 #define WLAN_EID_FTM_PARAMETERS 206 #define WLAN_EID_S1G_BCN_COMPAT 213 #define WLAN_EID_TWT 216 #define WLAN_EID_S1G_CAPABILITIES 217 #define WLAN_EID_VENDOR_SPECIFIC 221 #define WLAN_EID_S1G_OPERATION 232 #define WLAN_EID_CAG_NUMBER 237 #define WLAN_EID_AP_CSN 239 #define WLAN_EID_FILS_INDICATION 240 #define WLAN_EID_DILS 241 #define WLAN_EID_FRAGMENT 242 #define WLAN_EID_RSNX 244 #define WLAN_EID_EXTENSION 255 /* Element ID Extension (EID 255) values */ #define WLAN_EID_EXT_ASSOC_DELAY_INFO 1 #define WLAN_EID_EXT_FILS_REQ_PARAMS 2 #define WLAN_EID_EXT_FILS_KEY_CONFIRM 3 #define WLAN_EID_EXT_FILS_SESSION 4 #define WLAN_EID_EXT_FILS_HLP_CONTAINER 5 #define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6 #define WLAN_EID_EXT_KEY_DELIVERY 7 #define WLAN_EID_EXT_WRAPPED_DATA 8 #define WLAN_EID_EXT_FTM_SYNC_INFO 9 #define WLAN_EID_EXT_EXTENDED_REQUEST 10 #define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11 #define WLAN_EID_EXT_FILS_PUBLIC_KEY 12 #define WLAN_EID_EXT_FILS_NONCE 13 #define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14 #define WLAN_EID_EXT_OWE_DH_PARAM 32 #define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 #define WLAN_EID_EXT_HE_CAPABILITIES 35 #define WLAN_EID_EXT_HE_OPERATION 36 #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38 #define WLAN_EID_EXT_SPATIAL_REUSE 39 #define WLAN_EID_EXT_OCV_OCI 54 #define WLAN_EID_EXT_SHORT_SSID_LIST 58 #define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59 #define WLAN_EID_EXT_EDMG_CAPABILITIES 61 #define WLAN_EID_EXT_EDMG_OPERATION 62 #define WLAN_EID_EXT_MSCS_DESCRIPTOR 88 #define WLAN_EID_EXT_TCLAS_MASK 89 #define WLAN_EID_EXT_REJECTED_GROUPS 92 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93 #define WLAN_EID_EXT_PASN_PARAMS 100 /* Extended Capabilities field */ #define WLAN_EXT_CAPAB_20_40_COEX 0 #define WLAN_EXT_CAPAB_GLK 1 #define WLAN_EXT_CAPAB_EXT_CHAN_SWITCH 2 #define WLAN_EXT_CAPAB_GLK_GCR 3 #define WLAN_EXT_CAPAB_PSMP 4 /* 5 - Reserved */ #define WLAN_EXT_CAPAB_S_PSMP 6 #define WLAN_EXT_CAPAB_EVENT 7 #define WLAN_EXT_CAPAB_DIAGNOSTICS 8 #define WLAN_EXT_CAPAB_MULTICAST_DIAGNOSTICS 9 #define WLAN_EXT_CAPAB_LOCATION_TRACKING 10 #define WLAN_EXT_CAPAB_FMS 11 #define WLAN_EXT_CAPAB_PROXY_ARP 12 #define WLAN_EXT_CAPAB_COLL_INTERF_REP 13 #define WLAN_EXT_CAPAB_CIVIC_LOCATION 14 #define WLAN_EXT_CAPAB_GEOSPATIAL_LOCATION 15 #define WLAN_EXT_CAPAB_TFS 16 #define WLAN_EXT_CAPAB_WNM_SLEEP_MODE 17 #define WLAN_EXT_CAPAB_TIM_BROADCAST 18 #define WLAN_EXT_CAPAB_BSS_TRANSITION 19 #define WLAN_EXT_CAPAB_QOS_TRAFFIC 20 #define WLAN_EXT_CAPAB_AC_STA_COUNT 21 #define WLAN_EXT_CAPAB_MULTIPLE_BSSID 22 #define WLAN_EXT_CAPAB_TIMING_MEASUREMENT 23 #define WLAN_EXT_CAPAB_CHANNEL_USAGE 24 #define WLAN_EXT_CAPAB_SSID_LIST 25 #define WLAN_EXT_CAPAB_DMS 26 #define WLAN_EXT_CAPAB_UTF_TSF_OFFSET 27 #define WLAN_EXT_CAPAB_TPU_BUFFER_STA 28 #define WLAN_EXT_CAPAB_TDLS_PEER_PSM 29 #define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH 30 #define WLAN_EXT_CAPAB_INTERWORKING 31 #define WLAN_EXT_CAPAB_QOS_MAP 32 #define WLAN_EXT_CAPAB_EBR 33 #define WLAN_EXT_CAPAB_SSPN_INTERFACE 34 /* 35 - Reserved */ #define WLAN_EXT_CAPAB_MSGCF 36 #define WLAN_EXT_CAPAB_TDLS 37 #define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38 #define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39 #define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40 /* 41-43 - Service Interval Granularity */ #define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44 #define WLAN_EXT_CAPAB_U_APSD_COEX 45 #define WLAN_EXT_CAPAB_WNM_NOTIFCATION 46 #define WLAN_EXT_CAPAB_QAB 47 #define WLAN_EXT_CAPAB_UTF_8_SSID 48 #define WLAN_EXT_CAPAB_QMF 49 #define WLAN_EXT_CAPAB_QMF_RECONFIG 50 #define WLAN_EXT_CAPAB_ROBUST_AV_STREAMING 51 #define WLAN_EXT_CAPAB_ADVANCED_GCR 52 #define WLAN_EXT_CAPAB_MESH_GCR 53 #define WLAN_EXT_CAPAB_SCS 54 #define WLAN_EXT_CAPAB_QLOAD_REPORT 55 #define WLAN_EXT_CAPAB_ALT_EDCA 56 #define WLAN_EXT_CAPAB_UNPROT_TXOP_NEG 57 #define WLAN_EXT_CAPAB_PROT_TXOP_NEG 58 /* 59 - Reserved */ #define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60 #define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61 #define WLAN_EXT_CAPAB_OPMODE_NOTIF 62 /* 63-64 - Max Number of MSDUs In A-MSDU */ #define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65 #define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66 #define WLAN_EXT_CAPAB_NETWORK_CHANNEL_CTRL 67 #define WLAN_EXT_CAPAB_WHITE_SPACE_MAP 68 #define WLAN_EXT_CAPAB_CHANNEL_AVAIL_QUERY 69 #define WLAN_EXT_CAPAB_FTM_RESPONDER 70 #define WLAN_EXT_CAPAB_FTM_INITIATOR 71 #define WLAN_EXT_CAPAB_FILS 72 #define WLAN_EXT_CAPAB_EXT_SPECTRUM_MGMT 73 #define WLAN_EXT_CAPAB_FUTURE_CHANNEL_GUIDANCE 74 #define WLAN_EXT_CAPAB_PAD 75 /* 76-79 - Reserved */ #define WLAN_EXT_CAPAB_COMPLETE_NON_TX_BSSID_PROFILE 80 #define WLAN_EXT_CAPAB_SAE_PW_ID 81 #define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82 #define WLAN_EXT_CAPAB_BEACON_PROTECTION 84 #define WLAN_EXT_CAPAB_MSCS 85 #define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88 /* Extended RSN Capabilities */ /* bits 0-3: Field length (n-1) */ #define WLAN_RSNX_CAPAB_PROTECTED_TWT 4 #define WLAN_RSNX_CAPAB_SAE_H2E 5 #define WLAN_RSNX_CAPAB_SAE_PK 6 #define WLAN_RSNX_CAPAB_SECURE_LTF 8 #define WLAN_RSNX_CAPAB_SECURE_RTT 9 #define WLAN_RSNX_CAPAB_PROT_RANGE_NEG 10 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 #define WLAN_ACTION_QOS 1 #define WLAN_ACTION_DLS 2 #define WLAN_ACTION_BLOCK_ACK 3 #define WLAN_ACTION_PUBLIC 4 #define WLAN_ACTION_RADIO_MEASUREMENT 5 #define WLAN_ACTION_FT 6 #define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 #define WLAN_ACTION_PROTECTED_DUAL 9 #define WLAN_ACTION_WNM 10 #define WLAN_ACTION_UNPROTECTED_WNM 11 #define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_MESH 13 #define WLAN_ACTION_MULTIHOP 14 #define WLAN_ACTION_SELF_PROTECTED 15 #define WLAN_ACTION_DMG 16 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ #define WLAN_ACTION_FST 18 #define WLAN_ACTION_ROBUST_AV_STREAMING 19 #define WLAN_ACTION_UNPROTECTED_DMG 20 #define WLAN_ACTION_VHT 21 #define WLAN_ACTION_S1G 22 #define WLAN_ACTION_S1G_RELAY 23 #define WLAN_ACTION_FLOW_CONTROL 24 #define WLAN_ACTION_CTRL_RESP_MCS_NEG 25 #define WLAN_ACTION_FILS 26 #define WLAN_ACTION_PROTECTED_FTM 34 #define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126 #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Note: 128-255 used to report errors by setting category | 0x80 */ /* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */ #define WLAN_PA_20_40_BSS_COEX 0 #define WLAN_PA_DSE_ENABLEMENT 1 #define WLAN_PA_DSE_DEENABLEMENT 2 #define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3 #define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4 #define WLAN_PA_DSE_MEASUREMENT_REQ 5 #define WLAN_PA_DSE_MEASUREMENT_RESP 6 #define WLAN_PA_MEASUREMENT_PILOT 7 #define WLAN_PA_DSE_POWER_CONSTRAINT 8 #define WLAN_PA_VENDOR_SPECIFIC 9 #define WLAN_PA_GAS_INITIAL_REQ 10 #define WLAN_PA_GAS_INITIAL_RESP 11 #define WLAN_PA_GAS_COMEBACK_REQ 12 #define WLAN_PA_GAS_COMEBACK_RESP 13 #define WLAN_TDLS_DISCOVERY_RESPONSE 14 #define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15 #define WLAN_PA_QAB_REQUEST_FRAME 16 #define WLAN_PA_QAB_RESPONSE_FRAME 17 #define WLAN_PA_QMF_POLICY 18 #define WLAN_PA_QMF_POLICY_CHANGE 19 #define WLAN_PA_QLOAD_REQUEST 20 #define WLAN_PA_QLOAD_REPORT 21 #define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22 #define WLAN_PA_HCCA_TXOP_RESPONSE 23 #define WLAN_PA_PUBLIC_KEY 24 #define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25 #define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26 #define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27 #define WLAN_PA_GDD_ENABLEMENT_REQ 28 #define WLAN_PA_GDD_ENABLEMENT_RESP 29 #define WLAN_PA_NETWORK_CHANNEL_CONTROL 30 #define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31 #define WLAN_PA_FTM_REQUEST 32 #define WLAN_PA_FTM 33 #define WLAN_PA_FILS_DISCOVERY 34 #define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47 /* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11, * Table 9-332) */ #define WLAN_PROT_DSE_ENABLEMENT 1 #define WLAN_PROT_DSE_DEENABLEMENT 2 #define WLAN_PROT_EXT_CSA 4 #define WLAN_PROT_MEASUREMENT_REQ 5 #define WLAN_PROT_MEASUREMENT_REPORT 6 #define WLAN_PROT_DSE_POWER_CONSTRAINT 8 #define WLAN_PROT_VENDOR_SPECIFIC 9 #define WLAN_PROT_GAS_INITIAL_REQ 10 #define WLAN_PROT_GAS_INITIAL_RESP 11 #define WLAN_PROT_GAS_COMEBACK_REQ 12 #define WLAN_PROT_GAS_COMEBACK_RESP 13 #define WLAN_PROT_QAB_REQUEST_FRAME 16 #define WLAN_PROT_QAB_RESPONSE_FRAME 17 #define WLAN_PROT_QMF_POLICY 18 #define WLAN_PROT_QMF_POLICY_CHANGE 19 #define WLAN_PROT_QLOAD_REQUEST 20 #define WLAN_PROT_QLOAD_REPORT 21 #define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22 #define WLAN_PROT_HCCA_TXOP_RESPONSE 23 #define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25 #define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26 #define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27 #define WLAN_PROT_GDD_ENABLEMENT_REQ 28 #define WLAN_PROT_GDD_ENABLEMENT_RESP 29 #define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30 #define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 #define WLAN_SA_QUERY_RESPONSE 1 #define WLAN_SA_QUERY_TR_ID_LEN 2 /* TDLS action codes */ #define WLAN_TDLS_SETUP_REQUEST 0 #define WLAN_TDLS_SETUP_RESPONSE 1 #define WLAN_TDLS_SETUP_CONFIRM 2 #define WLAN_TDLS_TEARDOWN 3 #define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4 #define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5 #define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6 #define WLAN_TDLS_PEER_PSM_REQUEST 7 #define WLAN_TDLS_PEER_PSM_RESPONSE 8 #define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9 #define WLAN_TDLS_DISCOVERY_REQUEST 10 /* Radio Measurement Action codes */ #define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0 #define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1 #define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2 #define WLAN_RRM_LINK_MEASUREMENT_REPORT 3 #define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4 #define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5 /* Protected Fine Timing Frame Action Field value */ #define WLAN_PROT_FTM_REQUEST 1 #define WLAN_PROT_FTM 2 #define WLAN_PROT_FTM_REPORT 3 /* Radio Measurement capabilities (from RM Enabled Capabilities element) * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */ /* byte 1 (out of 5) */ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) #define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4) #define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5) #define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6) /* byte 2 (out of 5) */ #define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) /* byte 5 (out of 5) */ #define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2) /* * IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range * request) - Minimum AP count */ #define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15 /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 #define WLAN_TIMEOUT_KEY_LIFETIME 2 #define WLAN_TIMEOUT_ASSOC_COMEBACK 3 /* Interworking element (IEEE 802.11u) - Access Network Options */ #define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f #define INTERWORKING_ANO_INTERNET 0x10 #define INTERWORKING_ANO_ASRA 0x20 #define INTERWORKING_ANO_ESR 0x40 #define INTERWORKING_ANO_UESA 0x80 #define INTERWORKING_ANT_PRIVATE 0 #define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1 #define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2 #define INTERWORKING_ANT_FREE_PUBLIC 3 #define INTERWORKING_ANT_PERSONAL_DEVICE 4 #define INTERWORKING_ANT_EMERGENCY_SERVICES 5 #define INTERWORKING_ANT_TEST 6 #define INTERWORKING_ANT_WILDCARD 15 /* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */ enum adv_proto_id { ACCESS_NETWORK_QUERY_PROTOCOL = 0, MIH_INFO_SERVICE = 1, MIH_CMD_AND_EVENT_DISCOVERY = 2, EMERGENCY_ALERT_SYSTEM = 3, REGISTERED_LOCATION_QUERY_PROTO = 4, ADV_PROTO_VENDOR_SPECIFIC = 221 }; /* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016, * Table 9-271; P802.11ai) */ enum anqp_info_id { ANQP_QUERY_LIST = 256, ANQP_CAPABILITY_LIST = 257, ANQP_VENUE_NAME = 258, ANQP_EMERGENCY_CALL_NUMBER = 259, ANQP_NETWORK_AUTH_TYPE = 260, ANQP_ROAMING_CONSORTIUM = 261, ANQP_IP_ADDR_TYPE_AVAILABILITY = 262, ANQP_NAI_REALM = 263, ANQP_3GPP_CELLULAR_NETWORK = 264, ANQP_AP_GEOSPATIAL_LOCATION = 265, ANQP_AP_CIVIC_LOCATION = 266, ANQP_AP_LOCATION_PUBLIC_URI = 267, ANQP_DOMAIN_NAME = 268, ANQP_EMERGENCY_ALERT_URI = 269, ANQP_TDLS_CAPABILITY = 270, ANQP_EMERGENCY_NAI = 271, ANQP_NEIGHBOR_REPORT = 272, ANQP_QUERY_AP_LIST = 273, ANQP_AP_LIST_RESPONSE = 274, ANQP_FILS_REALM_INFO = 275, ANQP_CAG = 276, ANQP_VENUE_URL = 277, ANQP_ADVICE_OF_CHARGE = 278, ANQP_LOCAL_CONTENT = 279, ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280, ANQP_VENDOR_SPECIFIC = 56797 }; /* NAI Realm list - EAP Method subfield - Authentication Parameter ID */ enum nai_realm_eap_auth_param { NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1, NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2, NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3, NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4, NAI_REALM_EAP_AUTH_CRED_TYPE = 5, NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6, NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221 }; enum nai_realm_eap_auth_inner_non_eap { NAI_REALM_INNER_NON_EAP_PAP = 1, NAI_REALM_INNER_NON_EAP_CHAP = 2, NAI_REALM_INNER_NON_EAP_MSCHAP = 3, NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4 }; enum nai_realm_eap_cred_type { NAI_REALM_CRED_TYPE_SIM = 1, NAI_REALM_CRED_TYPE_USIM = 2, NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3, NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4, NAI_REALM_CRED_TYPE_SOFTOKEN = 5, NAI_REALM_CRED_TYPE_CERTIFICATE = 6, NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7, NAI_REALM_CRED_TYPE_NONE = 8, NAI_REALM_CRED_TYPE_ANONYMOUS = 9, NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 }; /* Unprotected S1G Action field values for WLAN_ACTION_S1G */ #define S1G_ACT_AID_SWITCH_REQUEST 0 #define S1G_ACT_AID_SWITCH_RESPONSE 1 #define S1G_ACT_SYNC_CONTROL 2 #define S1G_ACT_STA_INFO_ANNOUNCE 3 #define S1G_ACT_EDCA_PARAM_SET 4 #define S1G_ACT_EL_OPERATION 5 #define S1G_ACT_TWT_SETUP 6 #define S1G_ACT_TWT_TEARDOWN 7 #define S1G_ACT_SECT_GROUP_ID_LIST 8 #define S1G_ACT_SECT_ID_FEEDBACK 9 #define S1G_ACT_TWT_INFORMATION 11 /* * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for * measurement requests */ enum measure_type { MEASURE_TYPE_BASIC = 0, MEASURE_TYPE_CCA = 1, MEASURE_TYPE_RPI_HIST = 2, MEASURE_TYPE_CHANNEL_LOAD = 3, MEASURE_TYPE_NOISE_HIST = 4, MEASURE_TYPE_BEACON = 5, MEASURE_TYPE_FRAME = 6, MEASURE_TYPE_STA_STATISTICS = 7, MEASURE_TYPE_LCI = 8, MEASURE_TYPE_TRANSMIT_STREAM = 9, MEASURE_TYPE_MULTICAST_DIAG = 10, MEASURE_TYPE_LOCATION_CIVIC = 11, MEASURE_TYPE_LOCATION_ID = 12, MEASURE_TYPE_DIRECTIONAL_CHAN_QUALITY = 13, MEASURE_TYPE_DIRECTIONAL_MEASURE = 14, MEASURE_TYPE_DIRECTIONAL_STATS = 15, MEASURE_TYPE_FTM_RANGE = 16, MEASURE_TYPE_MEASURE_PAUSE = 255, }; /* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */ enum location_subject { LOCATION_SUBJECT_LOCAL = 0, LOCATION_SUBJECT_REMOTE = 1, LOCATION_SUBJECT_3RD_PARTY = 2, }; /* * IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request */ enum lci_req_subelem { LCI_REQ_SUBELEM_AZIMUTH_REQ = 1, LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2, LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3, LCI_REQ_SUBELEM_MAX_AGE = 4, }; #define FILS_NONCE_LEN 16 #define FILS_SESSION_LEN 8 #define FILS_CACHE_ID_LEN 2 #define FILS_MAX_KEY_AUTH_LEN 48 #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ struct ieee80211_hdr { le16 frame_control; le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; le16 seq_ctrl; /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame */ } STRUCT_PACKED; #define IEEE80211_DA_FROMDS addr1 #define IEEE80211_BSSID_FROMDS addr2 #define IEEE80211_SA_FROMDS addr3 #define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) #define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) struct ieee80211_mgmt { le16 frame_control; le16 duration; u8 da[6]; u8 sa[6]; u8 bssid[6]; le16 seq_ctrl; union { struct { le16 auth_alg; le16 auth_transaction; le16 status_code; /* possibly followed by Challenge text */ u8 variable[]; } STRUCT_PACKED auth; struct { le16 reason_code; u8 variable[]; } STRUCT_PACKED deauth; struct { le16 capab_info; le16 listen_interval; /* followed by SSID and Supported rates */ u8 variable[]; } STRUCT_PACKED assoc_req; struct { le16 capab_info; le16 status_code; le16 aid; /* followed by Supported rates */ u8 variable[]; } STRUCT_PACKED assoc_resp, reassoc_resp; struct { le16 capab_info; le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ u8 variable[]; } STRUCT_PACKED reassoc_req; struct { le16 reason_code; u8 variable[]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; le16 beacon_int; le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[]; } STRUCT_PACKED beacon; /* probe_req: only variable items: SSID, Supported rates */ struct { u8 timestamp[8]; le16 beacon_int; le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ u8 variable[]; } STRUCT_PACKED probe_resp; struct { u8 category; union { struct { u8 action_code; u8 dialog_token; u8 status_code; u8 variable[]; } STRUCT_PACKED wmm_action; struct{ u8 action_code; u8 element_id; u8 length; u8 switch_mode; u8 new_chan; u8 switch_count; } STRUCT_PACKED chan_switch; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_req; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; le16 status_code; u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_resp; struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; u8 variable[]; /* OCI element */ } STRUCT_PACKED sa_query_req; struct { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; u8 variable[]; /* OCI element */ } STRUCT_PACKED sa_query_resp; struct { u8 action; u8 dialogtoken; u8 variable[]; } STRUCT_PACKED wnm_sleep_req; struct { u8 action; u8 dialogtoken; le16 keydata_len; u8 variable[]; } STRUCT_PACKED wnm_sleep_resp; struct { u8 action; u8 variable[]; } STRUCT_PACKED public_action; struct { u8 action; /* 9 */ u8 oui[3]; /* Vendor-specific content */ u8 variable[]; } STRUCT_PACKED vs_public_action; struct { u8 action; /* 7 */ u8 dialog_token; u8 req_mode; le16 disassoc_timer; u8 validity_interval; /* BSS Termination Duration (optional), * Session Information URL (optional), * BSS Transition Candidate List * Entries */ u8 variable[]; } STRUCT_PACKED bss_tm_req; struct { u8 action; /* 8 */ u8 dialog_token; u8 status_code; u8 bss_termination_delay; /* Target BSSID (optional), * BSS Transition Candidate List * Entries (optional) */ u8 variable[]; } STRUCT_PACKED bss_tm_resp; struct { u8 action; /* 6 */ u8 dialog_token; u8 query_reason; /* BSS Transition Candidate List * Entries (optional) */ u8 variable[]; } STRUCT_PACKED bss_tm_query; struct { u8 action; /* 11 */ u8 dialog_token; u8 req_info; } STRUCT_PACKED coloc_intf_req; struct { u8 action; /* 12 */ u8 dialog_token; u8 variable[]; } STRUCT_PACKED coloc_intf_report; struct { u8 action; /* 15 */ u8 variable[]; } STRUCT_PACKED slf_prot_action; struct { u8 action; u8 variable[]; } STRUCT_PACKED fst_action; struct { u8 action; u8 dialog_token; u8 variable[]; } STRUCT_PACKED rrm; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; #define IEEE80211_MAX_MMPDU_SIZE 2304 /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ #define IEEE80211_HT_MCS_MASK_LEN 10 /* HT Capabilities element */ struct ieee80211_ht_capabilities { le16 ht_capabilities_info; u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1 * Minimum MPDU Start Spacing B2..B4 * Reserved B5..B7 */ u8 supported_mcs_set[16]; le16 ht_extended_capabilities; le32 tx_bf_capability_info; u8 asel_capabilities; } STRUCT_PACKED; /* HT Operation element */ struct ieee80211_ht_operation { u8 primary_chan; /* Five octets of HT Operation Information */ u8 ht_param; /* B0..B7 */ le16 operation_mode; /* B8..B23 */ le16 param; /* B24..B39 */ u8 basic_mcs_set[16]; } STRUCT_PACKED; struct ieee80211_obss_scan_parameters { le16 scan_passive_dwell; le16 scan_active_dwell; le16 width_trigger_scan_interval; le16 scan_passive_total_per_channel; le16 scan_active_total_per_channel; le16 channel_transition_delay_factor; le16 scan_activity_threshold; } STRUCT_PACKED; struct ieee80211_vht_capabilities { le32 vht_capabilities_info; struct { le16 rx_map; le16 rx_highest; le16 tx_map; le16 tx_highest; } vht_supported_mcs_set; } STRUCT_PACKED; struct ieee80211_vht_operation { u8 vht_op_info_chwidth; u8 vht_op_info_chan_center_freq_seg0_idx; u8 vht_op_info_chan_center_freq_seg1_idx; le16 vht_basic_mcs_set; } STRUCT_PACKED; struct ieee80211_ampe_ie { u8 selected_pairwise_suite[4]; u8 local_nonce[32]; u8 peer_nonce[32]; /* Followed by * Key Replay Counter[8] (optional) * (only in Mesh Group Key Inform/Acknowledge frames) * GTKdata[variable] (optional) * (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]) * IGTKdata[variable] (optional) * (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format) */ } STRUCT_PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #define ERP_INFO_NON_ERP_PRESENT BIT(0) #define ERP_INFO_USE_PROTECTION BIT(1) #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) #define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5 /* HT Capabilities Info field within HT Capabilities element */ #define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) #define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1)) #define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3))) #define HT_CAP_INFO_SMPS_STATIC ((u16) 0) #define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2)) #define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3))) #define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4)) #define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5)) #define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6)) #define HT_CAP_INFO_TX_STBC ((u16) BIT(7)) #define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9))) #define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8)) #define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9)) #define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9))) #define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10)) #define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11)) #define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12)) /* B13 - Reserved (was PSMP support during P802.11n development) */ #define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14)) #define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) /* HT Extended Capabilities field within HT Capabilities element */ #define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) #define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK ((u16) (BIT(1) | BIT(2))) #define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 /* B3..B7 - Reserved */ #define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK ((u16) (BIT(8) | BIT(9))) #define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 #define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10)) #define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) /* B12..B15 - Reserved */ /* Transmit Beanforming Capabilities within HT Capabilities element */ #define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0)) #define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) #define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) #define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3)) #define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4)) #define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5)) #define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7)) #define TX_BF_CAP_CALIB_OFFSET 6 #define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) #define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9)) #define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10)) #define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11))) #define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 #define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 #define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 #define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17 #define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 #define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 #define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 #define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 #define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28))) #define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27 /* B29..B31 - Reserved */ /* ASEL Capability field within HT Capabilities element */ #define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0)) #define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) #define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) #define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) #define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) #define ASEL_CAP_RX_AS_CAP ((u8) BIT(5)) #define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6)) /* B7 - Reserved */ /* First octet of HT Operation Information within HT Operation element */ #define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1)) #define HT_INFO_HT_PARAM_STA_CHNL_WIDTH ((u8) BIT(2)) #define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) /* B4..B7 - Reserved */ /* HT Protection (B8..B9 of HT Operation Information) */ #define HT_PROT_NO_PROTECTION 0 #define HT_PROT_NONMEMBER_PROTECTION 1 #define HT_PROT_20MHZ_PROTECTION 2 #define HT_PROT_NON_HT_MIXED 3 /* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in * HT Operation Information) */ #define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */ #define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */ /* BIT(3), i.e., B11 in HT Operation Information field - Reserved */ #define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT ((u16) BIT(4)) /* B12 */ /* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */ /* Last two octets of HT Operation Information (BIT(0) = B24) */ /* B24..B29 - Reserved */ #define HT_OPER_PARAM_DUAL_BEACON ((u16) BIT(6)) #define HT_OPER_PARAM_DUAL_CTS_PROTECTION ((u16) BIT(7)) #define HT_OPER_PARAM_STBC_BEACON ((u16) BIT(8)) #define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP ((u16) BIT(9)) #define HT_OPER_PARAM_PCO_ACTIVE ((u16) BIT(10)) #define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11)) /* B36..B39 - Reserved */ #define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 #define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123 /* VHT Defines */ #define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) #define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) #define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1)) #define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT 0 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) #define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) #define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2 #define VHT_CAP_RXLDPC ((u32) BIT(4)) #define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) #define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) #define VHT_CAP_TXSTBC ((u32) BIT(7)) #define VHT_CAP_RXSTBC_1 ((u32) BIT(8)) #define VHT_CAP_RXSTBC_2 ((u32) BIT(9)) #define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9)) #define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) #define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \ BIT(10)) #define VHT_CAP_RXSTBC_MASK_SHIFT 8 #define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) #define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) #define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \ BIT(14) | BIT(15)) #define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT 13 #define VHT_CAP_BEAMFORMEE_STS_OFFSET 13 #define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \ BIT(17) | BIT(18)) #define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT 16 #define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16 #define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) #define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) #define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21)) #define VHT_CAP_HTC_VHT ((u32) BIT(22)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1 ((u32) BIT(23)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2 ((u32) BIT(24)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3 ((u32) BIT(23) | BIT(24)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4 ((u32) BIT(25)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5 ((u32) BIT(23) | BIT(25)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6 ((u32) BIT(24) | BIT(25)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX ((u32) BIT(23) | \ BIT(24) | BIT(25)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) #define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1)) #define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \ BIT(6)) #define VHT_OPMODE_NOTIF_RX_NSS_SHIFT 4 #define VHT_RX_NSS_MAX_STREAMS 8 /* VHT/EDMG channel widths */ #define CHANWIDTH_USE_HT 0 #define CHANWIDTH_80MHZ 1 #define CHANWIDTH_160MHZ 2 #define CHANWIDTH_80P80MHZ 3 #define CHANWIDTH_2160MHZ 4 #define CHANWIDTH_4320MHZ 5 #define CHANWIDTH_6480MHZ 6 #define CHANWIDTH_8640MHZ 7 #define HE_NSS_MAX_STREAMS 8 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WPA_IE_VENDOR_TYPE 0x0050f201 #define WMM_IE_VENDOR_TYPE 0x0050f202 #define WPS_IE_VENDOR_TYPE 0x0050f204 #define OUI_WFA 0x506f9a #define P2P_IE_VENDOR_TYPE 0x506f9a09 #define WFD_IE_VENDOR_TYPE 0x506f9a0a #define WFD_OUI_TYPE 10 #define HS20_IE_VENDOR_TYPE 0x506f9a10 #define OSEN_IE_VENDOR_TYPE 0x506f9a12 #define MBO_IE_VENDOR_TYPE 0x506f9a16 #define MBO_OUI_TYPE 22 #define OWE_IE_VENDOR_TYPE 0x506f9a1c #define OWE_OUI_TYPE 28 #define MULTI_AP_OUI_TYPE 0x1B #define DPP_CC_IE_VENDOR_TYPE 0x506f9a1e #define DPP_CC_OUI_TYPE 0x1e #define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f #define SAE_PK_OUI_TYPE 0x1f #define MULTI_AP_SUB_ELEM_TYPE 0x06 #define MULTI_AP_TEAR_DOWN BIT(4) #define MULTI_AP_FRONTHAUL_BSS BIT(5) #define MULTI_AP_BACKHAUL_BSS BIT(6) #define MULTI_AP_BACKHAUL_STA BIT(7) #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 #define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1 #define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2 #define WMM_VERSION 1 #define WMM_ACTION_CODE_ADDTS_REQ 0 #define WMM_ACTION_CODE_ADDTS_RESP 1 #define WMM_ACTION_CODE_DELTS 2 #define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0 #define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1 /* 2 - Reserved */ #define WMM_ADDTS_STATUS_REFUSED 3 /* 4-255 - Reserved */ /* WMM TSPEC Direction Field Values */ #define WMM_TSPEC_DIRECTION_UPLINK 0 #define WMM_TSPEC_DIRECTION_DOWNLINK 1 /* 2 - Reserved */ #define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 /* * WMM Information Element (used in (Re)Association Request frames; may also be * used in Beacon frames) */ struct wmm_information_element { /* Element ID: 221 (0xdd); Length: 7 */ /* required fields for WMM version 1 */ u8 oui[3]; /* 00:50:f2 */ u8 oui_type; /* 2 */ u8 oui_subtype; /* 0 */ u8 version; /* 1 for WMM version 1.0 */ u8 qos_info; /* AP/STA specific QoS info */ } STRUCT_PACKED; #define WMM_QOSINFO_AP_UAPSD 0x80 #define WMM_QOSINFO_STA_AC_MASK 0x0f #define WMM_QOSINFO_STA_SP_MASK 0x03 #define WMM_QOSINFO_STA_SP_SHIFT 5 #define WMM_AC_AIFSN_MASK 0x0f #define WMM_AC_AIFNS_SHIFT 0 #define WMM_AC_ACM 0x10 #define WMM_AC_ACI_MASK 0x60 #define WMM_AC_ACI_SHIFT 5 #define WMM_AC_ECWMIN_MASK 0x0f #define WMM_AC_ECWMIN_SHIFT 0 #define WMM_AC_ECWMAX_MASK 0xf0 #define WMM_AC_ECWMAX_SHIFT 4 struct wmm_ac_parameter { u8 aci_aifsn; /* AIFSN, ACM, ACI */ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ le16 txop_limit; } STRUCT_PACKED; /* * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association * Response frmaes) */ struct wmm_parameter_element { /* Element ID: 221 (0xdd); Length: 24 */ /* required fields for WMM version 1 */ u8 oui[3]; /* 00:50:f2 */ u8 oui_type; /* 2 */ u8 oui_subtype; /* 1 */ u8 version; /* 1 for WMM version 1.0 */ u8 qos_info; /* AP/STA specific QoS info */ u8 reserved; /* 0 */ struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ } STRUCT_PACKED; /* WMM TSPEC Element */ struct wmm_tspec_element { u8 eid; /* 221 = 0xdd */ u8 length; /* 6 + 55 = 61 */ u8 oui[3]; /* 00:50:f2 */ u8 oui_type; /* 2 */ u8 oui_subtype; /* 2 */ u8 version; /* 1 */ /* WMM TSPEC body (55 octets): */ u8 ts_info[3]; le16 nominal_msdu_size; le16 maximum_msdu_size; le32 minimum_service_interval; le32 maximum_service_interval; le32 inactivity_interval; le32 suspension_interval; le32 service_start_time; le32 minimum_data_rate; le32 mean_data_rate; le32 peak_data_rate; le32 maximum_burst_size; le32 delay_bound; le32 minimum_phy_rate; le16 surplus_bandwidth_allowance; le16 medium_time; } STRUCT_PACKED; /* Access Categories / ACI to AC coding */ enum wmm_ac { WMM_AC_BE = 0 /* Best Effort */, WMM_AC_BK = 1 /* Background */, WMM_AC_VI = 2 /* Video */, WMM_AC_VO = 3 /* Voice */, WMM_AC_NUM = 4 }; #define HS20_INDICATION_OUI_TYPE 16 #define HS20_ANQP_OUI_TYPE 17 #define HS20_OSEN_OUI_TYPE 18 #define HS20_ROAMING_CONS_SEL_OUI_TYPE 29 #define HS20_STYPE_QUERY_LIST 1 #define HS20_STYPE_CAPABILITY_LIST 2 #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 #define HS20_STYPE_WAN_METRICS 4 #define HS20_STYPE_CONNECTION_CAPABILITY 5 #define HS20_STYPE_NAI_HOME_REALM_QUERY 6 #define HS20_STYPE_OPERATING_CLASS 7 #define HS20_STYPE_OSU_PROVIDERS_LIST 8 #define HS20_STYPE_ICON_REQUEST 10 #define HS20_STYPE_ICON_BINARY_FILE 11 #define HS20_STYPE_OPERATOR_ICON_METADATA 12 #define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13 #define HS20_DGAF_DISABLED 0x01 #define HS20_PPS_MO_ID_PRESENT 0x02 #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 #ifndef HS20_VERSION #define HS20_VERSION 0x20 /* Release 3 */ #endif /* HS20_VERSION */ /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 #define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2 #define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3 #define HS20_WNM_T_C_ACCEPTANCE 4 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 /* MBO v0.0_r19, 4.2: MBO Attributes */ /* Table 4-5: MBO Attributes */ /* OCE v0.0.10, Table 4-3: OCE Attributes */ enum mbo_attr_id { MBO_ATTR_ID_AP_CAPA_IND = 1, MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2, MBO_ATTR_ID_CELL_DATA_CAPA = 3, MBO_ATTR_ID_ASSOC_DISALLOW = 4, MBO_ATTR_ID_CELL_DATA_PREF = 5, MBO_ATTR_ID_TRANSITION_REASON = 6, MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7, MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8, OCE_ATTR_ID_CAPA_IND = 101, OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102, OCE_ATTR_ID_REDUCED_WAN_METRICS = 103, OCE_ATTR_ID_RNR_COMPLETENESS = 104, }; /* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */ /* Table 4-7: MBO AP Capability Indication Field Values */ #define MBO_AP_CAPA_CELL_AWARE BIT(6) /* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */ /* Table 4-10: Reason Code Field Values */ enum mbo_non_pref_chan_reason { MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0, MBO_NON_PREF_CHAN_REASON_RSSI = 1, MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2, MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3, }; /* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */ /* Table 4-13: Cellular Data Connectivity Field */ enum mbo_cellular_capa { MBO_CELL_CAPA_AVAILABLE = 1, MBO_CELL_CAPA_NOT_AVAILABLE = 2, MBO_CELL_CAPA_NOT_SUPPORTED = 3, }; /* MBO v0.0_r19, 4.2.4: Association Disallowed Attribute */ /* Table 4-15: Reason Code Field Values */ enum mbo_assoc_disallow_reason { MBO_ASSOC_DISALLOW_REASON_UNSPECIFIED = 1, MBO_ASSOC_DISALLOW_REASON_MAX_STA = 2, MBO_ASSOC_DISALLOW_REASON_AIR_INTERFERENCE = 3, MBO_ASSOC_DISALLOW_REASON_AUTH_SERVER_OVERLOAD = 4, MBO_ASSOC_DISALLOW_REASON_LOW_RSSI = 5, }; /* MBO v0.0_r19, 4.2.5: Cellular Data Connection Preference Attribute */ /* Table 4-17: Cellular Preference Field Values */ enum mbo_cell_pref { MBO_CELL_PREF_EXCLUDED = 0, MBO_CELL_PREF_NO_USE = 1, MBO_CELL_PREF_USE = 255 }; /* MBO v0.0_r19, 4.2.6: Transition Reason Code Attribute */ /* Table 4-19: Transition Reason Code Field Values */ enum mbo_transition_reason { MBO_TRANSITION_REASON_UNSPECIFIED = 0, MBO_TRANSITION_REASON_FRAME_LOSS = 1, MBO_TRANSITION_REASON_DELAY = 2, MBO_TRANSITION_REASON_BANDWIDTH = 3, MBO_TRANSITION_REASON_LOAD_BALANCE = 4, MBO_TRANSITION_REASON_RSSI = 5, MBO_TRANSITION_REASON_RETRANSMISSIONS = 6, MBO_TRANSITION_REASON_INTERFERENCE = 7, MBO_TRANSITION_REASON_GRAY_ZONE = 8, MBO_TRANSITION_REASON_PREMIUM_AP = 9, }; /* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */ /* Table 4-21: Transition Rejection Reason Code Field Values */ enum mbo_transition_reject_reason { MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0, MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1, MBO_TRANSITION_REJECT_REASON_DELAY = 2, MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3, MBO_TRANSITION_REJECT_REASON_RSSI = 4, MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5, MBO_TRANSITION_REJECT_REASON_SERVICES = 6, }; /* MBO v0.0_r27, 4.3: MBO ANQP-elements */ #define MBO_ANQP_OUI_TYPE 0x12 #define MBO_ANQP_SUBTYPE_QUERY_LIST 1 #define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2 #define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF /* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */ #define OCE_RELEASE 1 #define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2)) #define OCE_IS_STA_CFON BIT(3) #define OCE_IS_NON_OCE_AP_PRESENT BIT(4) /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 enum p2p_attr_id { P2P_ATTR_STATUS = 0, P2P_ATTR_MINOR_REASON_CODE = 1, P2P_ATTR_CAPABILITY = 2, P2P_ATTR_DEVICE_ID = 3, P2P_ATTR_GROUP_OWNER_INTENT = 4, P2P_ATTR_CONFIGURATION_TIMEOUT = 5, P2P_ATTR_LISTEN_CHANNEL = 6, P2P_ATTR_GROUP_BSSID = 7, P2P_ATTR_EXT_LISTEN_TIMING = 8, P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, P2P_ATTR_MANAGEABILITY = 10, P2P_ATTR_CHANNEL_LIST = 11, P2P_ATTR_NOTICE_OF_ABSENCE = 12, P2P_ATTR_DEVICE_INFO = 13, P2P_ATTR_GROUP_INFO = 14, P2P_ATTR_GROUP_ID = 15, P2P_ATTR_INTERFACE = 16, P2P_ATTR_OPERATING_CHANNEL = 17, P2P_ATTR_INVITATION_FLAGS = 18, P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, P2P_ATTR_SERVICE_HASH = 21, P2P_ATTR_SESSION_INFORMATION_DATA = 22, P2P_ATTR_CONNECTION_CAPABILITY = 23, P2P_ATTR_ADVERTISEMENT_ID = 24, P2P_ATTR_ADVERTISED_SERVICE = 25, P2P_ATTR_SESSION_ID = 26, P2P_ATTR_FEATURE_CAPABILITY = 27, P2P_ATTR_PERSISTENT_GROUP = 28, P2P_ATTR_VENDOR_SPECIFIC = 221 }; #define P2P_MAX_GO_INTENT 15 /* P2P Capability - Device Capability bitmap */ #define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0) #define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1) #define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2) #define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3) #define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4) #define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5) +#define P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE BIT(6) /* P2P Capability - Group Capability bitmap */ #define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0) #define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) #define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2) #define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3) #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) /* P2PS Coordination Protocol Transport Bitmap */ #define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0) #define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1) struct p2ps_feature_capab { u8 cpt; u8 reserved; } STRUCT_PACKED; /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) /* P2P Manageability */ #define P2P_MAN_DEVICE_MANAGEMENT BIT(0) #define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1) #define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2) enum p2p_status_code { P2P_SC_SUCCESS = 0, P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1, P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2, P2P_SC_FAIL_LIMIT_REACHED = 3, P2P_SC_FAIL_INVALID_PARAMS = 4, P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5, P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6, P2P_SC_FAIL_NO_COMMON_CHANNELS = 7, P2P_SC_FAIL_UNKNOWN_GROUP = 8, P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9, P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, P2P_SC_FAIL_REJECTED_BY_USER = 11, P2P_SC_SUCCESS_DEFERRED = 12, }; enum p2p_role_indication { P2P_DEVICE_NOT_IN_GROUP = 0x00, P2P_CLIENT_IN_A_GROUP = 0x01, P2P_GO_IN_A_GROUP = 0x02, }; #define P2P_WILDCARD_SSID "DIRECT-" #define P2P_WILDCARD_SSID_LEN 7 /* P2P action frames */ enum p2p_act_frame_type { P2P_NOA = 0, P2P_PRESENCE_REQ = 1, P2P_PRESENCE_RESP = 2, P2P_GO_DISC_REQ = 3 }; /* P2P public action frames */ enum p2p_action_frame_type { P2P_GO_NEG_REQ = 0, P2P_GO_NEG_RESP = 1, P2P_GO_NEG_CONF = 2, P2P_INVITATION_REQ = 3, P2P_INVITATION_RESP = 4, P2P_DEV_DISC_REQ = 5, P2P_DEV_DISC_RESP = 6, P2P_PROV_DISC_REQ = 7, P2P_PROV_DISC_RESP = 8 }; enum p2p_service_protocol_type { P2P_SERV_ALL_SERVICES = 0, P2P_SERV_BONJOUR = 1, P2P_SERV_UPNP = 2, P2P_SERV_WS_DISCOVERY = 3, P2P_SERV_WIFI_DISPLAY = 4, P2P_SERV_P2PS = 11, P2P_SERV_VENDOR_SPECIFIC = 255 }; enum p2p_sd_status { P2P_SD_SUCCESS = 0, P2P_SD_PROTO_NOT_AVAILABLE = 1, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2, P2P_SD_BAD_REQUEST = 3 }; enum wifi_display_subelem { WFD_SUBELEM_DEVICE_INFO = 0, WFD_SUBELEM_ASSOCIATED_BSSID = 1, WFD_SUBELEM_AUDIO_FORMATS = 2, WFD_SUBELEM_VIDEO_FORMATS = 3, WFD_SUBELEM_3D_VIDEO_FORMATS = 4, WFD_SUBELEM_CONTENT_PROTECTION = 5, WFD_SUBELEM_COUPLED_SINK = 6, WFD_SUBELEM_EXT_CAPAB = 7, WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, WFD_SUBELEM_SESSION_INFO = 9, WFD_SUBELEM_MAC_INFO = 10, WFD_SUBELEM_R2_DEVICE_INFO = 11, }; /* 802.11s */ #define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1 #define MESH_SYNC_METHOD_VENDOR 255 #define MESH_PATH_PROTOCOL_HWMP 1 #define MESH_PATH_PROTOCOL_VENDOR 255 #define MESH_PATH_METRIC_AIRTIME 1 #define MESH_PATH_METRIC_VENDOR 255 /* IEEE 802.11s - Mesh Capability */ #define MESH_CAP_ACCEPT_ADDITIONAL_PEER BIT(0) #define MESH_CAP_MCCA_SUPPORTED BIT(1) #define MESH_CAP_MCCA_ENABLED BIT(2) #define MESH_CAP_FORWARDING BIT(3) #define MESH_CAP_MBCA_ENABLED BIT(4) #define MESH_CAP_TBTT_ADJUSTING BIT(5) #define MESH_CAP_MESH_PS_LEVEL BIT(6) enum plink_action_field { PLINK_OPEN = 1, PLINK_CONFIRM, PLINK_CLOSE }; #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_VHT_TYPE 0x04 #define VENDOR_VHT_SUBTYPE 0x08 #define VENDOR_VHT_SUBTYPE2 0x00 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ /* IEEE 802.11v - WNM Action field values */ enum wnm_action { WNM_EVENT_REQ = 0, WNM_EVENT_REPORT = 1, WNM_DIAGNOSTIC_REQ = 2, WNM_DIAGNOSTIC_REPORT = 3, WNM_LOCATION_CFG_REQ = 4, WNM_LOCATION_CFG_RESP = 5, WNM_BSS_TRANS_MGMT_QUERY = 6, WNM_BSS_TRANS_MGMT_REQ = 7, WNM_BSS_TRANS_MGMT_RESP = 8, WNM_FMS_REQ = 9, WNM_FMS_RESP = 10, WNM_COLLOCATED_INTERFERENCE_REQ = 11, WNM_COLLOCATED_INTERFERENCE_REPORT = 12, WNM_TFS_REQ = 13, WNM_TFS_RESP = 14, WNM_TFS_NOTIFY = 15, WNM_SLEEP_MODE_REQ = 16, WNM_SLEEP_MODE_RESP = 17, WNM_TIM_BROADCAST_REQ = 18, WNM_TIM_BROADCAST_RESP = 19, WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20, WNM_CHANNEL_USAGE_REQ = 21, WNM_CHANNEL_USAGE_RESP = 22, WNM_DMS_REQ = 23, WNM_DMS_RESP = 24, WNM_TIMING_MEASUREMENT_REQ = 25, WNM_NOTIFICATION_REQ = 26, WNM_NOTIFICATION_RESP = 27 }; /* IEEE 802.11v - BSS Transition Management Request - Request Mode */ #define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0) #define WNM_BSS_TM_REQ_ABRIDGED BIT(1) #define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2) #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) /* IEEE Std 802.11-2012 - Table 8-253 */ enum bss_trans_mgmt_status_code { WNM_BSS_TM_ACCEPT = 0, WNM_BSS_TM_REJECT_UNSPECIFIED = 1, WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2, WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3, WNM_BSS_TM_REJECT_UNDESIRED = 4, WNM_BSS_TM_REJECT_DELAY_REQUEST = 5, WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7, WNM_BSS_TM_REJECT_LEAVING_ESS = 8 }; /* * IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for * neighbor report */ #define WNM_NEIGHBOR_TSF 1 #define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 #define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 #define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 #define WNM_NEIGHBOR_BEARING 5 #define WNM_NEIGHBOR_WIDE_BW_CHAN 6 #define WNM_NEIGHBOR_MEASUREMENT_REPORT 39 #define WNM_NEIGHBOR_HT_CAPAB 45 #define WNM_NEIGHBOR_HT_OPER 61 #define WNM_NEIGHBOR_SEC_CHAN_OFFSET 62 #define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 #define WNM_NEIGHBOR_VHT_CAPAB 191 #define WNM_NEIGHBOR_VHT_OPER 192 /* QoS action */ enum qos_action { QOS_ADDTS_REQ = 0, QOS_ADDTS_RESP = 1, QOS_DELTS = 2, QOS_SCHEDULE = 3, QOS_QOS_MAP_CONFIG = 4, }; /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ #define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) #define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) #define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) #define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) #define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) struct ieee80211_2040_bss_coex_ie { u8 element_id; u8 length; u8 coex_param; } STRUCT_PACKED; struct ieee80211_2040_intol_chan_report { u8 element_id; u8 length; u8 op_class; u8 variable[0]; /* Channel List */ } STRUCT_PACKED; /* IEEE 802.11v - WNM-Sleep Mode element */ struct wnm_sleep_element { u8 eid; /* WLAN_EID_WNMSLEEP */ u8 len; u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */ u8 status; le16 intval; } STRUCT_PACKED; #define WNM_SLEEP_MODE_ENTER 0 #define WNM_SLEEP_MODE_EXIT 1 enum wnm_sleep_mode_response_status { WNM_STATUS_SLEEP_ACCEPT = 0, WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, WNM_STATUS_DENIED_ACTION = 2, WNM_STATUS_DENIED_TMP = 3, WNM_STATUS_DENIED_KEY = 4, WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 }; /* WNM-Sleep Mode subelement IDs */ enum wnm_sleep_mode_subelement_id { WNM_SLEEP_SUBELEM_GTK = 0, WNM_SLEEP_SUBELEM_IGTK = 1, WNM_SLEEP_SUBELEM_BIGTK = 2, }; /* WNM notification type (IEEE P802.11-REVmd/D3.0, Table 9-430) */ enum wnm_notification_Type { WNM_NOTIF_TYPE_FIRMWARE_UPDATE = 0, WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE = 2, WNM_NOTIF_TYPE_VENDOR_SPECIFIC = 221, }; /* Channel Switch modes (802.11h) */ #define CHAN_SWITCH_MODE_ALLOW_TX 0 #define CHAN_SWITCH_MODE_BLOCK_TX 1 struct tpc_report { u8 eid; u8 len; u8 tx_power; u8 link_margin; } STRUCT_PACKED; #define RRM_CAPABILITIES_IE_LEN 5 /* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */ struct rrm_link_measurement_request { u8 dialog_token; s8 tx_power; s8 max_tp; u8 variable[0]; } STRUCT_PACKED; /* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */ struct rrm_link_measurement_report { u8 dialog_token; struct tpc_report tpc; u8 rx_ant_id; u8 tx_ant_id; u8 rcpi; u8 rsni; u8 variable[0]; } STRUCT_PACKED; /* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */ struct rrm_measurement_request_element { u8 eid; /* Element ID */ u8 len; /* Length */ u8 token; /* Measurement Token */ u8 mode; /* Measurement Request Mode */ u8 type; /* Measurement Type */ u8 variable[0]; /* Measurement Request */ } STRUCT_PACKED; /* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */ #define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0) #define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1) #define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2) #define MEASUREMENT_REQUEST_MODE_REPORT BIT(3) #define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4) /* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */ struct rrm_measurement_beacon_request { u8 oper_class; /* Operating Class */ u8 channel; /* Channel Number */ le16 rand_interval; /* Randomization Interval (in TUs) */ le16 duration; /* Measurement Duration (in TUs) */ u8 mode; /* Measurement Mode */ u8 bssid[ETH_ALEN]; /* BSSID */ u8 variable[0]; /* Optional Subelements */ } STRUCT_PACKED; /* * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon * request */ enum beacon_report_mode { BEACON_REPORT_MODE_PASSIVE = 0, BEACON_REPORT_MODE_ACTIVE = 1, BEACON_REPORT_MODE_TABLE = 2, }; /* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */ /* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for * Beacon request */ #define WLAN_BEACON_REQUEST_SUBELEM_SSID 0 #define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */ #define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */ #define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10 #define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */ #define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221 /* * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values */ enum beacon_report_detail { /* No fixed-length fields or elements */ BEACON_REPORT_DETAIL_NONE = 0, /* All fixed-length fields and any requested elements in the Request * element if present */ BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1, /* All fixed-length fields and elements (default, used when Reporting * Detail subelement is not included in a Beacon request) */ BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2, }; /* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */ struct rrm_measurement_report_element { u8 eid; /* Element ID */ u8 len; /* Length */ u8 token; /* Measurement Token */ u8 mode; /* Measurement Report Mode */ u8 type; /* Measurement Type */ u8 variable[0]; /* Measurement Report */ } STRUCT_PACKED; /* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */ #define MEASUREMENT_REPORT_MODE_ACCEPT 0 #define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0) #define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1) #define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2) /* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */ struct rrm_measurement_beacon_report { u8 op_class; /* Operating Class */ u8 channel; /* Channel Number */ le64 start_time; /* Actual Measurement Start Time * (in TSF of the BSS requesting the measurement) */ le16 duration; /* in TUs */ u8 report_info; /* Reported Frame Information */ u8 rcpi; /* RCPI */ u8 rsni; /* RSNI */ u8 bssid[ETH_ALEN]; /* BSSID */ u8 antenna_id; /* Antenna ID */ le32 parent_tsf; /* Parent TSF */ u8 variable[0]; /* Optional Subelements */ } STRUCT_PACKED; /* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */ /* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for * Beacon report */ #define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1 #define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2 #define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221 /* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the * Reported Frame Body Fragment ID subelement */ #define REPORTED_FRAME_BODY_SUBELEM_LEN 4 #define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7) /* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */ #define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3 /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ u8 len; u8 mb_ctrl; u8 band_id; u8 op_class; u8 chan; u8 bssid[ETH_ALEN]; le16 beacon_int; u8 tsf_offs[8]; u8 mb_connection_capability; u8 fst_session_tmout; /* Optional: * STA MAC Address * Pairwise Cipher Suite Count * Pairwise Cipher Suite List */ u8 variable[0]; } STRUCT_PACKED; enum mb_ctrl_sta_role { MB_STA_ROLE_AP = 0, MB_STA_ROLE_TDLS_STA = 1, MB_STA_ROLE_IBSS_STA = 2, MB_STA_ROLE_PCP = 3, MB_STA_ROLE_NON_PCP_NON_AP = 4 }; #define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2)) #define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK)) #define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3))) #define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4))) enum mb_band_id { MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */ MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */ MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */ }; #define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0))) #define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1))) #define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2))) #define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3))) #define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4))) /* IEEE Std 802.11ad-2014 - FST Action field */ enum fst_action { FST_ACTION_SETUP_REQUEST = 0, FST_ACTION_SETUP_RESPONSE = 1, FST_ACTION_TEAR_DOWN = 2, FST_ACTION_ACK_REQUEST = 3, FST_ACTION_ACK_RESPONSE = 4, FST_ACTION_ON_CHANNEL_TUNNEL = 5, }; /* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */ enum phy_type { PHY_TYPE_UNSPECIFIED = 0, PHY_TYPE_FHSS = 1, PHY_TYPE_DSSS = 2, PHY_TYPE_IRBASEBAND = 3, PHY_TYPE_OFDM = 4, PHY_TYPE_HRDSSS = 5, PHY_TYPE_ERP = 6, PHY_TYPE_HT = 7, PHY_TYPE_DMG = 8, PHY_TYPE_VHT = 9, }; /* IEEE P802.11-REVmd/D3.0, 9.4.2.36 - Neighbor Report element */ /* BSSID Information Field */ #define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0) #define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1) #define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1)) #define NEI_REP_BSSID_INFO_SECURITY BIT(2) #define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3) #define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4) #define NEI_REP_BSSID_INFO_QOS BIT(5) #define NEI_REP_BSSID_INFO_APSD BIT(6) #define NEI_REP_BSSID_INFO_RM BIT(7) #define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8) #define NEI_REP_BSSID_INFO_IMM_BA BIT(9) #define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10) #define NEI_REP_BSSID_INFO_HT BIT(11) #define NEI_REP_BSSID_INFO_VHT BIT(12) #define NEI_REP_BSSID_INFO_FTM BIT(13) #define NEI_REP_BSSID_INFO_HE BIT(14) /* * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information * subfields. * Note: These definitions are not the same as other CHANWIDTH_*. */ enum nr_chan_width { NR_CHAN_WIDTH_20 = 0, NR_CHAN_WIDTH_40 = 1, NR_CHAN_WIDTH_80 = 2, NR_CHAN_WIDTH_160 = 3, NR_CHAN_WIDTH_80P80 = 4, }; struct ieee80211_he_capabilities { u8 he_mac_capab_info[6]; u8 he_phy_capab_info[11]; /* Followed by 4, 8, or 12 octets of Supported HE-MCS And NSS Set field * and optional variable length PPE Thresholds field. */ u8 optional[37]; } STRUCT_PACKED; #define IEEE80211_HE_CAPAB_MIN_LEN (6 + 11) struct ieee80211_he_operation { le32 he_oper_params; /* HE Operation Parameters[3] and * BSS Color Information[1] */ le16 he_mcs_nss_set; /* Followed by conditional VHT Operation Information (3 octets), * Max Co-Hosted BSSID Indicator subfield (1 octet), and/or 6 GHz * Operation Information subfield (5 octets). */ } STRUCT_PACKED; /* IEEE P802.11ax/D6.0, Figure 9-787k - 6 GHz Operation Information field */ struct ieee80211_he_6ghz_oper_info { u8 primary_chan; u8 control; u8 chan_center_freq_seg0; u8 chan_center_freq_seg1; u8 min_rate; } STRUCT_PACKED; #define HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK (BIT(0) | BIT(1)) #define HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON BIT(2) /* IEEE P802.11ax/D6.0, 9.4.2.261 HE 6 GHz Band Capabilities element */ struct ieee80211_he_6ghz_band_cap { /* Minimum MPDU Start Spacing B0..B2 * Maximum A-MPDU Length Exponent B3..B5 * Maximum MPDU Length B6..B7 */ le16 capab; } STRUCT_PACKED; #define HE_6GHZ_BAND_CAP_MIN_MPDU_START (BIT(0) | BIT(1) | BIT(2)) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_16K BIT(3) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_32K BIT(4) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_64K (BIT(3) | BIT(4)) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_128K BIT(5) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_256K (BIT(3) | BIT(5)) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_512K (BIT(4) | BIT(5)) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_1024K (BIT(3) | BIT(4) | BIT(5)) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK (BIT(3) | BIT(4) | BIT(5)) #define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT 3 #define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_7991 BIT(6) #define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_11454 BIT(7) #define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK (BIT(6) | BIT(7)) #define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT 6 #define HE_6GHZ_BAND_CAP_SMPS_MASK (BIT(9) | BIT(10)) #define HE_6GHZ_BAND_CAP_SMPS_STATIC 0 #define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC BIT(9) #define HE_6GHZ_BAND_CAP_SMPS_DISABLED (BIT(9) | BIT(10)) #define HE_6GHZ_BAND_CAP_RD_RESPONDER BIT(11) #define HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS BIT(12) #define HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS BIT(13) /* * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element */ struct ieee80211_spatial_reuse { u8 sr_ctrl; /* SR Control */ /* Up to 19 octets of parameters: * Non-SRG OBSS PD Max Offset[0 or 1] * SRG OBSS PD Min Offset[0 or 1] * SRG OBSS PD Max Offset[0 or 1] * SRG BSS Color Bitmap[0 or 8] * SRG Partial BSSID Bitmap[0 or 8] */ u8 params[19]; } STRUCT_PACKED; /* HE Capabilities Information defines */ #define HE_MACCAP_TWT_RESPONDER ((u8) BIT(2)) #define HE_PHYCAP_CHANNEL_WIDTH_SET_IDX 0 #define HE_PHYCAP_CHANNEL_WIDTH_MASK ((u8) (BIT(1) | BIT(2) | \ BIT(3) | BIT(4))) #define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G ((u8) BIT(1)) #define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G ((u8) BIT(2)) #define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3)) #define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4)) #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3 #define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7)) #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4 #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB ((u8) BIT(0)) #define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4 #define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1)) #define HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX 6 #define HE_PHYCAP_PPE_THRESHOLD_PRESENT ((u8) BIT(7)) /* HE PPE Threshold define */ #define HE_PPE_THRES_RU_INDEX_BITMASK_MASK 0xf #define HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT 3 #define HE_PPE_THRES_NSS_MASK 0x7 /* HE Operation defines */ /* HE Operation Parameters and BSS Color Information fields */ #define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(0) | BIT(1) | \ BIT(2))) #define HE_OPERATION_DFLT_PE_DURATION_OFFSET 0 #define HE_OPERATION_TWT_REQUIRED ((u32) BIT(3)) #define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(4) | BIT(5) | \ BIT(6) | BIT(7) | \ BIT(8) | BIT(9) | \ BIT(10) | BIT(11) | \ BIT(12) | BIT(13))) #define HE_OPERATION_RTS_THRESHOLD_OFFSET 4 #define HE_OPERATION_VHT_OPER_INFO ((u32) BIT(14)) #define HE_OPERATION_COHOSTED_BSS ((u32) BIT(15)) #define HE_OPERATION_ER_SU_DISABLE ((u32) BIT(16)) #define HE_OPERATION_6GHZ_OPER_INFO ((u32) BIT(17)) #define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(24) | BIT(25) | \ BIT(26) | BIT(27) | \ BIT(28) | BIT(29))) #define HE_OPERATION_BSS_COLOR_PARTIAL ((u32) BIT(30)) #define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(31)) #define HE_OPERATION_BSS_COLOR_OFFSET 24 /* Spatial Reuse defines */ #define SPATIAL_REUSE_SRP_DISALLOWED BIT(0) #define SPATIAL_REUSE_NON_SRG_OBSS_PD_SR_DISALLOWED BIT(1) #define SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT BIT(2) #define SPATIAL_REUSE_SRG_INFORMATION_PRESENT BIT(3) #define SPATIAL_REUSE_HESIGA_SR_VAL15_ALLOWED BIT(4) struct ieee80211_he_mu_edca_parameter_set { u8 he_qos_info; u8 he_mu_ac_be_param[3]; u8 he_mu_ac_bk_param[3]; u8 he_mu_ac_vi_param[3]; u8 he_mu_ac_vo_param[3]; } STRUCT_PACKED; /* HE MU AC parameter record field format */ /* ACI/AIFSN */ #define HE_MU_AC_PARAM_ACI_IDX 0 #define HE_MU_AC_PARAM_AIFSN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3))) #define HE_MU_AC_PARAM_ACM ((u8) BIT(4)) #define HE_MU_AC_PARAM_ACI ((u8) (BIT(5) | BIT(6))) /* B7: Reserved */ /* ECWmin/ECWmax */ #define HE_MU_AC_PARAM_ECW_IDX 1 #define HE_MU_AC_PARAM_ECWMIN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3))) #define HE_MU_AC_PARAM_ECWMAX ((u8) (BIT(4) | BIT(5) | BIT(6) | BIT(7))) /* MU EDCA Timer */ #define HE_MU_AC_PARAM_TIMER_IDX 2 /* HE QoS Info field */ #define HE_QOS_INFO_EDCA_PARAM_SET_COUNT ((u8) (BIT(0) | BIT(1) | \ BIT(2) | BIT(3))) #define HE_QOS_INFO_Q_ACK ((u8) (BIT(4))) #define HE_QOS_INFO_QUEUE_REQUEST ((u8) (BIT(5))) #define HE_QOS_INFO_TXOP_REQUEST ((u8) (BIT(6))) /* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */ #define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7))) /* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */ #define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6 #define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET 7 /* IEEE P802.11ay/D4.0, 29.3.4 - Channelization */ enum edmg_channel { EDMG_CHANNEL_9 = 9, EDMG_CHANNEL_10 = 10, EDMG_CHANNEL_11 = 11, EDMG_CHANNEL_12 = 12, EDMG_CHANNEL_13 = 13, }; /* Represent CB2 contiguous channels */ #define EDMG_CHANNEL_9_SUBCHANNELS (BIT(0) | BIT(1)) /* channels 1 and 2 */ #define EDMG_CHANNEL_10_SUBCHANNELS (BIT(1) | BIT(2)) /* channels 2 and 3 */ #define EDMG_CHANNEL_11_SUBCHANNELS (BIT(2) | BIT(3)) /* channels 3 and 4 */ #define EDMG_CHANNEL_12_SUBCHANNELS (BIT(3) | BIT(4)) /* channels 4 and 5 */ #define EDMG_CHANNEL_13_SUBCHANNELS (BIT(4) | BIT(5)) /* channels 5 and 6 */ /** * enum edmg_bw_config - Allowed channel bandwidth configurations * @EDMG_BW_CONFIG_4: 2.16 GHz * @EDMG_BW_CONFIG_5: 2.16 GHz and 4.32 GHz * * IEEE P802.11ay/D4.0, 9.4.2.251 (EDMG Operation element), * Table 13 (Channel BW Configuration subfield definition) */ enum edmg_bw_config { EDMG_BW_CONFIG_4 = 4, EDMG_BW_CONFIG_5 = 5, }; /* DPP Public Action frame identifiers - OUI_WFA */ #define DPP_OUI_TYPE 0x1A /* Robust AV streaming Action field values */ enum robust_av_streaming_action { ROBUST_AV_SCS_REQ = 0, ROBUST_AV_SCS_RESP = 1, ROBUST_AV_GROUP_MEMBERSHIP_REQ = 2, ROBUST_AV_GROUP_MEMBERSHIP_RESP = 3, ROBUST_AV_MSCS_REQ = 4, ROBUST_AV_MSCS_RESP = 5, }; enum scs_request_type { SCS_REQ_ADD = 0, SCS_REQ_REMOVE = 1, SCS_REQ_CHANGE = 2, }; /* Optional subelement IDs for MSCS Descriptor element */ enum mscs_description_subelem { MCSC_SUBELEM_STATUS = 1, }; /* * IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format, * Figure 9-687b - FILS Discovery Frame Control subfield format */ #define FD_FRAME_CTL_CAP_PRESENT ((u16) BIT(5)) #define FD_FRAME_CTL_SHORT_SSID_PRESENT ((u16) BIT(6)) #define FD_FRAME_CTL_AP_CSN_PRESENT ((u16) BIT(7)) #define FD_FRAME_CTL_ANO_PRESENT ((u16) BIT(8)) #define FD_FRAME_CTL_FREQ_SEG1_PRESENT ((u16) BIT(9)) #define FD_FRAME_CTL_PRI_CHAN_PRESENT ((u16) BIT(10)) #define FD_FRAME_CTL_RSN_INFO_PRESENT ((u16) BIT(11)) #define FD_FRAME_CTL_LENGTH_PRESENT ((u16) BIT(12)) #define FD_FRAME_CTL_MD_PRESENT ((u16) BIT(13)) /* * IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format, * Figure 9-687c - FD Capability subfield format */ #define FD_CAP_ESS BIT(0) #define FD_CAP_PRIVACY BIT(1) #define FD_CAP_MULTI_BSSID_PRESENT BIT(9) #define FD_CAP_BSS_CHWIDTH_20 0 #define FD_CAP_BSS_CHWIDTH_40 1 #define FD_CAP_BSS_CHWIDTH_80 2 #define FD_CAP_BSS_CHWIDTH_160_80_80 3 #define FD_CAP_BSS_CHWIDTH_SHIFT 2 #define FD_CAP_NSS_1 0 #define FD_CAP_NSS_2 1 #define FD_CAP_NSS_3 2 #define FD_CAP_NSS_4 3 #define FD_CAP_NSS_5_8 4 #define FD_CAP_NSS_SHIFT 5 #define FD_CAP_PHY_INDEX_HR_DSSS 0 #define FD_CAP_PHY_INDEX_ERP_OFDM 1 #define FD_CAP_PHY_INDEX_HT 2 #define FD_CAP_PHY_INDEX_VHT 3 #define FD_CAP_PHY_INDEX_HE 4 /* P802.11ax */ #define FD_CAP_PHY_INDEX_SHIFT 10 /* * IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning */ #define FD_MAX_INTERVAL_6GHZ 20 /* TUs */ #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index ce588cc00a59..47666f04ae7c 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -1,11088 +1,11132 @@ /* * Qualcomm Atheros OUI and vendor specific assignments * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef QCA_VENDOR_H #define QCA_VENDOR_H /* * This file is a registry of identifier assignments from the Qualcomm Atheros * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers * can be assigned through normal review process for changes to the upstream * hostap.git repository. */ #define OUI_QCA 0x001374 #ifndef BIT #define BIT(x) (1U << (x)) #endif /** * enum qca_radiotap_vendor_ids - QCA radiotap vendor namespace IDs */ enum qca_radiotap_vendor_ids { QCA_RADIOTAP_VID_WLANTEST = 0, }; /** * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers * * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0 * * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event * * @QCA_NL80211_VENDOR_SUBCMD_ROAMING: Set roaming policy for drivers that use * internal BSS-selection. This command uses * @QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy * for the current connection (i.e., changes policy set by the nl80211 * Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be * included to indicate which BSS to use in case roaming is disabled. * * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency * ranges to avoid to reduce issues due to interference or internal * co-existence information in the driver. These frequencies aim to * minimize the traffic but not to totally avoid the traffic. That said * for a P2P use case, these frequencies are allowed for the P2P * discovery/negotiation but avoid the group to get formed on these * frequencies. The event data structure is defined in * struct qca_avoid_freq_list. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support * for DFS offloading. * * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass * NAN Request/Response and NAN Indication messages. These messages are * interpreted between the framework and the firmware component. While * sending the command from userspace to the driver, payload is not * encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN * is used when receiving vendor events in userspace from the driver. * * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be * used to configure PMK to the driver even when not connected. This can * be used to request offloading of key management operations. Only used * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. * * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of * NL80211_CMD_ROAM event with optional attributes including information * from offloaded key management operation. Uses * enum qca_wlan_vendor_attr_roam_auth attributes. Only used * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. * * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to * invoke the ACS function in device and pass selected channels to * hostapd. Uses enum qca_wlan_vendor_attr_acs_offload attributes. * * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features * supported by the driver. enum qca_wlan_vendor_features defines * the possible features. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver, * which supports DFS offloading, to indicate a channel availability check * start. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver, * which supports DFS offloading, to indicate a channel availability check * completion. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver, * which supports DFS offloading, to indicate that the channel availability * check aborted, no change to the channel status. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by * driver, which supports DFS offloading, to indicate that the * Non-Occupancy Period for this channel is over, channel becomes usable. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver, * which supports DFS offloading, to indicate a radar pattern has been * detected. The channel is now unusable. * * @QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO: Get information from the driver. * Attributes defined in enum qca_wlan_vendor_attr_get_wifi_info. * * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap * based on enum wifi_logger_supported_features. Attributes defined in * enum qca_wlan_vendor_attr_get_logger_features. * * @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular * logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the * attribute for this command. Attributes defined in * enum qca_wlan_vendor_attr_wifi_logger_start. * * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS * capabilities of the driver, parameters includes the attributes defined * in enum qca_wlan_vendor_attr_tdls_get_capabilities. * * @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload * sending of certain periodic IP packet to firmware, attributes defined in * enum qca_wlan_vendor_attr_offloaded_packets. * * @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI * monitoring, defines min and max RSSI which are configured for RSSI * monitoring. Also used to notify the RSSI breach and provides the BSSID * and RSSI value that was breached. Attributes defined in * enum qca_wlan_vendor_attr_rssi_monitoring. * * @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN * Data Path (NDP) related operations, attributes defined in * enum qca_wlan_vendor_attr_ndp_params. * * @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable * Neighbour Discovery offload, attributes defined in * enum qca_wlan_vendor_attr_nd_offload. * * @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various * configuration parameter for BPF packet filter, attributes defined in * enum qca_wlan_vendor_attr_packet_filter. * * @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware * maximum supported size, attributes defined in * enum qca_wlan_vendor_drv_info. * * @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various * data about wake reasons and datapath IP statistics, attributes defined * in enum qca_wlan_vendor_attr_wake_stats. * * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration * for IEEE 802.11 communicating outside the context of a basic service * set, called OCB command. Uses the attributes defines in * enum qca_wlan_vendor_attr_ocb_set_config. * * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB * UTC time. Use the attributes defines in * enum qca_wlan_vendor_attr_ocb_set_utc_time. * * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start * sending OCB timing advert frames. Uses the attributes defines in * enum qca_wlan_vendor_attr_ocb_start_timing_advert. * * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop * OCB timing advert. Uses the attributes defines in * enum qca_wlan_vendor_attr_ocb_stop_timing_advert. * * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF * timer value. Uses the attributes defines in * enum qca_wlan_vendor_attr_ocb_get_tsf_resp. * * @QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES: Command/event to update the * link properties of the respective interface. As an event, is used * to notify the connected station's status. The attributes for this * command are defined in enum qca_wlan_vendor_attr_link_properties. * * @QCA_NL80211_VENDOR_SUBCMD_SETBAND: Command to configure the enabled band(s) * to the driver. This command sets the band(s) through either the * attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE or * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK (or both). * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE refers enum qca_set_band as unsigned * integer values and QCA_WLAN_VENDOR_ATTR_SETBAND_MASK refers it as 32 * bit unsigned bitmask values. The allowed values for * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE are limited to QCA_SETBAND_AUTO, * QCA_SETBAND_5G, and QCA_SETBAND_2G. Other values/bitmasks are valid for * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. The attribute * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is deprecated and the recommendation * is to use the QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. If the both attributes * are included for backwards compatibility, the configurations through * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK will take the precedence with drivers * that support both attributes. * * @QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY: This command is used to configure * DFS policy and channel hint for ACS operation. This command uses the * attributes defined in enum qca_wlan_vendor_attr_acs_config and * enum qca_acs_dfs_mode. * * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to * start the P2P Listen offload function in device and pass the listen * channel, period, interval, count, device types, and vendor specific * information elements to the device driver and firmware. * Uses the attributes defines in * enum qca_wlan_vendor_attr_p2p_listen_offload. * * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to * indicate stop request/response of the P2P Listen offload function in * device. As an event, it indicates either the feature stopped after it * was already running or feature has actually failed to start. Uses the * attributes defines in enum qca_wlan_vendor_attr_p2p_listen_offload. * * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After AP starts * beaconing, this sub command provides the driver, the frequencies on the * 5 GHz band to check for any radar activity. Driver selects one channel * from this priority list provided through * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts * to check for radar activity on it. If no radar activity is detected * during the channel availability check period, driver internally switches * to the selected frequency of operation. If the frequency is zero, driver * internally selects a channel. The status of this conditional switch is * indicated through an event using the same sub command through * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are * listed in qca_wlan_vendor_attr_sap_conditional_chan_switch. * * @QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND: Set GPIO pins. This uses the * attributes defined in enum qca_wlan_gpio_attr. * * @QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY: Fetch hardware capabilities. * This uses @QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY to indicate which * capabilities are to be fetched and other * enum qca_wlan_vendor_attr_get_hw_capability attributes to return the * requested capabilities. * * @QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT: Link layer statistics extension. * enum qca_wlan_vendor_attr_ll_stats_ext attributes are used with this * command and event. * * @QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA: Get capabilities for * indoor location features. Capabilities are reported in * QCA_WLAN_VENDOR_ATTR_LOC_CAPA. * * @QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION: Start an FTM * (fine timing measurement) session with one or more peers. * Specify Session cookie in QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE and * peer information in QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS. * On success, 0 or more QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT * events will be reported, followed by * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE event to indicate * end of session. * Refer to IEEE P802.11-REVmc/D7.0, 11.24.6 * * @QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION: Abort a running session. * A QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE will be reported with * status code indicating session was aborted. * * @QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT: Event with measurement * results for one peer. Results are reported in * QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS. * * @QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE: Event triggered when * FTM session is finished, either successfully or aborted by * request. * * @QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER: Configure FTM responder * mode. QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE specifies whether * to enable or disable the responder. LCI/LCR reports can be * configured with QCA_WLAN_VENDOR_ATTR_FTM_LCI and * QCA_WLAN_VENDOR_ATTR_FTM_LCR. Can be called multiple * times to update the LCI/LCR reports. * * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of * arrival) measurement with a single peer. Specify peer MAC address in * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in * QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel * scan results cache and use the frequency from there). * Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE. * Measurement result is reported in * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event. * * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify * peer MAC address in QCA_WLAN_VENDOR_ATTR_MAC_ADDR. * * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT: Event that reports * the AOA measurement result. * Peer MAC address reported in QCA_WLAN_VENDOR_ATTR_MAC_ADDR. * success/failure status is reported in * QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS. * Measurement data is reported in QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT. * The antenna array(s) used in the measurement are reported in * QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK. * * @QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST: Encrypt/decrypt the given * data as per the given parameters. * * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a * specific chain. * * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level * configuration for a DMG RF sector. Specify sector index in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules * to return sector information for in * QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the * exact time where information was captured in * QCA_WLAN_VENDOR_ATTR_TSF. * * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level * configuration for a DMG RF sector. Specify sector index in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration * for one or more DMG RF modules in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. * * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected * DMG RF sector for a station. This is the sector that the HW * will use to communicate with the station. Specify the MAC address * of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not * needed for unassociated station). Specify sector type to return in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected * sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX. * Also return the exact time where the information was captured * in QCA_WLAN_VENDOR_ATTR_TSF. * * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the * selected DMG RF sector for a station. This is the sector that * the HW will use to communicate with the station. * Specify the MAC address of associated station/AP/PCP in * QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX. * The selected sector will be locked such that it will not be * modified like it normally does (for example when station * moves around). To unlock the selected sector for a station * pass the special value 0xFFFF in the sector index. To unlock * all connected stations also pass a broadcast MAC address. * * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior * in the host driver. The different TDLS configurations are defined * by the attributes in enum qca_wlan_vendor_attr_tdls_configuration. * * @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE * capabilities. The response uses the attributes defined in * enum qca_wlan_vendor_attr_get_he_capabilities. * * @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was * started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command * carries the scan cookie of the corresponding scan request. The scan * cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE. * * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific * Absorption Rate (SAR) power limits. A critical regulation for * FCC compliance, OEMs require methods to set SAR limits on TX * power of WLAN/WWAN. enum qca_vendor_attr_sar_limits * attributes are used with this command. * * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the * host driver for offloading the implementation of Auto Channel Selection * (ACS) to an external user space entity. This interface is used as the * event from the host driver to the user space entity and also as the * request from the user space entity to the host driver. The event from * the host driver is used by the user space entity as an indication to * start the ACS functionality. The attributes used by this event are * represented by the enum qca_wlan_vendor_attr_external_acs_event. * User space entity uses the same interface to inform the host driver with * selected channels after the ACS operation using the attributes defined * by enum qca_wlan_vendor_attr_external_acs_channels. * * @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the * requisite information leading to a power save failure. The information * carried as part of this event is represented by the * enum qca_attr_chip_power_save_failure attributes. * * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics * collection. Uses attributes defined in enum qca_attr_nud_stats_set. * * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These * statistics are represented by the enum qca_attr_nud_stats_get * attributes. * * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch * the BSS transition status, whether accept or reject, for a list of * candidate BSSIDs provided by the userspace. This uses the vendor * attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify * the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an * array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response * the driver shall specify array of * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. * * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a * specific QCA module. The trace levels are represented by * enum qca_attr_trace_level attributes. * * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement * Protocol antenna limit in different modes. See enum * qca_wlan_vendor_attr_brp_ant_limit_mode. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan * parameters are specified by enum qca_wlan_vendor_attr_spectral_scan. * This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) * identifying the operation in success case. In failure cases an * error code (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE) * describing the reason for the failure is returned. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses * a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to * be stopped. * * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the * specific interface. This can be used to modify some of the low level * scan parameters (off channel dwell time, home channel time) in the * driver/firmware. These parameters are maintained within the host driver. * This command is valid only when the interface is in the connected state. * These scan parameters shall be reset by the driver/firmware once * disconnected. The attributes used with this command are defined in * enum qca_wlan_vendor_attr_active_tos. * * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the * driver has detected an internal failure. This event carries the * information indicating the reason that triggered this detection. The * attributes for this command are defined in * enum qca_wlan_vendor_attr_hang. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values * of spectral parameters used. The spectral scan parameters are specified * by enum qca_wlan_vendor_attr_spectral_scan. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats * for spectral scan functionality. The debug stats are specified by * enum qca_wlan_vendor_attr_spectral_diag_stats. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral * scan system capabilities. The capabilities are specified * by enum qca_wlan_vendor_attr_spectral_cap. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current * status of spectral scan. The status values are specified * by enum qca_wlan_vendor_attr_spectral_scan_status. * * @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush * peer pending packets. Specify the peer MAC address in * QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets * in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed * in enum qca_wlan_vendor_attr_flush_pending. * * @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative * RF Operating Parameter (RROP) information. The attributes for this * information are defined in enum qca_wlan_vendor_attr_rrop_info. This is * intended for use by external Auto Channel Selection applications. * * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate * (SAR) power limits. This is a companion to the command * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the * settings currently in use. The attributes returned by this command are * defined by enum qca_vendor_attr_sar_limits. * * @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of * the WLAN hardware MAC. Also, provides the WLAN netdev interface * information attached to the respective MAC. * This works both as a query (user space asks the current mode) or event * interface (driver advertising the current mode to the user space). * Driver does not trigger this event for temporary hardware mode changes. * Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion, * channel change, etc.) are updated with this event. Attributes for this * interface are defined in enum qca_wlan_vendor_attr_mac. * * @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold * per peer per TID. Attributes for this command are define in * enum qca_wlan_set_qdepth_thresh_attr. * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action * guide for WLAN driver. Request to suspend of driver and FW if the * temperature is higher than the suspend threshold; resume action is * requested to driver if the temperature is lower than the resume * threshold. In user poll mode, request temperature data by user. For test * purpose, getting thermal shutdown configuration parameters is needed. * Attributes for this interface are defined in * enum qca_wlan_vendor_attr_thermal_cmd. * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from * driver. Thermal temperature and indication of resume completion are * reported as thermal events. The attributes for this command are defined * in enum qca_wlan_vendor_attr_thermal_event. * * @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi * test configuration. Attributes for this command are defined in * enum qca_wlan_vendor_attr_wifi_test_config. * * @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an * RX filter to receive frames from stations that are active on the * operating channel, but not associated with the local device (e.g., STAs * associated with other APs). Filtering is done based on a list of BSSIDs * and STA MAC addresses added by the user. This command is also used to * fetch the statistics of unassociated stations. The attributes used with * this command are defined in enum qca_wlan_vendor_attr_bss_filter. * * @QCA_NL80211_VENDOR_SUBCMD_NAN_EXT: An extendable version of NAN vendor * command. The earlier command for NAN, QCA_NL80211_VENDOR_SUBCMD_NAN, * carried a payload which was a binary blob of data. The command was not * extendable to send more information. The newer version carries the * legacy blob encapsulated within an attribute and can be extended with * additional vendor attributes that can enhance the NAN command interface. * @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered * or stopped within driver/firmware in order to initiate roaming. The * attributes used with this event are defined in enum * qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events * in few cases, e.g., if the host processor is sleeping when this event * is generated in firmware. * * @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to * configure parameters per peer to capture Channel Frequency Response * (CFR) and enable Periodic CFR capture. The attributes for this command * are defined in enum qca_wlan_vendor_peer_cfr_capture_attr. This command * can also be used to send CFR data from the driver to userspace when * netlink events are used to send CFR data. * * @QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT: Event to indicate changes * in throughput dynamically. The driver estimates the throughput based on * number of packets being transmitted/received per second and indicates * the changes in throughput to user space. Userspace tools can use this * information to configure kernel's TCP parameters in order to achieve * peak throughput. Optionally, the driver will also send guidance on * modifications to kernel's TCP parameters which can be referred by * userspace tools. The attributes used with this event are defined in enum * qca_wlan_vendor_attr_throughput_change. * * @QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG: This command is used to set * priorities among different types of traffic during coex scenarios. * Current supported prioritization is among WLAN/BT/ZIGBEE with different * profiles mentioned in enum qca_coex_config_profiles. The associated * attributes used with this command are defined in enum * qca_vendor_attr_coex_config. * * Based on the config provided, FW will boost the weight and prioritize * the traffic for that subsystem (WLAN/BT/Zigbee). * * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query * the supported AKM suite selectorss from the driver. It returns the list * of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES. * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware * state from the driver. It returns the firmware state in the attribute * QCA_WLAN_VENDOR_ATTR_FW_STATE. * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand * is used by the driver to flush per-peer cached statistics to user space * application. This interface is used as an event from the driver to * user space application. Attributes for this event are specified in * enum qca_wlan_vendor_attr_peer_stats_cache_params. * QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be * sent in the event. * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to * improve the success rate of Zigbee joining network. * Due to PTA master limitation, Zigbee joining network success rate is * low while WLAN is working. The WLAN driver needs to configure some * parameters including Zigbee state and specific WLAN periods to enhance * PTA master. All these parameters are delivered by the attributes * defined in enum qca_mpta_helper_vendor_attr. * @QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING: This sub command is used to * implement Beacon frame reporting feature. * * Userspace can request the driver/firmware to periodically report * received Beacon frames whose BSSID is same as the current connected * BSS's MAC address. * * In case the STA seamlessly (without sending disconnect indication to * userspace) roams to a different BSS, Beacon frame reporting will be * automatically enabled for the Beacon frames whose BSSID is same as the * MAC address of the new BSS. Beacon reporting will be stopped when the * STA is disconnected (when the disconnect indication is sent to * userspace) and need to be explicitly enabled by userspace for next * connection. * * When a Beacon frame matching configured conditions is received, and if * userspace has requested to send asynchronous beacon reports, the * driver/firmware will encapsulate the details of the Beacon frame in an * event and send it to userspace along with updating the BSS information * in cfg80211 scan cache, otherwise driver will only update the cfg80211 * scan cache with the information from the received Beacon frame but will * not send any active report to userspace. * * The userspace can request the driver/firmware to stop reporting Beacon * frames. If the driver/firmware is not able to receive Beacon frames due * to other Wi-Fi operations such as off-channel activities, etc., the * driver/firmware will send a pause event to userspace and stop reporting * Beacon frames. Whether the beacon reporting will be automatically * resumed or not by the driver/firmware later will be reported to * userspace using the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES * flag. The beacon reporting shall be resumed for all the cases except * either when userspace sets * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME flag in the command * which triggered the current beacon reporting or during any disconnection * case as indicated by setting * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON to * QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED by the * driver. * * After QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_PAUSE event is received * by userspace with QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES * flag not set, the next first * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO event from the driver * shall be considered as un-pause event. * * All the attributes used with this command are defined in * enum qca_wlan_vendor_attr_beacon_reporting_params. * @QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP: In practice, some APs have * interop issues with the DUT. This sub command is used to transfer the * AP info between the driver and user space. This works both as a command * and an event. As a command, it configures the stored list of APs from * user space to firmware; as an event, it indicates the AP info detected * by the firmware to user space for persistent storage. The attributes * defined in enum qca_vendor_attr_interop_issues_ap are used to deliver * the parameters. * @QCA_NL80211_VENDOR_SUBCMD_OEM_DATA: This command/event is used to * send/receive OEM data binary blobs to/from application/service to/from * firmware. The attributes defined in enum * qca_wlan_vendor_attr_oem_data_params are used to deliver the * parameters. * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT: This command/event is used * to send/receive avoid frequency data using * enum qca_wlan_vendor_attr_avoid_frequency_ext. * This new command is alternative to existing command * QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY since existing command/event * is using stream of bytes instead of structured data using vendor * attributes. * * @QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE: This vendor subcommand is used to * add the STA node details in driver/firmware. Attributes for this event * are specified in enum qca_wlan_vendor_attr_add_sta_node_params. * @QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE: This command is used to set BT * coex chain mode from application/service. * The attributes defined in enum qca_vendor_attr_btc_chain_mode are used * to deliver the parameters. * * @QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO: This vendor subcommand is used to * get information of a station from driver to userspace. This command can * be used in both STA and AP modes. For STA mode, it provides information * of the current association when in connected state or the last * association when in disconnected state. For AP mode, only information * of the currently connected stations is available. This command uses * attributes defined in enum qca_wlan_vendor_attr_get_sta_info. * * @QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_EVENT: This acts as an event. * Host drivers can request the user space entity to set the SAR power * limits with this event. Accordingly, the user space entity is expected * to set the SAR power limits. Host drivers can retry this event to the * user space for the SAR power limits configuration from user space. If * the driver does not get the SAR power limits from user space for all * the retried attempts, it can configure a default SAR power limit. * * @QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO: This acts as a vendor event and * is used to update the information about the station from the driver to * userspace. Uses attributes from enum * qca_wlan_vendor_attr_update_sta_info. * * @QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON: This acts as an event. * The host driver initiates the disconnection for scenarios such as beacon * miss, NUD failure, peer kick out, etc. The disconnection indication * through cfg80211_disconnected() expects the reason codes from enum * ieee80211_reasoncode which does not signify these various reasons why * the driver has triggered the disconnection. This event will be used to * send the driver specific reason codes by the host driver to userspace. * Host drivers should trigger this event and pass the respective reason * code immediately prior to triggering cfg80211_disconnected(). The * attributes used with this event are defined in enum * qca_wlan_vendor_attr_driver_disconnect_reason. * * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC: This vendor subcommand is used to * add/delete TSPEC for each AC. One command is for one specific AC only. * This command can only be used in STA mode and the STA must be * associated with an AP when the command is issued. Uses attributes * defined in enum qca_wlan_vendor_attr_config_tspec. * * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT: Vendor subcommand to configure TWT. * Uses attributes defined in enum qca_wlan_vendor_attr_config_twt. * * @QCA_NL80211_VENDOR_SUBCMD_GETBAND: Command to get the enabled band(s) from * the driver. The band configurations obtained are referred through * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. * * @QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS: Vendor subcommand/event for medium * assessment. * Uses attributes defined in enum qca_wlan_vendor_attr_medium_assess. * * @QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID: This acts as a vendor event and is * used to update SSID information in hostapd when it is updated in the * driver. Uses the attribute NL80211_ATTR_SSID. * * @QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS: This vendor subcommand is used by * the driver to send opaque data from the firmware to userspace. The * driver sends an event to userspace whenever such data is received from * the firmware. * * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA is used as the attribute to * send this opaque data for this event. * * The format of the opaque data is specific to the particular firmware * version and there is no guarantee of the format remaining same. * * @QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS: This acts as an event. * The host driver selects Tx VDEV, and notifies user. The attributes * used with this event are defined in enum * qca_wlan_vendor_attr_mbssid_tx_vdev_status. * * @QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY: Vendor command to * configure the concurrent session policies when multiple STA interfaces * are (getting) active. The attributes used by this command are defined * in enum qca_wlan_vendor_attr_concurrent_sta_policy. * * @QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS: Userspace can use this command * to query usable channels for different interface types such as STA, * AP, P2P GO, P2P Client, NAN, etc. The driver shall report all usable * channels in the response based on country code, different static * configurations, concurrency combinations, etc. The attributes used * with this command are defined in * enum qca_wlan_vendor_attr_usable_channels. * * @QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY: This vendor subcommand is used * to get DFS radar history from the driver to userspace. The driver * returns QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES attribute with an * array of nested entries. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, QCA_NL80211_VENDOR_SUBCMD_TEST = 1, /* subcmds 2..8 not yet allocated */ QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9, QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, QCA_NL80211_VENDOR_SUBCMD_NAN = 12, QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19, QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20, QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21, QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22, QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23, QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25, QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27, QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29, QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32, QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33, QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34, QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35, QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36, QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37, QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38, QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39, QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40, QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41, QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42, /* 43..49 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51, QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, /* 53 - reserved - was used by QCA, but not in use anymore */ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61, QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62, QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63, QCA_NL80211_VENDOR_SUBCMD_ROAM = 64, QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65, QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66, QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67, QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69, QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70, QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71, QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72, QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73, /* Wi-Fi configuration subcommands */ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74, QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75, QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76, QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77, QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78, QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79, QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80, QCA_NL80211_VENDOR_SUBCMD_NDP = 81, QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82, QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83, QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84, QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85, /* 86-90 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94, QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95, QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96, QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97, QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98, QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99, QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100, QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101, QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102, QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106, QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107, QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108, QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109, /* 110..114 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115, QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY = 116, /* 117 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118, QCA_NL80211_VENDOR_SUBCMD_TSF = 119, QCA_NL80211_VENDOR_SUBCMD_WISA = 120, /* 121 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122, QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123, QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124, QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND = 125, QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY = 126, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT = 127, /* FTM/indoor location subcommands */ QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130, QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131, QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132, QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133, QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134, QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135, QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137, QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138, /* DMG low level RF sector operations */ QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142, QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143, QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144, QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145, QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146, QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147, QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148, QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149, QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150, QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151, QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152, QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153, QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154, QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155, QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156, QCA_NL80211_VENDOR_SUBCMD_HANG = 157, QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158, QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159, QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160, QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161, /* Flush peer pending data */ QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162, QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163, QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164, QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165, QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166, /* Thermal shutdown commands to protect wifi chip */ QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167, QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168, /* Wi-Fi test configuration subcommand */ QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169, /* Frame filter operations for other BSSs/unassociated STAs */ QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170, QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171, QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172, QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173, QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174, QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175, QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176, QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177, QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178, QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179, QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING = 180, QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP = 181, QCA_NL80211_VENDOR_SUBCMD_OEM_DATA = 182, QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT = 183, QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE = 184, QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE = 185, QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO = 186, QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT = 187, QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO = 188, QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON = 189, QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC = 190, QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT = 191, QCA_NL80211_VENDOR_SUBCMD_GETBAND = 192, QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS = 193, QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID = 194, QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS = 195, QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS = 196, QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY = 197, QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS = 198, QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY = 199, }; enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_INVALID = 0, /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ QCA_WLAN_VENDOR_ATTR_DFS = 1, /* Used only when driver sends vendor events to the userspace under the * command QCA_NL80211_VENDOR_SUBCMD_NAN. Not used when userspace sends * commands to the driver. */ QCA_WLAN_VENDOR_ATTR_NAN = 2, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4, /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined * by enum qca_roaming_policy. */ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, QCA_WLAN_VENDOR_ATTR_TEST = 8, /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ /* Unsigned 32-bit value. */ QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11, /* Unsigned 32-bit value from enum qca_set_band. The allowed values for * this attribute are limited to QCA_SETBAND_AUTO, QCA_SETBAND_5G, and * QCA_SETBAND_2G. This attribute is deprecated. Recommendation is to * use QCA_WLAN_VENDOR_ATTR_SETBAND_MASK instead. */ QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12, /* Dummy (NOP) attribute for 64 bit padding */ QCA_WLAN_VENDOR_ATTR_PAD = 13, /* Unique FTM session cookie (Unsigned 64 bit). Specified in * QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in * the session in QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE. */ QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14, /* Indoor location capabilities, returned by * QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA. * see enum qca_wlan_vendor_attr_loc_capa. */ QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15, /* Array of nested attributes containing information about each peer * in FTM measurement session. See enum qca_wlan_vendor_attr_peer_info * for supported attributes for each peer. */ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16, /* Array of nested attributes containing measurement results for * one or more peers, reported by the * QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event. * See enum qca_wlan_vendor_attr_peer_result for list of supported * attributes. */ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17, /* Flag attribute for enabling or disabling responder functionality. */ QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18, /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER * command to specify the LCI report that will be sent by * the responder during a measurement exchange. The format is * defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.10. */ QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19, /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER * command to specify the location civic report that will * be sent by the responder during a measurement exchange. * The format is defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.13. */ QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20, /* Session/measurement completion status code, * reported in QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE and * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT * see enum qca_vendor_attr_loc_session_status. */ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21, /* Initial dialog token used by responder (0 if not specified), * unsigned 8 bit value. */ QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22, /* AOA measurement type. Requested in QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS * and optionally in QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if * AOA measurements are needed as part of an FTM session. * Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT. See * enum qca_wlan_vendor_attr_aoa_type. */ QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23, /* A bit mask (unsigned 32 bit value) of antenna arrays used * by indoor location measurements. Refers to the antenna * arrays described by QCA_VENDOR_ATTR_LOC_CAPA_ANTENNA_ARRAYS. */ QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24, /* AOA measurement data. Its contents depends on the AOA measurement * type and antenna array mask: * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values, * phase of the strongest CIR path for each antenna in the measured * array(s). * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16 * values, phase and amplitude of the strongest CIR path for each * antenna in the measured array(s). */ QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command * to specify the chain number (unsigned 32 bit value) to inquire * the corresponding antenna RSSI value */ QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command * to report the specific antenna RSSI value (unsigned 32 bit value) */ QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27, /* Frequency in MHz, various uses. Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_FREQ = 28, /* TSF timer value, unsigned 64 bit value. * May be returned by various commands. */ QCA_WLAN_VENDOR_ATTR_TSF = 29, /* DMG RF sector index, unsigned 16 bit number. Valid values are * 0..127 for sector indices or 65535 as special value used to * unlock sector selection in * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR. */ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30, /* DMG RF sector type, unsigned 8 bit value. One of the values * in enum qca_wlan_vendor_attr_dmg_rf_sector_type. */ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31, /* Bitmask of DMG RF modules for which information is requested. Each * bit corresponds to an RF module with the same index as the bit * number. Unsigned 32 bit number but only low 8 bits can be set since * all DMG chips currently have up to 8 RF modules. */ QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32, /* Array of nested attributes where each entry is DMG RF sector * configuration for a single RF module. * Attributes for each entry are taken from enum * qca_wlan_vendor_attr_dmg_rf_sector_cfg. * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG. */ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33, /* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command * to report frame aggregation statistics to userspace. */ QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34, QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35, /* Unsigned 8-bit value representing MBO transition reason code as * provided by the AP used by subcommand * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is * specified by the userspace in the request to the driver. */ QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36, /* Array of nested attributes, BSSID and status code, used by subcommand * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each * entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info. * The userspace space specifies the list/array of candidate BSSIDs in * the order of preference in the request. The driver specifies the * status code, for each BSSID in the list, in the response. The * acceptable candidates are listed in the order preferred by the * driver. */ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37, /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command * See enum qca_wlan_vendor_attr_brp_ant_limit_mode. */ QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38, /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command * to define the number of antennas to use for BRP. * different purpose in each ANT_LIMIT_MODE: * DISABLE - ignored * EFFECTIVE - upper limit to number of antennas to be used * FORCE - exact number of antennas to be used * unsigned 8 bit value */ QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command * to report the corresponding antenna index to the chain RSSI value */ QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report * the specific antenna EVM value (unsigned 32 bit value). With a * determinate group of antennas, the driver specifies the EVM value * for each antenna ID, and application extract them in user space. */ QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41, /* * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report * wlan firmware current state. FW state is an unsigned 8 bit value, * one of the values in enum qca_wlan_vendor_attr_fw_state. */ QCA_WLAN_VENDOR_ATTR_FW_STATE = 42, /* Unsigned 32-bitmask value from enum qca_set_band. Substitutes the * attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE for which only a subset * of single values from enum qca_set_band are valid. This attribute * uses bitmask combinations to define the respective allowed band * combinations and this attributes takes precedence over * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE if both attributes are included. */ QCA_WLAN_VENDOR_ATTR_SETBAND_MASK = 43, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, }; enum qca_roaming_policy { QCA_ROAMING_NOT_ALLOWED, QCA_ROAMING_ALLOWED_WITHIN_ESS, }; /** * enum qca_roam_reason - Represents the reason codes for roaming. Used by * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON. * * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below * reasons. * * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached * the configured threshold. * * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured * beacon misses from the then connected AP. * * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported * by the connected AP. * * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor. * * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel * or environment being very noisy or congested. * * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request * from the user (user space). * * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from * the connected AP. * * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization * breaching out the configured threshold. */ enum qca_roam_reason { QCA_ROAM_REASON_UNKNOWN, QCA_ROAM_REASON_PER, QCA_ROAM_REASON_BEACON_MISS, QCA_ROAM_REASON_POOR_RSSI, QCA_ROAM_REASON_BETTER_RSSI, QCA_ROAM_REASON_CONGESTION, QCA_ROAM_REASON_USER_TRIGGER, QCA_ROAM_REASON_BTM, QCA_ROAM_REASON_BSS_LOAD, }; enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, /* Indicates the status of re-association requested by user space for * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID. * Type u16. * Represents the status code from AP. Use * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the * real status code for failures. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS, /* This attribute indicates that the old association was maintained when * a re-association is requested by user space and that re-association * attempt fails (i.e., cannot connect to the requested BSS, but can * remain associated with the BSS with which the association was in * place when being requested to roam). Used along with * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current * re-association status. Type flag. * This attribute is applicable only for re-association failure cases. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION, /* This attribute specifies the PMK if one was newly generated during * FILS roaming. This is added to the PMKSA cache and is used in * subsequent connections with PMKSA caching. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11, /* This attribute specifies the PMKID used/generated for the current * FILS roam. This is used in subsequent connections with PMKSA caching. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12, /* A 16-bit unsigned value specifying the next sequence number to use * in ERP message in the currently associated realm. This is used in * doing subsequent ERP based connections in the same realm. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13, /* A 16-bit unsigned value representing the reasons for the roaming. * Defined by enum qca_roam_reason. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 }; enum qca_wlan_vendor_attr_p2p_listen_offload { QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0, /* A 32-bit unsigned value; the P2P listen frequency (MHz); must be one * of the social channels. */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL, /* A 32-bit unsigned value; the P2P listen offload period (ms). */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD, /* A 32-bit unsigned value; the P2P listen interval duration (ms). */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL, /* A 32-bit unsigned value; number of interval times the firmware needs * to run the offloaded P2P listen operation before it stops. */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT, /* An array of arbitrary binary data with one or more 8-byte values. * The device types include both primary and secondary device types. */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES, /* An array of unsigned 8-bit characters; vendor information elements. */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE, /* A 32-bit unsigned value; a control flag to indicate whether listen * results need to be flushed to wpa_supplicant. */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG, /* A 8-bit unsigned value; reason code for P2P listen offload stop * event. */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, /* keep last */ QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX = QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_acs_offload - Defines attributes to be used with * vendor command/event QCA_NL80211_VENDOR_SUBCMD_DO_ACS. * * @QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL: Required (u8). * Used with event to notify the primary channel number selected in ACS * operation. * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL is deprecated; use * QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY instead. * To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL * is still used if either of the driver or user space application doesn't * support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL: Required (u8). * Used with event to notify the secondary channel number selected in ACS * operation. * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL is deprecated; use * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY instead. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL is still used if either of * the driver or user space application doesn't support 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE: Required (u8). * (a) Used with command to configure hw_mode from * enum qca_wlan_vendor_acs_hw_mode for ACS operation. * (b) Also used with event to notify the hw_mode of selected primary channel * in ACS operation. * * @QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED: Flag attribute. * Used with command to configure ACS operation for HT mode. * Disable (flag attribute not present) - HT disabled and * Enable (flag attribute present) - HT enabled. * * @QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED: Flag attribute. * Used with command to configure ACS operation for HT40 mode. * Disable (flag attribute not present) - HT40 disabled and * Enable (flag attribute present) - HT40 enabled. * * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED: Flag attribute. * Used with command to configure ACS operation for VHT mode. * Disable (flag attribute not present) - VHT disabled and * Enable (flag attribute present) - VHT enabled. * * @QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH: Optional (u16) with command and * mandatory with event. * If specified in command path, ACS operation is configured with the given * channel width (in MHz). * In event path, specifies the channel width of the primary channel selected. * * @QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST: Required and type is NLA_UNSPEC. * Used with command to configure channel list using an array of * channel numbers (u8). * Note: If both the driver and user-space application supports the 6 GHz band, * the driver mandates use of QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST whereas * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is optional. * * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL: Required (u8). * Used with event to notify the VHT segment 0 center channel number selected in * ACS operation. The value is the index of the channel center frequency for * 20 MHz, 40 MHz, and 80 MHz channels. The value is the center frequency index * of the primary 80 MHz segment for 160 MHz and 80+80 MHz channels. * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL is deprecated; use * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY instead. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL is still used if either of * the driver or user space application doesn't support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL: Required (u8). * Used with event to notify the VHT segment 1 center channel number selected in * ACS operation. The value is zero for 20 MHz, 40 MHz, and 80 MHz channels. * The value is the index of the channel center frequency for 160 MHz channels * and the center frequency index of the secondary 80 MHz segment for 80+80 MHz * channels. * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL is deprecated; use * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY instead. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL is still used if either of * the driver or user space application doesn't support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST: Required and type is NLA_UNSPEC. * Used with command to configure the channel list using an array of channel * center frequencies in MHz (u32). * Note: If both the driver and user-space application supports the 6 GHz band, * the driver first parses the frequency list and if it fails to get a frequency * list, parses the channel list specified using * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST (considers only 2 GHz and 5 GHz channels in * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST). * * @QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY: Required (u32). * Used with event to notify the primary channel center frequency (MHz) selected * in ACS operation. * Note: If the driver supports the 6 GHz band, the event sent from the driver * includes this attribute along with QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL. * * @QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY: Required (u32). * Used with event to notify the secondary channel center frequency (MHz) * selected in ACS operation. * Note: If the driver supports the 6 GHz band, the event sent from the driver * includes this attribute along with * QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL. * * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY: Required (u32). * Used with event to notify the VHT segment 0 center channel frequency (MHz) * selected in ACS operation. * Note: If the driver supports the 6 GHz band, the event sent from the driver * includes this attribute along with * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL. * * @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY: Required (u32). * Used with event to notify the VHT segment 1 center channel frequency (MHz) * selected in ACS operation. * Note: If the driver supports the 6 GHz band, the event sent from the driver * includes this attribute along with * QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL. * * @QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED: Flag attribute. * Used with command to notify the driver of EDMG request for ACS * operation. * * @QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL: Optional (u8). * Used with event to notify the EDMG channel number selected in ACS * operation. * EDMG primary channel is indicated by QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL */ enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL = 1, QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL = 2, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE = 3, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED = 4, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED = 5, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED = 6, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH = 7, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST = 8, QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL = 9, QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL = 10, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST = 11, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY = 12, QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY = 13, QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY = 14, QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY = 15, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED = 16, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL = 17, /* keep last */ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ACS_MAX = QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_acs_hw_mode - Defines HW mode to be used with the * vendor command/event QCA_NL80211_VENDOR_SUBCMD_DO_ACS. * * @QCA_ACS_MODE_IEEE80211B: 802.11b mode * @QCA_ACS_MODE_IEEE80211G: 802.11g mode * @QCA_ACS_MODE_IEEE80211A: 802.11a mode * @QCA_ACS_MODE_IEEE80211AD: 802.11ad mode * @QCA_ACS_MODE_IEEE80211ANY: all modes * @QCA_ACS_MODE_IEEE80211AX: 802.11ax mode */ enum qca_wlan_vendor_acs_hw_mode { QCA_ACS_MODE_IEEE80211B, QCA_ACS_MODE_IEEE80211G, QCA_ACS_MODE_IEEE80211A, QCA_ACS_MODE_IEEE80211AD, QCA_ACS_MODE_IEEE80211ANY, QCA_ACS_MODE_IEEE80211AX, }; /** * enum qca_wlan_vendor_features - Vendor device/driver feature flags * * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key * management offload, a mechanism where the station's firmware * does the exchange with the AP to establish the temporal keys * after roaming, rather than having the user space wpa_supplicant do it. * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic * band selection based on channel selection results. * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports * simultaneous off-channel operations. * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P * Listen offload; a mechanism where the station's firmware takes care of * responding to incoming Probe Request frames received from other P2P * Devices whilst in Listen state, rather than having the user space * wpa_supplicant do it. Information from received P2P requests are * forwarded from firmware to host whenever the host processor wakes up. * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA * specific features. * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific * features. * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON * specific features only. If a Device sets this bit but not the * %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that * this Device may not support all OCE AP functionalities but can support * only OCE STA-CFON functionalities. * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self * managed regulatory. * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time). * @QCA_WLAN_VENDOR_FEATURE_11AX: Device supports 802.11ax (HE) * @QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT: Device supports 6 GHz band operation * @QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG: Device is capable of receiving * and applying thermal configuration through * %QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL and * %QCA_WLAN_VENDOR_ATTR_THERMAL_COMPLETION_WINDOW attributes from * userspace. * @QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R: Device supports Adaptive 11r. * With Adaptive 11r feature, access points advertise the vendor * specific IEs and MDE but do not include FT AKM in the RSNE. * The Adaptive 11r supported stations are expected to identify * such vendor specific IEs and connect to the AP in FT mode though * the profile is configured in non-FT mode. * The driver-based SME cases also need to have this support for * Adaptive 11r to handle the connection and roaming scenarios. * This flag indicates the support for the same to the user space. * @QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS: Device supports * concurrent network sessions on different Wi-Fi bands. This feature * capability is attributed to the hardware's capability to support * the same (e.g., DBS). * @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT: Flag indicating whether the * responses for the respective TWT operations are asynchronous (separate * event message) from the driver. If not specified, the responses are * synchronous (in vendor command reply) to the request. Each TWT * operation is specifically mentioned (against its respective * documentation) to support either of these or both modes. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3, QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4, QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5, QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6, QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7, QCA_WLAN_VENDOR_FEATURE_TWT = 8, QCA_WLAN_VENDOR_FEATURE_11AX = 9, QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT = 10, QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG = 11, QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R = 12, QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS = 13, QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT = 14, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; /** * enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication * * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to * the offloaded data. * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded * data. * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload * indication. */ enum qca_wlan_vendor_attr_data_offload_ind { QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0, QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION, QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL, QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT, /* keep last */ QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX = QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set * OCB config * * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the * configuration * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be * scheduled * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel * information * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL * active state configuration * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as * OCB_CONFIG_FLAG_80211_FRAME_MODE * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to * use in the case that a packet is sent without a TX control header * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the * last TA received that the local time set by TA is synchronous to other * communicating OCB STAs. */ enum qca_wlan_vendor_attr_ocb_set_config { QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX = QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set * UTC time * * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of * 10 bytes * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of * 5 bytes */ enum qca_wlan_vendor_attr_ocb_set_utc_time { QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX = QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes * to start sending timing advert frames * * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency * on which to send the frames * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times * the frame is sent in 5 seconds */ enum qca_wlan_vendor_attr_ocb_start_timing_advert { QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX = QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes * to stop timing advert * * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel * frequency on which to stop the timing advert */ enum qca_wlan_vendor_attr_ocb_stop_timing_advert { QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX = QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to * get TSF timer value * * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the * timer * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer */ enum qca_wlan_vendor_attr_ocb_get_tsf_resp { QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1, QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2, QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX = QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1 }; enum qca_vendor_attr_get_preferred_freq_list { QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID, /* A 32-unsigned value; the interface type/mode for which the preferred * frequency list is requested (see enum qca_iface_type for possible * values); used in GET_PREFERRED_FREQ_LIST command from user-space to * kernel and in the kernel response back to user-space. */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, /* An array of 32-unsigned values; values are frequency (MHz); sent * from kernel space to user space. */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, /* An array of nested values as per enum qca_wlan_vendor_attr_pcl * attribute. Each element contains frequency (MHz), weight, and flag * bit mask indicating how the frequency should be used in P2P * negotiation; sent from kernel space to user space. */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1 }; enum qca_vendor_attr_probable_oper_channel { QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID, /* 32-bit unsigned value; indicates the connection/iface type likely to * come on this channel (see enum qca_iface_type). */ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, /* 32-bit unsigned value; the frequency (MHz) of the probable channel */ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, /* keep last */ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX = QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1 }; enum qca_iface_type { QCA_IFACE_TYPE_STA, QCA_IFACE_TYPE_AP, QCA_IFACE_TYPE_P2P_CLIENT, QCA_IFACE_TYPE_P2P_GO, QCA_IFACE_TYPE_IBSS, QCA_IFACE_TYPE_TDLS, }; enum qca_set_band { QCA_SETBAND_AUTO = 0, QCA_SETBAND_5G = BIT(0), QCA_SETBAND_2G = BIT(1), QCA_SETBAND_6G = BIT(2), }; /** * enum qca_access_policy - Access control policy * * Access control policy is applied on the configured IE * (QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE). * To be set with QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY. * * @QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: Deny Wi-Fi connections which match * the specific configuration (IE) set, i.e., allow all the * connections which do not match the configuration. * @QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: Accept Wi-Fi connections which match * the specific configuration (IE) set, i.e., deny all the * connections which do not match the configuration. */ enum qca_access_policy { QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED, QCA_ACCESS_POLICY_DENY_UNLESS_LISTED, }; /** * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32) * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized * SOC timer value at TSF capture */ enum qca_vendor_attr_tsf_cmd { QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TSF_CMD, QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE, QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE, QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TSF_MAX = QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1 }; /** * enum qca_tsf_operation: TSF driver commands * @QCA_TSF_CAPTURE: Initiate TSF Capture * @QCA_TSF_GET: Get TSF capture value * @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value + * @QCA_TSF_AUTO_REPORT_ENABLE: Used in STA mode only. Once set, the target + * will automatically send TSF report to the host. To query + * QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY, this operation needs to be + * initiated first. + * @QCA_TSF_AUTO_REPORT_DISABLE: Used in STA mode only. Once set, the target + * will not automatically send TSF report to the host. If + * QCA_TSF_AUTO_REPORT_ENABLE is initiated and + * QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY is not queried anymore, this + * operation needs to be initiated. */ enum qca_tsf_cmd { QCA_TSF_CAPTURE, QCA_TSF_GET, QCA_TSF_SYNC_GET, + QCA_TSF_AUTO_REPORT_ENABLE, + QCA_TSF_AUTO_REPORT_DISABLE, }; /** * enum qca_vendor_attr_wisa_cmd * @QCA_WLAN_VENDOR_ATTR_WISA_MODE: WISA mode value (u32) * WISA setup vendor commands */ enum qca_vendor_attr_wisa_cmd { QCA_WLAN_VENDOR_ATTR_WISA_INVALID = 0, QCA_WLAN_VENDOR_ATTR_WISA_MODE, QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WISA_MAX = QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST - 1 }; /* IEEE 802.11 Vendor Specific elements */ /** * enum qca_vendor_element_id - QCA Vendor Specific element types * * These values are used to identify QCA Vendor Specific elements. The * payload of the element starts with the three octet OUI (OUI_QCA) and * is followed by a single octet type which is defined by this enum. * * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list. * This element can be used to specify preference order for supported * channels. The channels in this list are in preference order (the first * one has the highest preference) and are described as a pair of * (global) Operating Class and Channel Number (each one octet) fields. * * This extends the standard P2P functionality by providing option to have * more than one preferred operating channel. When this element is present, * it replaces the preference indicated in the Operating Channel attribute. * For supporting other implementations, the Operating Channel attribute is * expected to be used with the highest preference channel. Similarly, all * the channels included in this Preferred channel list element are * expected to be included in the Channel List attribute. * * This vendor element may be included in GO Negotiation Request, P2P * Invitation Request, and Provision Discovery Request frames. * * @QCA_VENDOR_ELEM_HE_CAPAB: HE Capabilities element. * This element can be used for pre-standard publication testing of HE * before P802.11ax draft assigns the element ID. The payload of this * vendor specific element is defined by the latest P802.11ax draft. * Please note that the draft is still work in progress and this element * payload is subject to change. * * @QCA_VENDOR_ELEM_HE_OPER: HE Operation element. * This element can be used for pre-standard publication testing of HE * before P802.11ax draft assigns the element ID. The payload of this * vendor specific element is defined by the latest P802.11ax draft. * Please note that the draft is still work in progress and this element * payload is subject to change. * * @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set * element). * This element can be used for pre-standard publication testing of HE * before P802.11ax draft assigns the element ID extension. The payload of * this vendor specific element is defined by the latest P802.11ax draft * (not including the Element ID Extension field). Please note that the * draft is still work in progress and this element payload is subject to * change. * * @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element. * This element can be used for pre-standard publication testing of HE * before P802.11ax draft assigns the element ID extension. The payload of * this vendor specific element is defined by the latest P802.11ax draft * (not including the Element ID Extension field). Please note that the * draft is still work in progress and this element payload is subject to * change. * * @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element. * This element can be used for pre-standard publication testing of HE * before P802.11ax draft assigns the element ID extension. The payload of * this vendor specific element is defined by the latest P802.11ax draft * (not including the Element ID Extension field). Please note that the * draft is still work in progress and this element payload is subject to * change. * * @QCA_VENDOR_ELEM_ALLPLAY: Allplay element */ enum qca_vendor_element_id { QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, QCA_VENDOR_ELEM_HE_CAPAB = 1, QCA_VENDOR_ELEM_HE_OPER = 2, QCA_VENDOR_ELEM_RAPS = 3, QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4, QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5, QCA_VENDOR_ELEM_ALLPLAY = 6, }; /** * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes * * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes * with frequencies to be scanned (in MHz) * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported * rates to be included * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests * at non CCK rate in 2GHz band * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the * driver for the specific scan request * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan * request decoded as in enum scan_status * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation * scan flag is set * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with * randomisation * @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the * specific BSSID to scan for. * @QCA_WLAN_VENDOR_ATTR_SCAN_DWELL_TIME: Unsigned 64-bit dwell time in * microseconds. This is a common value which applies across all * frequencies specified by QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES. */ enum qca_wlan_vendor_attr_scan { QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0, QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1, QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3, QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7, QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8, QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11, QCA_WLAN_VENDOR_ATTR_SCAN_DWELL_TIME = 12, QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SCAN_MAX = QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1 }; /** * enum scan_status - Specifies the valid values the vendor scan attribute * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take * * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with * new scan results * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between */ enum scan_status { VENDOR_SCAN_STATUS_NEW_RESULTS, VENDOR_SCAN_STATUS_ABORTED, VENDOR_SCAN_STATUS_MAX, }; /** * enum qca_vendor_attr_ota_test - Specifies the values for vendor * command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test */ enum qca_vendor_attr_ota_test { QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID, /* 8-bit unsigned value to indicate if OTA test is enabled */ QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE, /* keep last */ QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX = QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1 }; /** * enum qca_vendor_attr_txpower_scale - vendor sub commands index * * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value */ enum qca_vendor_attr_txpower_scale { QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID, /* 8-bit unsigned value to indicate the scaling of tx power */ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE, /* keep last */ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX = QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1 }; /** * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease * * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB. */ enum qca_vendor_attr_txpower_decr_db { QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID, /* 8-bit unsigned value to indicate the reduction of TX power in dB for * a virtual interface. */ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB, /* keep last */ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX = QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1 }; /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands. */ enum qca_wlan_vendor_attr_config { QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0, /* Unsigned 32-bit value to set the DTIM period. * Whether the wifi chipset wakes at every dtim beacon or a multiple of * the DTIM period. If DTIM is set to 3, the STA shall wake up every 3 * DTIM beacons. */ QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1, /* Unsigned 32-bit value to set the wifi_iface stats averaging factor * used to calculate statistics like average the TSF offset or average * number of frame leaked. * For instance, upon Beacon frame reception: * current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000 * For instance, when evaluating leaky APs: * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000 */ QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2, /* Unsigned 32-bit value to configure guard time, i.e., when * implementing IEEE power management based on frame control PM bit, how * long the driver waits before shutting down the radio and after * receiving an ACK frame for a Data frame with PM bit set. */ QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3, /* Unsigned 32-bit value to change the FTM capability dynamically */ QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4, /* Unsigned 16-bit value to configure maximum TX rate dynamically */ QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5, /* Unsigned 32-bit value to configure the number of continuous * Beacon Miss which shall be used by the firmware to penalize * the RSSI. */ QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6, /* Unsigned 8-bit value to configure the channel avoidance indication * behavior. Firmware to send only one indication and ignore duplicate * indications when set to avoid multiple Apps wakeups. */ QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7, /* 8-bit unsigned value to configure the maximum TX MPDU for * aggregation. */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8, /* 8-bit unsigned value to configure the maximum RX MPDU for * aggregation. */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9, /* 8-bit unsigned value to configure the Non aggregrate/11g sw * retry threshold (0 disable, 31 max). */ QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10, /* 8-bit unsigned value to configure the aggregrate sw * retry threshold (0 disable, 31 max). */ QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11, /* 8-bit unsigned value to configure the MGMT frame * retry threshold (0 disable, 31 max). */ QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12, /* 8-bit unsigned value to configure the CTRL frame * retry threshold (0 disable, 31 max). */ QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13, /* 8-bit unsigned value to configure the propagation delay for * 2G/5G band (0~63, units in us) */ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14, /* Unsigned 32-bit value to configure the number of unicast TX fail * packet count. The peer is disconnected once this threshold is * reached. */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15, /* Attribute used to set scan default IEs to the driver. * * These IEs can be used by scan operations that will be initiated by * the driver/firmware. * * For further scan requests coming to the driver, these IEs should be * merged with the IEs received along with scan request coming to the * driver. If a particular IE is present in the scan default IEs but not * present in the scan request, then that IE should be added to the IEs * sent in the Probe Request frames for that scan request. */ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16, /* Unsigned 32-bit attribute for generic commands */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17, /* Unsigned 32-bit value attribute for generic commands */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18, /* Unsigned 32-bit data attribute for generic command response */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19, /* Unsigned 32-bit length attribute for * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20, /* Unsigned 32-bit flags attribute for * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21, /* Unsigned 32-bit, defining the access policy. * See enum qca_access_policy. Used with * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22, /* Sets the list of full set of IEs for which a specific access policy * has to be applied. Used along with * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access. * Zero length payload can be used to clear this access constraint. */ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23, /* Unsigned 32-bit, specifies the interface index (netdev) for which the * corresponding configurations are applied. If the interface index is * not specified, the configurations are attributed to the respective * wiphy. */ QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24, /* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25, /* 8-bit unsigned value to configure the driver and below layers to * ignore the assoc disallowed set by APs while connecting * 1-Ignore, 0-Don't ignore */ QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26, /* 32-bit unsigned value to trigger antenna diversity features: * 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27, /* 32-bit unsigned value to configure specific chain antenna */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28, /* 32-bit unsigned value to trigger cycle selftest * 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29, /* 32-bit unsigned to configure the cycle time of selftest * the unit is micro-second */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30, /* 32-bit unsigned value to set reorder timeout for AC_VO */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31, /* 32-bit unsigned value to set reorder timeout for AC_VI */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32, /* 32-bit unsigned value to set reorder timeout for AC_BE */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33, /* 32-bit unsigned value to set reorder timeout for AC_BK */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34, /* 6-byte MAC address to point out the specific peer */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35, /* 32-bit unsigned value to set window size for specific peer */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36, /* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37, /* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38, /* 32-bit unsigned value to configure 5 or 10 MHz channel width for * station device while in disconnect state. The attribute use the * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz, * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or * 10 MHz channel width, the station will not connect to a BSS using 20 * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to * clear this constraint. */ QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39, /* 32-bit unsigned value to configure the propagation absolute delay * for 2G/5G band (units in us) */ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40, /* 32-bit unsigned value to set probe period */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41, /* 32-bit unsigned value to set stay period */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42, /* 32-bit unsigned value to set snr diff */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43, /* 32-bit unsigned value to set probe dwell time */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44, /* 32-bit unsigned value to set mgmt snr weight */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45, /* 32-bit unsigned value to set data snr weight */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46, /* 32-bit unsigned value to set ack snr weight */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47, /* 32-bit unsigned value to configure the listen interval. * This is in units of beacon intervals. This configuration alters * the negotiated listen interval with the AP during the connection. * It is highly recommended to configure a value less than or equal to * the one negotiated during the association. Configuring any greater * value can have adverse effects (frame loss, AP disassociating STA, * etc.). */ QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48, /* * 8 bit unsigned value that is set on an AP/GO virtual interface to * disable operations that would cause the AP/GO to leave its operating * channel. * * This will restrict the scans to the AP/GO operating channel and the * channels of the other band, if DBS is supported.A STA/CLI interface * brought up after this setting is enabled, will be restricted to * connecting to devices only on the AP/GO interface's operating channel * or on the other band in DBS case. P2P supported channel list is * modified, to only include AP interface's operating-channel and the * channels of the other band if DBS is supported. * * These restrictions are only applicable as long as the AP/GO interface * is alive. If the AP/GO interface is brought down then this * setting/restriction is forgotten. * * If this variable is set on an AP/GO interface while a multi-channel * concurrent session is active, it has no effect on the operation of * the current interfaces, other than restricting the scan to the AP/GO * operating channel and the other band channels if DBS is supported. * However, if the STA is brought down and restarted then the new STA * connection will either be formed on the AP/GO channel or on the * other band in a DBS case. This is because of the scan being * restricted on these channels as mentioned above. * * 1-Restrict / 0-Don't restrict offchannel operations. */ QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49, /* * 8 bit unsigned value to enable/disable LRO (Large Receive Offload) * on an interface. * 1 - Enable, 0 - Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50, /* * 8 bit unsigned value to globally enable/disable scan * 1 - Enable, 0 - Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51, /* 8-bit unsigned value to set the total beacon miss count * This parameter will set the total beacon miss count. */ QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52, /* Unsigned 32-bit value to configure the number of continuous * Beacon Miss which shall be used by the firmware to penalize * the RSSI for BTC. */ QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53, /* 8-bit unsigned value to configure the driver and below layers to * enable/disable all FILS features. * 0-enable, 1-disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54, /* 16-bit unsigned value to configure the level of WLAN latency * module. See enum qca_wlan_vendor_attr_config_latency_level. */ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55, /* 8-bit unsigned value indicating the driver to use the RSNE as-is from * the connect interface. Exclusively used for the scenarios where the * device is used as a test bed device with special functionality and * not recommended for production. This helps driver to not validate the * RSNE passed from user space and thus allow arbitrary IE data to be * used for testing purposes. * 1-enable, 0-disable. * Applications set/reset this configuration. If not reset, this * parameter remains in use until the driver is unloaded. */ QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56, /* 8-bit unsigned value to trigger green Tx power saving. * 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57, /* Attribute to configure disconnect IEs to the driver. * This carries an array of unsigned 8-bit characters. * * If this is configured, driver shall fill the IEs in disassoc/deauth * frame. * These IEs are expected to be considered only for the next * immediate disconnection (disassoc/deauth frame) originated by * the DUT, irrespective of the entity (user space/driver/firmware) * triggering the disconnection. * The host drivers are not expected to use the IEs set through * this interface for further disconnections after the first immediate * disconnection initiated post the configuration. * If the IEs are also updated through cfg80211 interface (after the * enhancement to cfg80211_disconnect), host driver is expected to * take the union of IEs from both of these interfaces and send in * further disassoc/deauth frames. */ QCA_WLAN_VENDOR_ATTR_CONFIG_DISCONNECT_IES = 58, /* 8-bit unsigned value for ELNA bypass. * 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS = 59, /* 8-bit unsigned value. This attribute enables/disables the host driver * to send the Beacon Report Response with failure reason for the * scenarios where STA cannot honor the Beacon Report Request from AP. * 1-Enable, 0-Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL = 60, /* 8-bit unsigned value. This attribute enables/disables the host driver * to send roam reason information in the Reassociation Request frame to * the target AP when roaming within the same ESS. * 1-Enable, 0-Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON = 61, /* 32-bit unsigned value to configure different PHY modes to the * driver/firmware. The possible values are defined in * enum qca_wlan_vendor_phy_mode. The configuration will be reset to * default value, i.e., QCA_WLAN_VENDOR_PHY_MODE_AUTO upon restarting * the driver. */ QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE = 62, /* 8-bit unsigned value to configure the maximum supported channel width * for STA mode. If this value is configured when STA is in connected * state, it should not exceed the negotiated channel width. If it is * configured when STA is in disconnected state, the configured value * will take effect for the next immediate connection. * Possible values are: * NL80211_CHAN_WIDTH_20 * NL80211_CHAN_WIDTH_40 * NL80211_CHAN_WIDTH_80 * NL80211_CHAN_WIDTH_80P80 * NL80211_CHAN_WIDTH_160 */ QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH = 63, /* 8-bit unsigned value to enable/disable dynamic bandwidth adjustment. * This attribute is only applicable for STA mode. When dynamic * bandwidth adjustment is disabled, STA will use static channel width * the value of which is negotiated during connection. * 1-enable (default), 0-disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW = 64, /* 8-bit unsigned value to configure the maximum number of subframes of * TX MSDU for aggregation. Possible values are 0-31. When set to 0, * it is decided by the hardware. */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION = 65, /* 8-bit unsigned value to configure the maximum number of subframes of * RX MSDU for aggregation. Possible values are 0-31. When set to 0, * it is decided by the hardware. */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION = 66, /* 8-bit unsigned value. This attribute is used to dynamically * enable/disable the LDPC capability of the device. When configured in * the disconnected state, the updated configuration will be considered * for the immediately following connection attempt. If this * configuration is modified while the device is in the connected state, * the LDPC TX will be updated with this configuration immediately, * while the LDPC RX configuration update will take place starting from * the subsequent association attempt. * 1-Enable, 0-Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC = 67, /* 8-bit unsigned value. This attribute is used to dynamically * enable/disable the TX STBC capability of the device. When configured * in the disconnected state, the updated configuration will be * considered for the immediately following connection attempt. If the * connection is formed with TX STBC enabled and if this configuration * is disabled during that association, the TX will be impacted * immediately. Further connection attempts will disable TX STBC. * However, enabling the TX STBC for a connected session with disabled * capability is not allowed and will fail. * 1-Enable, 0-Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC = 68, /* 8-bit unsigned value. This attribute is used to dynamically * enable/disable the RX STBC capability of the device. When configured * in the disconnected state, the updated configuration will be * considered for the immediately following connection attempt. If the * configuration is modified in the connected state, there will be no * impact for the current association, but further connection attempts * will use the updated configuration. * 1-Enable, 0-Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69, /* 8-bit unsigned value. This attribute is used to dynamically configure * the number of spatial streams. When configured in the disconnected * state, the updated configuration will be considered for the * immediately following connection attempt. If the NSS is updated after * the connection, the updated NSS value is notified to the peer using * the Operating Mode Notification/Spatial Multiplexing Power Save * frame. The updated NSS value after the connection shall not be * greater than the one negotiated during the connection. Any such * higher value configuration shall be returned with a failure. * Only symmetric NSS configuration (such as 2X2 or 1X1) can be done * using this attribute. QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and * QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attributes shall be used to * configure the asymmetric NSS configuration (such as 1X2). */ QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70, /* 8-bit unsigned value to trigger Optimized Power Management: * 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71, /* 8-bit unsigned value. This attribute takes the QoS/access category * value represented by the enum qca_wlan_ac_type and expects the driver * to upgrade the UDP frames to this access category. The value of * QCA_WLAN_AC_ALL is invalid for this attribute. This will override the * DSCP value configured in the frame with the intention to only upgrade * the access category. That said, it is not intended to downgrade the * access category for the frames. * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be * disabled, as BK is of the lowest priority and an upgrade to it does * not result in any changes for the frames. */ QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72, /* 8-bit unsigned value. This attribute is used to dynamically configure * the number of chains to be used for transmitting data. This * configuration is allowed only when in connected state and will be * effective until disconnected. The driver rejects this configuration * if the number of spatial streams being used in the current connection * cannot be supported by this configuration. */ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73, /* 8-bit unsigned value. This attribute is used to dynamically configure * the number of chains to be used for receiving data. This * configuration is allowed only when in connected state and will be * effective until disconnected. The driver rejects this configuration * if the number of spatial streams being used in the current connection * cannot be supported by this configuration. */ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74, /* 8-bit unsigned value to configure ANI setting type. * See &enum qca_wlan_ani_setting for possible values. */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING = 75, /* 32-bit signed value to configure ANI level. This is used when * ANI settings type is &QCA_WLAN_ANI_SETTING_FIXED. * The set and get of ANI level with &QCA_WLAN_ANI_SETTING_AUTO * is invalid, the driver will return a failure. */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL = 76, /* 8-bit unsigned value. This attribute is used to dynamically configure * the number of spatial streams used for transmitting the data. When * configured in the disconnected state, the configured value will * be considered for the following connection attempt. * If the NSS is updated after the connection, the updated NSS value * is notified to the peer using the Operating Mode Notification/Spatial * Multiplexing Power Save frame. * The TX NSS value configured after the connection shall not be greater * than the value negotiated during the connection. Any such higher * value configuration shall be treated as invalid configuration by * the driver. This attribute shall be configured along with * QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attribute to define the symmetric * configuration (such as 2X2 or 1X1) or the asymmetric * configuration (such as 1X2). * If QCA_WLAN_VENDOR_ATTR_CONFIG_NSS attribute is also provided along * with this QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS attribute the driver * will update the TX NSS based on QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS. */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS = 77, /* 8-bit unsigned value. This attribute is used to dynamically configure * the number of spatial streams used for receiving the data. When * configured in the disconnected state, the configured value will * be considered for the following connection attempt. * If the NSS is updated after the connection, the updated NSS value * is notified to the peer using the Operating Mode Notification/Spatial * Multiplexing Power Save frame. * The RX NSS value configured after the connection shall not be greater * than the value negotiated during the connection. Any such higher * value configuration shall be treated as invalid configuration by * the driver. This attribute shall be configured along with * QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS attribute to define the symmetric * configuration (such as 2X2 or 1X1) or the asymmetric * configuration (such as 1X2). * If QCA_WLAN_VENDOR_ATTR_CONFIG_NSS attribute is also provided along * with this QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attribute the driver * will update the RX NSS based on QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS. */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS = 78, /* * 8-bit unsigned value. This attribute, when set, indicates whether the * specified interface is the primary STA interface when there are more * than one STA interfaces concurrently active. * * This configuration helps the firmware/hardware to support certain * features (e.g., roaming) on this primary interface, if the same * cannot be supported on the concurrent STA interfaces simultaneously. * * This configuration is only applicable for a single STA interface on * a device and gives the priority for it only over other concurrent STA * interfaces. * * If the device is a multi wiphy/soc, this configuration applies to a * single STA interface across the wiphys. * * 1-Enable (is the primary STA), 0-Disable (is not the primary STA) */ QCA_WLAN_VENDOR_ATTR_CONFIG_CONCURRENT_STA_PRIMARY = 79, /* * 8-bit unsigned value. This attribute can be used to configure the * driver to enable/disable FT-over-DS feature. Possible values for * this attribute are 1-Enable and 0-Disable. */ QCA_WLAN_VENDOR_ATTR_CONFIG_FT_OVER_DS = 80, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1, }; /* Compatibility defines for previously used incorrect enum * qca_wlan_vendor_attr_config names. These values should not be used in any * new implementation. */ #define QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES \ QCA_WLAN_VENDOR_ATTR_CONFIG_DISCONNECT_IES #define QCA_WLAN_VENDOR_ATTR_BEACON_REPORT_FAIL \ QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL /** * enum qca_wlan_ani_setting - ANI setting type * @QCA_WLAN_ANI_SETTING_AUTO: Automatically determine ANI level * @QCA_WLAN_ANI_SETTING_FIXED: Fix ANI level to the dBm parameter */ enum qca_wlan_ani_setting { QCA_WLAN_ANI_SETTING_AUTO = 0, QCA_WLAN_ANI_SETTING_FIXED = 1, }; /** * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration * * @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL: Optional (u8) * Channel number on which Access Point should restart. * Note: If both the driver and user space application supports the 6 GHz band, * this attribute is deprecated and QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY * should be used. * To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL * is still used if either of the driver or user space application doesn't * support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY: Optional (u32) * Channel center frequency (MHz) on which the access point should restart. */ enum qca_wlan_vendor_attr_sap_config { QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL = 1, /* List of frequencies on which AP is expected to operate. * This is irrespective of ACS configuration. This list is a priority * based one and is looked for before the AP is created to ensure the * best concurrency sessions (avoid MCC and use DBS/SCC) co-exist in * the system. */ QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY = 3, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX = QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for AP * conditional channel switch */ enum qca_wlan_vendor_attr_sap_conditional_chan_switch { QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0, /* Priority based frequency list (an array of u32 values in host byte * order) */ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1, /* Status of the conditional switch (u32). * 0: Success, Non-zero: Failure */ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2, QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX = QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1, }; /** * enum qca_wlan_gpio_attr - Parameters for GPIO configuration * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND: Required (u32) * value to specify the GPIO command. Please refer to enum qca_gpio_cmd_type * for the available values. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM: Required (u32) * value to specify the GPIO number. * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG or %QCA_WLAN_VENDOR_GPIO_OUTPUT. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE: Required (u32) * value to specify the GPIO output level. Please refer to enum qca_gpio_value * for the available values. * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_OUTPUT. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE: Optional (u32) * value to specify the GPIO pull type. Please refer to enum qca_gpio_pull_type * for the available values. * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG and * %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG attribute is not present. * Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG * attribute is present. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE: Optional (u32) * value to specify the GPIO interrupt mode. Please refer to enum * qca_gpio_interrupt_mode for the available values. * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG and * %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG attribute is not present. * Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG * attribute is present. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR: Optional (u32) * value to specify the GPIO direction. Please refer to enum qca_gpio_direction * for the available values. * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG and * %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG attribute is not present. * Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG * attribute is present. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MUX_CONFIG: Optional (u32) * Value to specify the mux config. Meaning of a given value is dependent * on the target chipset and GPIO pin. Must be of the range 0-15. * Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG. Defaults to 0. * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DRIVE: Optional (u32) * Value to specify the drive, refer to enum qca_gpio_drive. * Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG. Defaults to QCA_WLAN_GPIO_DRIVE_2MA(0). * * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG: Optional (flag) * Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is * %QCA_WLAN_VENDOR_GPIO_CONFIG. When present this attribute signals that all * other parameters for the given GPIO will be obtained from internal * configuration. Only %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM must be * specified to indicate the GPIO pin being configured. */ enum qca_wlan_gpio_attr { QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0, /* Unsigned 32-bit attribute for GPIO command */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND = 1, /* Unsigned 32-bit attribute for GPIO PIN number to configure */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM = 2, /* Unsigned 32-bit attribute for GPIO value to configure */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE = 3, /* Unsigned 32-bit attribute for GPIO pull type */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE = 4, /* Unsigned 32-bit attribute for GPIO interrupt mode */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE = 5, /* Unsigned 32-bit attribute for GPIO direction to configure */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR = 6, /* Unsigned 32-bit attribute for GPIO mux config */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MUX_CONFIG = 7, /* Unsigned 32-bit attribute for GPIO drive */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DRIVE = 8, /* Flag attribute for using internal GPIO configuration */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG = 9, /* keep last */ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST, QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX = QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST - 1 }; /** * enum gpio_cmd_type - GPIO configuration command type * @QCA_WLAN_VENDOR_GPIO_CONFIG: Set GPIO configuration info * @QCA_WLAN_VENDOR_GPIO_OUTPUT: Set GPIO output level */ enum qca_gpio_cmd_type { QCA_WLAN_VENDOR_GPIO_CONFIG = 0, QCA_WLAN_VENDOR_GPIO_OUTPUT = 1, }; /** * enum qca_gpio_pull_type - GPIO pull type * @QCA_WLAN_GPIO_PULL_NONE: Set GPIO pull type to none * @QCA_WLAN_GPIO_PULL_UP: Set GPIO pull up * @QCA_WLAN_GPIO_PULL_DOWN: Set GPIO pull down */ enum qca_gpio_pull_type { QCA_WLAN_GPIO_PULL_NONE = 0, QCA_WLAN_GPIO_PULL_UP = 1, QCA_WLAN_GPIO_PULL_DOWN = 2, QCA_WLAN_GPIO_PULL_MAX, }; /** * enum qca_gpio_direction - GPIO direction * @QCA_WLAN_GPIO_INPUT: Set GPIO as input mode * @QCA_WLAN_GPIO_OUTPUT: Set GPIO as output mode * @QCA_WLAN_GPIO_VALUE_MAX: Invalid value */ enum qca_gpio_direction { QCA_WLAN_GPIO_INPUT = 0, QCA_WLAN_GPIO_OUTPUT = 1, QCA_WLAN_GPIO_DIR_MAX, }; /** * enum qca_gpio_value - GPIO Value * @QCA_WLAN_GPIO_LEVEL_LOW: set gpio output level to low * @QCA_WLAN_GPIO_LEVEL_HIGH: set gpio output level to high * @QCA_WLAN_GPIO_LEVEL_MAX: Invalid value */ enum qca_gpio_value { QCA_WLAN_GPIO_LEVEL_LOW = 0, QCA_WLAN_GPIO_LEVEL_HIGH = 1, QCA_WLAN_GPIO_LEVEL_MAX, }; /** * enum gpio_interrupt_mode - GPIO interrupt mode * @QCA_WLAN_GPIO_INTMODE_DISABLE: Disable interrupt trigger * @QCA_WLAN_GPIO_INTMODE_RISING_EDGE: Interrupt with GPIO rising edge trigger * @QCA_WLAN_GPIO_INTMODE_FALLING_EDGE: Interrupt with GPIO falling edge trigger * @QCA_WLAN_GPIO_INTMODE_BOTH_EDGE: Interrupt with GPIO both edge trigger * @QCA_WLAN_GPIO_INTMODE_LEVEL_LOW: Interrupt with GPIO level low trigger * @QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH: Interrupt with GPIO level high trigger * @QCA_WLAN_GPIO_INTMODE_MAX: Invalid value */ enum qca_gpio_interrupt_mode { QCA_WLAN_GPIO_INTMODE_DISABLE = 0, QCA_WLAN_GPIO_INTMODE_RISING_EDGE = 1, QCA_WLAN_GPIO_INTMODE_FALLING_EDGE = 2, QCA_WLAN_GPIO_INTMODE_BOTH_EDGE = 3, QCA_WLAN_GPIO_INTMODE_LEVEL_LOW = 4, QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH = 5, QCA_WLAN_GPIO_INTMODE_MAX, }; /** * enum qca_gpio_drive - GPIO drive * @QCA_WLAN_GPIO_DRIVE_2MA: drive 2MA * @QCA_WLAN_GPIO_DRIVE_4MA: drive 4MA * @QCA_WLAN_GPIO_DRIVE_6MA: drive 6MA * @QCA_WLAN_GPIO_DRIVE_8MA: drive 8MA * @QCA_WLAN_GPIO_DRIVE_10MA: drive 10MA * @QCA_WLAN_GPIO_DRIVE_12MA: drive 12MA * @QCA_WLAN_GPIO_DRIVE_14MA: drive 14MA * @QCA_WLAN_GPIO_DRIVE_16MA: drive 16MA * @QCA_WLAN_GPIO_DRIVE_MAX: invalid GPIO drive */ enum qca_gpio_drive { QCA_WLAN_GPIO_DRIVE_2MA = 0, QCA_WLAN_GPIO_DRIVE_4MA = 1, QCA_WLAN_GPIO_DRIVE_6MA = 2, QCA_WLAN_GPIO_DRIVE_8MA = 3, QCA_WLAN_GPIO_DRIVE_10MA = 4, QCA_WLAN_GPIO_DRIVE_12MA = 5, QCA_WLAN_GPIO_DRIVE_14MA = 6, QCA_WLAN_GPIO_DRIVE_16MA = 7, QCA_WLAN_GPIO_DRIVE_MAX, }; /** * qca_wlan_set_qdepth_thresh_attr - Parameters for setting * MSDUQ depth threshold per peer per tid in the target * * Associated Vendor Command: * QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH */ enum qca_wlan_set_qdepth_thresh_attr { QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0, /* 6-byte MAC address */ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR, /* Unsigned 32-bit attribute for holding the TID */ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID, /* Unsigned 32-bit attribute for holding the update mask * bit 0 - Update high priority msdu qdepth threshold * bit 1 - Update low priority msdu qdepth threshold * bit 2 - Update UDP msdu qdepth threshold * bit 3 - Update Non UDP msdu qdepth threshold * rest of bits are reserved */ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK, /* Unsigned 32-bit attribute for holding the threshold value */ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE, /* keep last */ QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST, QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX = QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1, }; /** * enum qca_acs_dfs_mode - Defines different types of DFS channel * configurations for ACS operation. * * @QCA_ACS_DFS_MODE_NONE: Refer to invalid DFS mode * @QCA_ACS_DFS_MODE_ENABLE: Consider DFS channels in ACS operation * @QCA_ACS_DFS_MODE_DISABLE: Do not consider DFS channels in ACS operation * @QCA_ACS_DFS_MODE_DEPRIORITIZE: Deprioritize DFS channels in ACS operation */ enum qca_acs_dfs_mode { QCA_ACS_DFS_MODE_NONE = 0, QCA_ACS_DFS_MODE_ENABLE = 1, QCA_ACS_DFS_MODE_DISABLE = 2, QCA_ACS_DFS_MODE_DEPRIORITIZE = 3, }; /** * enum qca_wlan_vendor_attr_acs_config - Defines Configuration attributes * used by the vendor command QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY. * * @QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE: Required (u8) * DFS mode for ACS operation from enum qca_acs_dfs_mode. * * @QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT: Required (u8) * channel number hint for ACS operation, if valid channel is specified then * ACS operation gives priority to this channel. * Note: If both the driver and user space application supports the 6 GHz band, * this attribute is deprecated and QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT * should be used. * To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT * is still used if either of the driver or user space application doesn't * support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT: Required (u32). * Channel center frequency (MHz) hint for ACS operation, if a valid center * frequency is specified, ACS operation gives priority to this channel. */ enum qca_wlan_vendor_attr_acs_config { QCA_WLAN_VENDOR_ATTR_ACS_MODE_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE = 1, QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT = 2, QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT = 3, QCA_WLAN_VENDOR_ATTR_ACS_DFS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX = QCA_WLAN_VENDOR_ATTR_ACS_DFS_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability */ enum qca_wlan_vendor_attr_get_hw_capability { QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_INVALID, /* Antenna isolation * An attribute used in the response. * The content of this attribute is encoded in a byte array. Each byte * value is an antenna isolation value. The array length is the number * of antennas. */ QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, /* Request HW capability * An attribute used in the request. * The content of this attribute is a u32 array for one or more of * hardware capabilities (attribute IDs) that are being requested. Each * u32 value has a value from this * enum qca_wlan_vendor_attr_get_hw_capability * identifying which capabilities are requested. */ QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY, /* keep last */ QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_MAX = QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_ll_stats_ext - Attributes for MAC layer monitoring * offload which is an extension for LL_STATS. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD: Monitoring period. Unit in ms. * If MAC counters do not exceed the threshold, FW will report monitored * link layer counters periodically as this setting. The first report is * always triggered by this timer. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD: It is a percentage (1-99). * For each MAC layer counter, FW holds two copies. One is the current value. * The other is the last report. Once a current counter's increment is larger * than the threshold, FW will indicate that counter to host even if the * monitoring timer does not expire. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG: Peer STA power state change * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID: TID of MSDU * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU: Count of MSDU with the same * failure code. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS: TX failure code * 1: TX packet discarded * 2: No ACK * 3: Postpone * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS: peer MAC address * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE: Peer STA current state * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL: Global threshold. * Threshold for all monitored parameters. If per counter dedicated threshold * is not enabled, this threshold will take effect. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE: Indicate what triggers this * event, PERORID_TIMEOUT == 1, THRESH_EXCEED == 0. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID: interface ID * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID: peer ID * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP: bitmap for TX counters * Bit0: TX counter unit in MSDU * Bit1: TX counter unit in MPDU * Bit2: TX counter unit in PPDU * Bit3: TX counter unit in byte * Bit4: Dropped MSDUs * Bit5: Dropped Bytes * Bit6: MPDU retry counter * Bit7: MPDU failure counter * Bit8: PPDU failure counter * Bit9: MPDU aggregation counter * Bit10: MCS counter for ACKed MPDUs * Bit11: MCS counter for Failed MPDUs * Bit12: TX Delay counter * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP: bitmap for RX counters * Bit0: MAC RX counter unit in MPDU * Bit1: MAC RX counter unit in byte * Bit2: PHY RX counter unit in PPDU * Bit3: PHY RX counter unit in byte * Bit4: Disorder counter * Bit5: Retry counter * Bit6: Duplication counter * Bit7: Discard counter * Bit8: MPDU aggregation size counter * Bit9: MCS counter * Bit10: Peer STA power state change (wake to sleep) counter * Bit11: Peer STA power save counter, total time in PS mode * Bit12: Probe request counter * Bit13: Other management frames counter * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP: bitmap for CCA * Bit0: Idle time * Bit1: TX time * Bit2: time RX in current bss * Bit3: Out of current bss time * Bit4: Wireless medium busy time * Bit5: RX in bad condition time * Bit6: TX in bad condition time * Bit7: time wlan card not available * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP: bitmap for signal * Bit0: Per channel SNR counter * Bit1: Per channel noise floor counter * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM: number of peers * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM: number of channels * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_AC_RX_NUM: number of RX stats * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS: per channel BSS CCA stats * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER: container for per PEER stats * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU: Number of total TX MSDUs * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU: Number of total TX MPDUs * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU: Number of total TX PPDUs * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES: bytes of TX data * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP: Number of dropped TX packets * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES: Bytes dropped * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY: waiting time without an ACK * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK: number of MPDU not-ACKed * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK: number of PPDU not-ACKed * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM: * aggregation stats buffer length * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM: length of mcs stats * buffer for ACKed MPDUs. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM: length of mcs stats * buffer for failed MPDUs. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE: * length of delay stats array. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR: TX aggregation stats * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS: MCS stats for ACKed MPDUs * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS: MCS stats for failed MPDUs * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY: tx delay stats * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU: MPDUs received * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES: bytes received * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU: PPDU received * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES: PPDU bytes received * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST: packets lost * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY: number of RX packets * flagged as retransmissions * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP: number of RX packets * flagged as duplicated * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD: number of RX * packets discarded * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM: length of RX aggregation * stats buffer. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM: length of RX mcs * stats buffer. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS: RX mcs stats buffer * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR: aggregation stats buffer * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES: times STAs go to sleep * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION: STAs' total sleep time * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ: number of probe * requests received * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT: number of other mgmt * frames received * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME: Percentage of idle time * there is no TX, nor RX, nor interference. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME: percentage of time * transmitting packets. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME: percentage of time * for receiving. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY: percentage of time * interference detected. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD: percentage of time * receiving packets with errors. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD: percentage of time * TX no-ACK. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL: percentage of time * the chip is unable to work in normal conditions. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME: percentage of time * receiving packets in current BSS. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME: percentage of time * receiving packets not in current BSS. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM: number of antennas * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL: * This is a container for per antenna signal stats. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR: per antenna SNR value * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64 * Absolute timestamp from 1970/1/1, unit in ms. After receiving the * message, user layer APP could call gettimeofday to get another * timestamp and calculate transfer delay for the message. * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32 * Real period for this measurement, unit in us. */ enum qca_wlan_vendor_attr_ll_stats_ext { QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0, /* Attributes for configurations */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD, QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD, /* Peer STA power state change */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG, /* TX failure event */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, /* MAC counters */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER, /* Sub-attributes for PEER_AC_TX */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY, /* Sub-attributes for PEER_AC_RX */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT, /* Sub-attributes for CCA_BSS */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL, /* sub-attribute for BSS_RX_TIME */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME, /* Sub-attributes for PEER_SIGNAL */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF, /* Sub-attributes for IFACE_BSS */ QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1 }; /* Attributes for FTM commands and events */ /** * enum qca_wlan_vendor_attr_loc_capa - Indoor location capabilities * * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: Various flags. See * enum qca_wlan_vendor_attr_loc_capa_flags. * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number * of measurement sessions that can run concurrently. * Default is one session (no session concurrency). * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique * peers that are supported in running sessions. For example, * if the value is 8 and maximum number of sessions is 2, you can * have one session with 8 unique peers, or 2 sessions with 4 unique * peers each, and so on. * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number * of bursts per peer, as an exponent (2^value). Default is 0, * meaning no multi-burst support. * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number * of measurement exchanges allowed in a single burst. * @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement * types. A bit mask (unsigned 32 bit value), each bit corresponds * to an AOA type as defined by enum qca_vendor_attr_aoa_type. */ enum qca_wlan_vendor_attr_loc_capa { QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST, QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES, /* keep last */ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX = QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags * * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver * can be configured as an FTM responder (for example, an AP that * services FTM requests). QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER * will be supported if set. * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver * can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION * will be supported if set. * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder * supports immediate (ASAP) response. * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone * AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS. * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports * requesting AOA measurements as part of an FTM session. */ enum qca_wlan_vendor_attr_loc_capa_flags { QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4, }; /** * enum qca_wlan_vendor_attr_ftm_peer_info: Information about * a single peer in a measurement session. * * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related * to measurement. See enum qca_wlan_vendor_attr_ftm_peer_meas_flags. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of * FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0 * 9.4.2.167. See enum qca_wlan_vendor_attr_ftm_meas_param for * list of supported attributes. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for * secure measurement. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA * measurement every bursts. If 0 or not specified, * AOA measurements will be disabled for this peer. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where * the measurement frames are exchanged. Optional; if not * specified, try to locate the peer in the kernel scan * results cache and use frequency from there. */ enum qca_wlan_vendor_attr_ftm_peer_info { QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID, QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD, QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ, /* keep last */ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX = QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags, * per-peer * * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request * immediate (ASAP) response from peer. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request * LCI report from peer. The LCI report includes the absolute * location of the peer in "official" coordinates (similar to GPS). * See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request * Location civic report from peer. The LCR includes the location * of the peer in free-form format. See IEEE P802.11-REVmc/D7.0, * 11.24.6.7 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set, * request a secure measurement. * QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided. */ enum qca_wlan_vendor_attr_ftm_peer_meas_flags { QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP = 1 << 0, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI = 1 << 1, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR = 1 << 2, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE = 1 << 3, }; /** * enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters * * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements * to perform in a single burst. * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to * perform, specified as an exponent (2^value). * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst * instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts, * as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must * be larger than QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION. */ enum qca_wlan_vendor_attr_ftm_meas_param { QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD, /* keep last */ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX = QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results * * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported * peer. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement * request for this peer. * See enum qca_wlan_vendor_attr_ftm_peer_result_status. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related * to measurement results for this peer. * See enum qca_wlan_vendor_attr_ftm_peer_result_flags. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when * request failed and peer requested not to send an additional request * for this number of seconds. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received * from peer. In the format specified by IEEE P802.11-REVmc/D7.0, * 9.4.2.22.10. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when * received from peer. In the format specified by IEEE P802.11-REVmc/D7.0, * 9.4.2.22.13. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer * overridden some measurement request parameters. See * enum qca_wlan_vendor_attr_ftm_meas_param. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement * for this peer. Same contents as @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement * results. Each entry is a nested attribute defined * by enum qca_wlan_vendor_attr_ftm_meas. */ enum qca_wlan_vendor_attr_ftm_peer_result { QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS, /* keep last */ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX = QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_ftm_peer_result_status * * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results * will be provided. Peer may have overridden some measurement parameters, * in which case overridden parameters will be report by * QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAM attribute. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable * of performing the measurement request. No more results will be sent * for this peer in this session. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request * failed, and requested not to send an additional request for number * of seconds specified by QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS * attribute. * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation * failed. Request was not sent over the air. */ enum qca_wlan_vendor_attr_ftm_peer_result_status { QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID, }; /** * enum qca_wlan_vendor_attr_ftm_peer_result_flags: Various flags * for measurement result, per-peer * * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set, * measurement completed for this peer. No more results will be reported * for this peer in this session. */ enum qca_wlan_vendor_attr_ftm_peer_result_flags { QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0, }; /** * enum qca_vendor_attr_loc_session_status: Session completion status code * * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed * successfully. * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted * by request. * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request * was invalid and was not started. * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error * and did not complete normally (for example out of resources). */ enum qca_vendor_attr_loc_session_status { QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK, QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED, QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID, QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED, }; /** * enum qca_wlan_vendor_attr_ftm_meas: Single measurement data * * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure (TOD) of FTM packet as * recorded by responder, in picoseconds. * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival (TOA) of FTM packet at * initiator, in picoseconds. * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by * initiator, in picoseconds. * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at * responder, in picoseconds. * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded * during this measurement exchange. Optional and will be provided if * the hardware can measure it. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by * responder. Not always provided. * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by * responder. Not always provided. * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by * initiator. Not always provided. * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by * initiator. Not always provided. * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding. */ enum qca_wlan_vendor_attr_ftm_meas { QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD, /* keep last */ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX = QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_aoa_type - AOA measurement type * * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest * CIR (channel impulse response) path for each antenna. * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude * of the strongest CIR path for each antenna. */ enum qca_wlan_vendor_attr_aoa_type { QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE, QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP, QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX }; /** * enum qca_wlan_vendor_attr_encryption_test - Attributes to * validate encryption engine * * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION: Flag attribute. * This will be included if the request is for decryption; if not included, * the request is treated as a request for encryption by default. * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER: Unsigned 32-bit value * indicating the key cipher suite. Takes same values as * NL80211_ATTR_KEY_CIPHER. * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID: Unsigned 8-bit value * Key Id to be used for encryption * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK: Array of 8-bit values. * Key (TK) to be used for encryption/decryption * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN: Array of 8-bit values. * Packet number to be specified for encryption/decryption * 6 bytes for TKIP/CCMP/GCMP. * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA: Array of 8-bit values * representing the 802.11 packet (header + payload + FCS) that * needs to be encrypted/decrypted. * Encrypted/decrypted response from the driver will also be sent * to userspace with the same attribute. */ enum qca_wlan_vendor_attr_encryption_test { QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA, /* keep last */ QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX = QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of * sector for DMG RF sector operations. * * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector */ enum qca_wlan_vendor_attr_dmg_rf_sector_type { QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX }; /** * enum qca_wlan_vendor_attr_fw_state - State of firmware * * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active */ enum qca_wlan_vendor_attr_fw_state { QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR, QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE, QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX }; /** * BRP antenna limit mode * * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force * antenna limit, BRP will be performed as usual. * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal * antennas limit. the hardware may use less antennas than the * maximum limit. * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will * use exactly the specified number of antennas for BRP. */ enum qca_wlan_vendor_attr_brp_ant_limit_mode { QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE, QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE, QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE, QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX }; /** * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for * DMG RF sector configuration for a single RF module. * The values are defined in a compact way which closely matches * the way it is stored in HW registers. * The configuration provides values for 32 antennas and 8 distribution * amplifiers, and together describes the characteristics of the RF * sector - such as a beam in some direction with some gain. * * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index * of RF module for this configuration. * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge * amplifier gain index. Unsigned 32 bit number containing * bits for all 32 antennas. * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge * amplifier gain index. Unsigned 32 bit number containing * bits for all 32 antennas. * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge * amplifier gain index. Unsigned 32 bit number containing * bits for all 32 antennas. * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values * for first 16 antennas, 2 bits per antenna. * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values * for last 16 antennas, 2 bits per antenna. * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains * DTYPE values (3 bits) for each distribution amplifier, followed * by X16 switch bits for each distribution amplifier. There are * total of 8 distribution amplifiers. */ enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7, /* keep last */ QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX = QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 }; enum qca_wlan_vendor_attr_ll_stats_set { QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_ll_stats_clr { QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0, /* Unsigned 32bit bitmap for clearing statistics * All radio statistics 0x00000001 * cca_busy_time (within radio statistics) 0x00000002 * All channel stats (within radio statistics) 0x00000004 * All scan statistics (within radio statistics) 0x00000008 * All interface statistics 0x00000010 * All tx rate statistics (within interface statistics) 0x00000020 * All ac statistics (with in interface statistics) 0x00000040 * All contention (min, max, avg) statistics (within ac statisctics) * 0x00000080. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1, /* Unsigned 8 bit value: Request to stop statistics collection */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2, /* Unsigned 32 bit bitmap: Response from the driver * for the cleared statistics */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3, /* Unsigned 8 bit value: Response from driver/firmware * for the stop request */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_ll_stats_get { QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0, /* Unsigned 32 bit value provided by the caller issuing the GET stats * command. When reporting the stats results, the driver uses the same * value to indicate which GET request the results correspond to. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1, /* Unsigned 32 bit value - bit mask to identify what statistics are * requested for retrieval. * Radio Statistics 0x00000001 * Interface Statistics 0x00000020 * All Peer Statistics 0x00000040 * Peer Statistics 0x00000080 */ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_ll_stats_results { QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0, /* Unsigned 32bit value. Used by the driver; must match the request id * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5, /* Signed 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6, /* Signed 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7, /* Signed 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are * nested within the interface stats. */ /* Interface mode, e.g., STA, SOFTAP, IBSS, etc. * Type = enum wifi_interface_mode. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9, /* Interface MAC address. An array of 6 Unsigned int8 */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10, /* Type = enum wifi_connection_state, e.g., DISCONNECTED, * AUTHENTICATING, etc. valid for STA, CLI only. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11, /* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12, /* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13, /* NULL terminated SSID. An array of 33 Unsigned 8bit values */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14, /* BSSID. An array of 6 unsigned 8 bit values */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15, /* Country string advertised by AP. An array of 3 unsigned 8 bit * values. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16, /* Country string for this association. An array of 3 unsigned 8 bit * values. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could * be nested within the interface stats. */ /* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26, /* Unsigned int 32 value corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27, /* Unsigned int 32 values corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28, /* Unsigned int 32 values corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29, /* Unsigned int 32 values corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30, /* Unsigned int 32 values corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31, /* Unsigned int 32 values corresponding to respective AC */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32, /* Unsigned 32 bit value. Number of peers */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are * nested within the interface stats. */ /* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34, /* MAC addr corresponding to respective peer. An array of 6 unsigned * 8 bit values. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35, /* Unsigned int 32 bit value representing capabilities corresponding * to respective peer. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36, /* Unsigned 32 bit value. Number of rates */ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* * are nested within the rate stat. */ /* Wi-Fi Rate - separate attributes defined for individual fields */ /* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38, /* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39, /* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40, /* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std * in the units of 0.5 Mbps HT/VHT it would be MCS index */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41, /* Unsigned 32 bit value. Bit rate in units of 100 kbps */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be * nested within the peer info stats. */ /* Unsigned int 32 bit value. Number of successfully transmitted data * packets, i.e., with ACK received corresponding to the respective * rate. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43, /* Unsigned int 32 bit value. Number of received data packets * corresponding to the respective rate. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44, /* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK * received corresponding to the respective rate. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45, /* Unsigned int 32 bit value. Total number of data packet retries for * the respective rate. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46, /* Unsigned int 32 bit value. Total number of short data packet retries * for the respective rate. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47, /* Unsigned int 32 bit value. Total number of long data packet retries * for the respective rate. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48, QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49, /* Unsigned 32 bit value. Total number of msecs the radio is awake * accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50, /* Unsigned 32 bit value. Total number of msecs the radio is * transmitting accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51, /* Unsigned 32 bit value. Total number of msecs the radio is in active * receive accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52, /* Unsigned 32 bit value. Total number of msecs the radio is awake due * to all scan accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53, /* Unsigned 32 bit value. Total number of msecs the radio is awake due * to NAN accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54, /* Unsigned 32 bit value. Total number of msecs the radio is awake due * to GSCAN accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55, /* Unsigned 32 bit value. Total number of msecs the radio is awake due * to roam scan accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56, /* Unsigned 32 bit value. Total number of msecs the radio is awake due * to PNO scan accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57, /* Unsigned 32 bit value. Total number of msecs the radio is awake due * to Hotspot 2.0 scans and GAS exchange accruing over time. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58, /* Unsigned 32 bit value. Number of channels. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could * be nested within the channel stats. */ /* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60, /* Unsigned 32 bit value. Primary 20 MHz channel. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61, /* Unsigned 32 bit value. Center frequency (MHz) first segment. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62, /* Unsigned 32 bit value. Center frequency (MHz) second segment. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63, /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be * nested within the radio stats. */ /* Unsigned int 32 bit value representing total number of msecs the * radio is awake on that channel accruing over time, corresponding to * the respective channel. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64, /* Unsigned int 32 bit value representing total number of msecs the CCA * register is busy accruing over time corresponding to the respective * channel. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65, QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66, /* Signifies the nested list of channel attributes * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67, /* Signifies the nested list of peer info attributes * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* */ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68, /* Signifies the nested list of rate info attributes * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* */ QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69, /* Signifies the nested list of wmm info attributes * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70, /* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates * that more stats, e.g., peers or radio, are to follow in the next * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event. * Otherwise, it is set to 0. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71, /* Unsigned 64 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77, /* Number of msecs the radio spent in transmitting for each power level */ QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81, /* Unsigned 32 bit value */ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82, /* Unsigned int 32 value. * Pending MSDUs corresponding to respective AC. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83, /* u32 value representing total time in milliseconds for which the radio * is transmitting on this channel. This attribute will be nested * within QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_TX_TIME = 84, /* u32 value representing total time in milliseconds for which the radio * is receiving all 802.11 frames intended for this device on this * channel. This attribute will be nested within * QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO. */ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_RX_TIME = 85, + /* u8 value representing the channel load percentage. Possible values + * are 0-100. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_LOAD_PERCENTAGE = 86, /* keep last */ QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_ll_stats_type { QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3, /* keep last */ QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for * TDLS configuration to the host driver. * * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger * mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode * represents the different TDLS trigger modes. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within * which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number * of packets shall meet the criteria for implicit TDLS setup. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets * within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD * to initiate a TDLS setup. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate * a TDLS Discovery to the peer. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of * discovery attempts to know the TDLS capability of the peer. A peer is * marked as TDLS not capable if there is no response for all the attempts. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32) * within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD * number of TX / RX frames meet the criteria for TDLS teardown. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32) * of Tx/Rx packets within a duration * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold * corresponding to the RSSI of the peer below which a TDLS setup is * triggered. * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold * corresponding to the RSSI of the peer above which a TDLS teardown is * triggered. */ enum qca_wlan_vendor_attr_tdls_configuration { QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1, /* Attributes configuring the TDLS Implicit Trigger */ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9, /* keep last */ QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX = QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in * the driver * * The following are the different values for * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE. * * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown * the TDLS connection to a respective peer comes from the user space. * wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN, * TDLS_DISCOVER to do this. * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS * setup/teardown to the eligible peer once the configured criteria * (such as TX/RX threshold, RSSI) is met. The attributes * in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to * the different configuration criteria for the TDLS trigger from the * host driver. * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger * the TDLS setup / teardown through the implicit mode only to the * configured MAC addresses (wpa_supplicant, with tdls_external_control=1, * configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands). * External mode works on top of the implicit mode. Thus the host driver * is expected to configure in TDLS Implicit mode too to operate in * External mode. * Configuring External mode alone without Implicit mode is invalid. * * All the above implementations work as expected only when the host driver * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing * that the TDLS message exchange is not internal to the host driver, but * depends on wpa_supplicant to do the message exchange. */ enum qca_wlan_vendor_tdls_trigger_mode { QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0, QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1, QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2, }; /** * enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0 * that is hard-coded in the Board Data File (BDF). * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1 * that is hard-coded in the Board Data File (BDF). * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2 * that is hard-coded in the Board Data File (BDF). * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3 * that is hard-coded in the Board Data File (BDF). * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4 * that is hard-coded in the Board Data File (BDF). * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any * source of SAR power limits, thereby disabling the SAR power * limit feature. * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power * limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. * * This enumerates the valid set of values that may be supplied for * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in * the response to an instance of the * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command. */ enum qca_vendor_attr_sar_limits_selections { QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7, }; /** * enum qca_vendor_attr_sar_limits_spec_modulations - * SAR limits specification modulation * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK - * CCK modulation * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM - * OFDM modulation * * This enumerates the valid set of values that may be supplied for * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an * instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an * instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor * command or in the response to an instance of the * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command. */ enum qca_vendor_attr_sar_limits_spec_modulations { QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1, }; /** * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to * select which SAR power limit table should be used. Valid * values are enumerated in enum * %qca_vendor_attr_sar_limits_selections. The existing SAR * power limit selection is unchanged if this attribute is not * present. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value * which specifies the number of SAR power limit specifications * which will follow. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power * limit specifications. The number of specifications is * specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each * specification contains a set of * QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A * specification is uniquely identified by the attributes * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND, * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always * contains as a payload the attribute * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT, * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX. * Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is * needed based upon the value of * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to * indicate for which band this specification applies. Valid * values are enumerated in enum %nl80211_band (although not all * bands may be supported by a given device). If the attribute is * not supplied then the specification will be applied to all * supported bands. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value * to indicate for which antenna chain this specification * applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the * attribute is not supplied then the specification will be * applied to all chains. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32) * value to indicate for which modulation scheme this * specification applies. Valid values are enumerated in enum * %qca_vendor_attr_sar_limits_spec_modulations. If the attribute * is not supplied then the specification will be applied to all * modulation schemes. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32) * value to specify the actual power limit value in units of 0.5 * dBm (i.e., a value of 11 represents 5.5 dBm). * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER. * * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32) * value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles. * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0. * * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS. */ enum qca_vendor_attr_sar_limits { QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command. * * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION: In a request this attribute * should be set to any U8 value to indicate that the driver version * should be returned. When enabled in this manner, in a response this * attribute will contain a string representation of the driver version. * * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION: In a request this attribute * should be set to any U8 value to indicate that the firmware version * should be returned. When enabled in this manner, in a response this * attribute will contain a string representation of the firmware version. * * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX: In a request this attribute * should be set to any U32 value to indicate that the current radio * index should be returned. When enabled in this manner, in a response * this attribute will contain a U32 radio index value. * */ enum qca_wlan_vendor_attr_get_wifi_info { QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1, }; /* * enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command. */ enum qca_wlan_vendor_attr_wifi_logger_start { QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX = QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_logger_results { QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0, /* Unsigned 32-bit value; must match the request Id supplied by * Wi-Fi HAL in the corresponding subcmd NL msg. */ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1, /* Unsigned 32-bit value; used to indicate the size of memory * dump to be allocated. */ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX = QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1, }; /** * enum qca_scan_freq_list_type: Frequency list types * * @QCA_PREFERRED_SCAN_FREQ_LIST: The driver shall use the scan frequency list * specified with attribute QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST as * a preferred frequency list for roaming. * * @QCA_SPECIFIC_SCAN_FREQ_LIST: The driver shall use the frequency list * specified with attribute QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST as * a specific frequency list for roaming. */ enum qca_scan_freq_list_type { QCA_PREFERRED_SCAN_FREQ_LIST = 1, QCA_SPECIFIC_SCAN_FREQ_LIST = 2, }; /** * enum qca_vendor_attr_scan_freq_list_scheme: Frequency list scheme * * @QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST: Nested attribute of u32 values * List of frequencies in MHz to be considered for a roam scan. * * @QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE: Unsigned 32-bit value. * Type of frequency list scheme being configured/gotten as defined by the * enum qca_scan_freq_list_type. */ enum qca_vendor_attr_scan_freq_list_scheme { QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST = 1, QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE = 2, /* keep last */ QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST, QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_MAX = QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST - 1, }; /** * enum qca_roam_scan_scheme: Scan scheme * * @QCA_ROAM_SCAN_SCHEME_NO_SCAN: No frequencies specified to scan. * Indicates the driver to not scan on a Roam Trigger scenario, but * disconnect. E.g., on a BTM request from the AP the driver/firmware shall * disconnect from the current connected AP by notifying a failure * code in the BTM response. * * @QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN: Indicates the driver/firmware to * trigger partial frequency scans. These frequencies are the ones learned * or maintained by the driver based on the probability of finding the * BSSIDs in the ESS for which the roaming is triggered. * * @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to * trigger the scan on all the valid frequencies to find better * candidates to roam. */ enum qca_roam_scan_scheme { QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0, QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1, QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2, }; /* * enum qca_vendor_roam_triggers: Bitmap of roaming triggers * * @QCA_ROAM_TRIGGER_REASON_PER: Set if the roam has to be triggered based on * a bad packet error rates (PER). * @QCA_ROAM_TRIGGER_REASON_BEACON_MISS: Set if the roam has to be triggered * based on beacon misses from the connected AP. * @QCA_ROAM_TRIGGER_REASON_POOR_RSSI: Set if the roam has to be triggered * due to poor RSSI of the connected AP. * @QCA_ROAM_TRIGGER_REASON_BETTER_RSSI: Set if the roam has to be triggered * upon finding a BSSID with a better RSSI than the connected BSSID. * Here the RSSI of the current BSSID need not be poor. * @QCA_ROAM_TRIGGER_REASON_PERIODIC: Set if the roam has to be triggered * by triggering a periodic scan to find a better AP to roam. * @QCA_ROAM_TRIGGER_REASON_DENSE: Set if the roam has to be triggered * when the connected channel environment is too noisy/congested. * @QCA_ROAM_TRIGGER_REASON_BTM: Set if the roam has to be triggered * when BTM Request frame is received from the connected AP. * @QCA_ROAM_TRIGGER_REASON_BSS_LOAD: Set if the roam has to be triggered * when the channel utilization is goes above the configured threshold. * @QCA_ROAM_TRIGGER_REASON_USER_TRIGGER: Set if the roam has to be triggered * based on the request from the user (space). * @QCA_ROAM_TRIGGER_REASON_DEAUTH: Set if the roam has to be triggered when * device receives Deauthentication/Disassociation frame from connected AP. * @QCA_ROAM_TRIGGER_REASON_IDLE: Set if the roam has to be triggered when the * device is in idle state (no TX/RX) and suspend mode, if the current RSSI * is determined to be a poor one. * @QCA_ROAM_TRIGGER_REASON_TX_FAILURES: Set if the roam has to be triggered * based on continuous TX Data frame failures to the connected AP. * @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered * based on the scan results obtained from an external scan (not triggered * to aim roaming). * * Set the corresponding roam trigger reason bit to consider it for roam * trigger. * Userspace can set multiple bits and send to the driver. The driver shall * consider all of them to trigger/initiate a roam scan. */ enum qca_vendor_roam_triggers { QCA_ROAM_TRIGGER_REASON_PER = 1 << 0, QCA_ROAM_TRIGGER_REASON_BEACON_MISS = 1 << 1, QCA_ROAM_TRIGGER_REASON_POOR_RSSI = 1 << 2, QCA_ROAM_TRIGGER_REASON_BETTER_RSSI = 1 << 3, QCA_ROAM_TRIGGER_REASON_PERIODIC = 1 << 4, QCA_ROAM_TRIGGER_REASON_DENSE = 1 << 5, QCA_ROAM_TRIGGER_REASON_BTM = 1 << 6, QCA_ROAM_TRIGGER_REASON_BSS_LOAD = 1 << 7, QCA_ROAM_TRIGGER_REASON_USER_TRIGGER = 1 << 8, QCA_ROAM_TRIGGER_REASON_DEAUTH = 1 << 9, QCA_ROAM_TRIGGER_REASON_IDLE = 1 << 10, QCA_ROAM_TRIGGER_REASON_TX_FAILURES = 1 << 11, QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN = 1 << 12, }; /* * enum qca_vendor_roam_fail_reasons: Defines the various roam * fail reasons. This enum value is used in * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON attribute. * * @QCA_ROAM_FAIL_REASON_SCAN_NOT_ALLOWED: Roam module in the firmware is not * able to trigger the scan. * @QCA_ROAM_FAIL_REASON_NO_AP_FOUND: No roamable APs found during roam scan. * @QCA_ROAM_FAIL_REASON_NO_CAND_AP_FOUND: No candidate APs found during roam * scan. * @QCA_ROAM_FAIL_REASON_HOST: Roam fail due to disconnect issued from host. * @QCA_ROAM_FAIL_REASON_AUTH_SEND: Unable to send Authentication frame. * @QCA_ROAM_FAIL_REASON_AUTH_RECV: Received Authentication frame with error * status code. * @QCA_ROAM_FAIL_REASON_NO_AUTH_RESP: Authentication frame not received. * @QCA_ROAM_FAIL_REASON_REASSOC_SEND: Unable to send Reassociation Request * frame. * @QCA_ROAM_FAIL_REASON_REASSOC_RECV: Received Reassociation Response frame * with error status code. * @QCA_ROAM_FAIL_REASON_NO_REASSOC_RESP: Reassociation Response frame not * received. * @QCA_ROAM_FAIL_REASON_SCAN_FAIL: Scan module not able to start scan. * @QCA_ROAM_FAIL_REASON_AUTH_NO_ACK: No ACK is received for Authentication * frame. * @QCA_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP: Authentication frame is dropped * internally before transmission. * @QCA_ROAM_FAIL_REASON_REASSOC_NO_ACK: No ACK is received for Reassociation * Request frame. * @QCA_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP: Reassociation Request frame is * dropped internally. * @QCA_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT: EAPOL-Key M1 is not received and * times out. * @QCA_ROAM_FAIL_REASON_EAPOL_M2_SEND: Unable to send EAPOL-Key M2 frame. * @QCA_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP: EAPOL-Key M2 frame dropped * internally. * @QCA_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK: No ACK is received for EAPOL-Key * M2 frame. * @QCA_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT: EAPOL-Key M3 frame is not received. * @QCA_ROAM_FAIL_REASON_EAPOL_M4_SEND: Unable to send EAPOL-Key M4 frame. * @QCA_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP: EAPOL-Key M4 frame dropped * internally. * @QCA_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK: No ACK is received for EAPOL-Key M4 * frame. * @QCA_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BEACON_MISS: Roam scan is not * started for final beacon miss case. * @QCA_ROAM_FAIL_REASON_DISCONNECT: Deauthentication or Disassociation frame * received from the AP during roaming handoff. * @QCA_ROAM_FAIL_REASON_RESUME_ABORT: Firmware roams to the AP when the Apps * or host is suspended and gives the indication of the last roamed AP only * when the Apps is resumed. If the Apps is resumed while the roaming is in * progress, this ongoing roaming is aborted and the last roamed AP is * indicated to host. * @QCA_ROAM_FAIL_REASON_SAE_INVALID_PMKID: WPA3-SAE invalid PMKID. * @QCA_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT: WPA3-SAE pre-authentication times * out. * @QCA_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL: WPA3-SAE pre-authentication fails. */ enum qca_vendor_roam_fail_reasons { QCA_ROAM_FAIL_REASON_NONE = 0, QCA_ROAM_FAIL_REASON_SCAN_NOT_ALLOWED = 1, QCA_ROAM_FAIL_REASON_NO_AP_FOUND = 2, QCA_ROAM_FAIL_REASON_NO_CAND_AP_FOUND = 3, QCA_ROAM_FAIL_REASON_HOST = 4, QCA_ROAM_FAIL_REASON_AUTH_SEND = 5, QCA_ROAM_FAIL_REASON_AUTH_RECV = 6, QCA_ROAM_FAIL_REASON_NO_AUTH_RESP = 7, QCA_ROAM_FAIL_REASON_REASSOC_SEND = 8, QCA_ROAM_FAIL_REASON_REASSOC_RECV = 9, QCA_ROAM_FAIL_REASON_NO_REASSOC_RESP = 10, QCA_ROAM_FAIL_REASON_SCAN_FAIL = 11, QCA_ROAM_FAIL_REASON_AUTH_NO_ACK = 12, QCA_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP = 13, QCA_ROAM_FAIL_REASON_REASSOC_NO_ACK = 14, QCA_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP = 15, QCA_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT = 16, QCA_ROAM_FAIL_REASON_EAPOL_M2_SEND = 17, QCA_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP = 18, QCA_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK = 19, QCA_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT = 20, QCA_ROAM_FAIL_REASON_EAPOL_M4_SEND = 21, QCA_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP = 22, QCA_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK = 23, QCA_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BEACON_MISS = 24, QCA_ROAM_FAIL_REASON_DISCONNECT = 25, QCA_ROAM_FAIL_REASON_RESUME_ABORT = 26, QCA_ROAM_FAIL_REASON_SAE_INVALID_PMKID = 27, QCA_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT = 28, QCA_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL = 29, }; /* * enum qca_vendor_roam_invoke_fail_reasons: Defines the various roam * invoke fail reasons. This enum value is used in * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON attribute. * * @QCA_ROAM_INVOKE_STATUS_IFACE_INVALID: Invalid interface ID is passed * in roam invoke command. * @QCA_ROAM_INVOKE_STATUS_OFFLOAD_DISABLE: Roam offload in firmware is not * enabled. * @QCA_ROAM_INVOKE_STATUS_AP_SSID_LENGTH_INVALID: Connected AP profile SSID * length is invalid. * @QCA_ROAM_INVOKE_STATUS_ROAM_DISALLOW: Firmware internal roaming is already * in progress. * @QCA_ROAM_INVOKE_STATUS_NON_ROAMABLE_AP: Host sends the Beacon/Probe Response * of the AP in the roam invoke command to firmware. This reason is sent by the * firmware when the given AP is configured to be ignored or SSID/security * does not match. * @QCA_ROAM_INVOKE_STATUS_ROAM_INTERNAL_FAIL: Roam handoff failed because of * firmware internal reasons. * @QCA_ROAM_INVOKE_STATUS_DISALLOW: Roam invoke trigger is not enabled. * @QCA_ROAM_INVOKE_STATUS_SCAN_FAIL: Scan start fail for roam invoke. * @QCA_ROAM_INVOKE_STATUS_START_ROAM_FAIL: Roam handoff start fail. * @QCA_ROAM_INVOKE_STATUS_INVALID_PARAMS: Roam invoke parameters are invalid. * @QCA_ROAM_INVOKE_STATUS_NO_CAND_AP: No candidate AP found to roam to. * @QCA_ROAM_INVOKE_STATUS_ROAM_FAIL: Roam handoff failed. */ enum qca_vendor_roam_invoke_fail_reasons { QCA_ROAM_INVOKE_STATUS_NONE = 0, QCA_ROAM_INVOKE_STATUS_IFACE_INVALID = 1, QCA_ROAM_INVOKE_STATUS_OFFLOAD_DISABLE = 2, QCA_ROAM_INVOKE_STATUS_AP_SSID_LENGTH_INVALID = 3, QCA_ROAM_INVOKE_STATUS_ROAM_DISALLOW = 4, QCA_ROAM_INVOKE_STATUS_NON_ROAMABLE_AP = 5, QCA_ROAM_INVOKE_STATUS_ROAM_INTERNAL_FAIL = 6, QCA_ROAM_INVOKE_STATUS_DISALLOW = 7, QCA_ROAM_INVOKE_STATUS_SCAN_FAIL = 8, QCA_ROAM_INVOKE_STATUS_START_ROAM_FAIL = 9, QCA_ROAM_INVOKE_STATUS_INVALID_PARAMS = 10, QCA_ROAM_INVOKE_STATUS_NO_CAND_AP = 11, QCA_ROAM_INVOKE_STATUS_ROAM_FAIL = 12, }; /** * enum qca_vendor_attr_roam_candidate_selection_criteria: * * Each attribute carries a weightage in percentage (%). * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI: Unsigned 8-bit value. * Represents the weightage to be given for the RSSI selection * criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE: Unsigned 8-bit value. * Represents the weightage to be given for the rate selection * criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BW: Unsigned 8-bit value. * Represents the weightage to be given for the band width selection * criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BAND: Unsigned 8-bit value. * Represents the weightage to be given for the band selection * criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_NSS: Unsigned 8-bit value. * Represents the weightage to be given for the NSS selection * criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_CHAN_CONGESTION: Unsigned 8-bit value. * Represents the weightage to be given for the channel congestion * selection criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BEAMFORMING: Unsigned 8-bit value. * Represents the weightage to be given for the beamforming selection * criteria among other parameters. * * @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_OCE_WAN: Unsigned 8-bit value. * Represents the weightage to be given for the OCE selection * criteria among other parameters. */ enum qca_vendor_attr_roam_candidate_selection_criteria { QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI = 1, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE = 2, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BW = 3, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BAND = 4, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_NSS = 5, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_CHAN_CONGESTION = 6, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BEAMFORMING = 7, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_OCE_WAN = 8, /* keep last */ QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_AFTER_LAST, QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_MAX = QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_AFTER_LAST - 1, }; /** * enum qca_vendor_attr_roam_control - Attributes to carry roam configuration * The following attributes are used to set/get/clear the respective * configurations to/from the driver. * For the get, the attribute for the configuration to be queried shall * carry any of its acceptable values to the driver. In return, the driver * shall send the configured values within the same attribute to the user * space. * * @QCA_ATTR_ROAM_CONTROL_ENABLE: Unsigned 8-bit value. * Signifies to enable/disable roam control in driver. * 1-enable, 0-disable * Enable: Mandates the driver to do the further roams using the * configuration parameters set through * QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET. * Disable: Disables the driver/firmware roaming triggered through * QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET. Further roaming is * expected to continue with the default configurations. * * @QCA_ATTR_ROAM_CONTROL_STATUS: Unsigned 8-bit value. * This is used along with QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET. * Roam control status is obtained through this attribute. * * @QCA_ATTR_ROAM_CONTROL_CLEAR_ALL: Flag attribute to indicate the * complete config set through QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET * is to be cleared in the driver. * This is used along with QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR * and shall be ignored if used with other sub commands. * If this attribute is specified along with subcmd * QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR, the driver shall ignore * all other attributes, if there are any. * If this attribute is not specified when the subcmd * QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR is sent, the driver shall * clear the data corresponding to the attributes specified. * * @QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME: Nested attribute to carry the * list of frequencies and its type, represented by * enum qca_vendor_attr_scan_freq_list_scheme. * Frequency list and its type are mandatory for this attribute to set * the frequencies. * Frequency type is mandatory for this attribute to get the frequencies * and the frequency list is obtained through * QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST. * Frequency list type is mandatory for this attribute to clear the * frequencies. * * @QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD: Unsigned 32-bit value. * Carries the value of scan period in seconds to set. * The value of scan period is obtained with the same attribute for get. * Clears the scan period in the driver when specified with clear command. * Scan period is the idle time in seconds between each subsequent * channel scans. * * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD: Unsigned 32-bit value. * Carries the value of full scan period in seconds to set. * The value of full scan period is obtained with the same attribute for * get. * Clears the full scan period in the driver when specified with clear * command. Full scan period is the idle period in seconds between two * successive full channel roam scans. * * @QCA_ATTR_ROAM_CONTROL_TRIGGERS: Unsigned 32-bit value. * Carries a bitmap of the roam triggers specified in * enum qca_vendor_roam_triggers. * The driver shall enable roaming by enabling corresponding roam triggers * based on the trigger bits sent with this attribute. * If this attribute is not configured, the driver shall proceed with * default behavior. * The bitmap configured is obtained with the same attribute for get. * Clears the bitmap configured in driver when specified with clear * command. * * @QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA: Nested attribute signifying the * weightage in percentage (%) to be given for each selection criteria. * Different roam candidate selection criteria are represented by * enum qca_vendor_attr_roam_candidate_selection_criteria. * The driver shall select the roam candidate based on corresponding * candidate selection scores sent. * * An empty nested attribute is used to indicate that no specific * preference score/criteria is configured (i.e., to disable this mechanism * in the set case and to show that the mechanism is disabled in the get * case). * * Userspace can send multiple attributes out of this enum to the driver. * Since this attribute represents the weight/percentage of preference for * the respective selection criteria, it is preferred to configure 100% * total weightage. The value in each attribute or cumulative weight of the * values in all the nested attributes should not exceed 100%. The driver * shall reject such configuration. * * If the weights configured through this attribute are less than 100%, * the driver shall honor the weights (x%) passed for the corresponding * selection criteria and choose/distribute rest of the weight (100-x)% * for the other selection criteria, based on its internal logic. * * The selection criteria configured is obtained with the same * attribute for get. * * Clears the selection criteria configured in the driver when specified * with clear command. * * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME: Unsigned 32-bit value. * Represents value of the scan frequency scheme from enum * qca_roam_scan_scheme. * It's an optional attribute. If this attribute is not configured, the * driver shall proceed with default behavior. * * @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm, * signifying the RSSI threshold of the current connected AP, indicating * the driver to trigger roam only when the current connected AP's RSSI * is less than this threshold. * * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD: Signed 32-bit value in dBm, * signifying the RSSI threshold of the candidate AP, indicating * the driver to trigger roam only to the candidate AP with RSSI * better than this threshold. If RSSI thresholds for candidate APs found * in the 2.4 GHz, 5 GHz, and 6 GHz bands are configured separately using * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ, * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ, and/or * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ, those values will * take precedence over the value configured using the * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute. * * @QCA_ATTR_ROAM_CONTROL_USER_REASON: Unsigned 32-bit value. Represents the * user defined reason code to be sent to the AP in response to AP's * request to trigger the roam if the roaming cannot be triggered. * Applies to all the scenarios of AP assisted roaming (e.g., BTM). * * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS: Unsigned 32-bit value. * Carries a bitmap of the roam triggers specified in * enum qca_vendor_roam_triggers. * Represents the roam triggers for which the specific scan scheme from * enum qca_roam_scan_scheme has to be applied. * It's an optional attribute. If this attribute is not configured, but * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is specified, the scan scheme * specified through QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is applicable for * all the roams. * If both QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME and * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS are not specified, the * driver shall proceed with the default behavior. * * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ: Signed 32-bit value * in dBm, signifying the RSSI threshold of the candidate AP found in the * 2.4 GHz band. The driver/firmware shall trigger roaming to the candidate * AP found in the 2.4 GHz band only if its RSSI value is better than this * threshold. Optional attribute. If this attribute is not included, the * threshold value specified by the * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute shall be used. * * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ: Signed 32-bit value in * dBm, signifying the RSSI threshold of the candidate AP found in the 5 * GHz band. The driver/firmware shall trigger roaming to the candidate AP * found in the 5 GHz band only if its RSSI value is better than this * threshold. Optional attribute. If this attribute is not included, the * threshold value specified by tge * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute shall be used. * * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ: Signed 32-bit value in * dBm, signifying the RSSI threshold of the candidate AP found in the 6 * GHz band. The driver/firmware shall trigger roaming to the candidate AP * found in the 6 GHz band only if its RSSI value is better than this * threshold. Optional attribute. If this attribute is not included, the * threshold value specified by the * QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute shall be used. * */ enum qca_vendor_attr_roam_control { QCA_ATTR_ROAM_CONTROL_ENABLE = 1, QCA_ATTR_ROAM_CONTROL_STATUS = 2, QCA_ATTR_ROAM_CONTROL_CLEAR_ALL = 3, QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME= 4, QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD = 5, QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6, QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7, QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA = 8, QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME = 9, QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD = 10, QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD = 11, QCA_ATTR_ROAM_CONTROL_USER_REASON = 12, QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS = 13, QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ = 14, QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ = 15, QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ = 16, /* keep last */ QCA_ATTR_ROAM_CONTROL_AFTER_LAST, QCA_ATTR_ROAM_CONTROL_MAX = QCA_ATTR_ROAM_CONTROL_AFTER_LAST - 1, }; /* * enum qca_wlan_vendor_attr_roaming_config_params: Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD: Unsigned 32-bit value. * Represents the different roam sub commands referred by * enum qca_wlan_vendor_roaming_subcmd. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID: Unsigned 32-bit value. * Represents the Request ID for the specific set of commands. * This also helps to map specific set of commands to the respective * ID / client. e.g., helps to identify the user entity configuring the * ignored BSSIDs and accordingly clear the respective ones with the * matching ID. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS: Unsigned * 32-bit value.Represents the number of whitelist SSIDs configured. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST: Nested attribute * to carry the list of Whitelist SSIDs. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID: SSID (binary attribute, * 0..32 octets). Represents the white list SSID. Whitelist SSIDs * represent the list of SSIDs to which the firmware/driver can consider * to roam to. * * The following PARAM_A_BAND_XX attributes are applied to 5GHz BSSIDs when * comparing with a 2.4GHz BSSID. They are not applied when comparing two * 5GHz BSSIDs.The following attributes are set through the Roaming SUBCMD - * QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD: Signed 32-bit * value, RSSI threshold above which 5GHz RSSI is favored. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD: Signed 32-bit * value, RSSI threshold below which 5GHz RSSI is penalized. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR: Unsigned 32-bit * value, factor by which 5GHz RSSI is boosted. * boost=(RSSI_measured-5GHz_boost_threshold)*5GHz_boost_factor * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR: Unsigned 32-bit * value, factor by which 5GHz RSSI is penalized. * penalty=(5GHz_penalty_threshold-RSSI_measured)*5GHz_penalty_factor * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST: Unsigned 32-bit * value, maximum boost that can be applied to a 5GHz RSSI. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS: Unsigned 32-bit * value, boost applied to current BSSID to ensure the currently * associated BSSID is favored so as to prevent ping-pong situations. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER: Signed 32-bit * value, RSSI below which "Alert" roam is enabled. * "Alert" mode roaming - firmware is "urgently" hunting for another BSSID * because the RSSI is low, or because many successive beacons have been * lost or other bad link conditions. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE: Unsigned 32-bit * value. 1-Enable, 0-Disable. Represents "Lazy" mode, where * firmware is hunting for a better BSSID or white listed SSID even though * the RSSI of the link is good. The parameters enabling the roaming are * configured through the PARAM_A_BAND_XX attrbutes. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS: Nested attribute, * represents the BSSIDs preferred over others while evaluating them * for the roaming. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID: Unsigned * 32-bit value. Represents the number of preferred BSSIDs set. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID: 6-byte MAC * address representing the BSSID to be preferred. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER: Signed * 32-bit value, representing the modifier to be applied to the RSSI of * the BSSID for the purpose of comparing it with other roam candidate. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS: Nested attribute, * represents the BSSIDs to get ignored for roaming. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID: Unsigned * 32-bit value, represents the number of ignored BSSIDs. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID: 6-byte MAC * address representing the ignored BSSID. * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT: Flag attribute, * indicates this request to ignore the BSSID as a hint to the driver. The * driver can select this BSSID in the worst case (when no other BSSIDs are * better). * * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL: Nested attribute to * set/get/clear the roam control config as * defined @enum qca_vendor_attr_roam_control. */ enum qca_wlan_vendor_attr_roaming_config_params { QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2, /* Attributes for wifi_set_ssid_white_list */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5, /* Attributes for set_roam_params */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12, /* Attribute for set_lazy_roam */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13, /* Attribute for set_lazy_roam with preferences */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17, /* Attribute for setting ignored BSSID parameters */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20, /* Flag attribute indicates this entry as a hint */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL = 22, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX = QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1, }; /* * enum qca_wlan_vendor_roaming_subcmd: Referred by * QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST: Sub command to * configure the white list SSIDs. These are configured through * the following attributes. * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS, * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST, * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS: Sub command to * configure the Roam params. These parameters are evaluated on the GScan * results. Refers the attributes PARAM_A_BAND_XX above to configure the * params. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM: Sets the Lazy roam. Uses * the attribute QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE * to enable/disable Lazy roam. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS: Sets the BSSID * preference. Contains the attribute * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS to set the BSSID * preference. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the list of BSSIDs * to ignore in roaming decision. Uses * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to set the list. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: Command to set the * roam control config to the driver with the attribute * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET: Command to obtain the * roam control config from driver with the attribute * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL. * For the get, the attribute for the configuration to be queried shall * carry any of its acceptable value to the driver. In return, the driver * shall send the configured values within the same attribute to the user * space. * * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR: Command to clear the * roam control config in the driver with the attribute * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL. * The driver shall continue with its default roaming behavior when data * corresponding to an attribute is cleared. */ enum qca_wlan_vendor_roaming_subcmd { QCA_WLAN_VENDOR_ROAMING_SUBCMD_INVALID = 0, QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST = 1, QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2, QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM = 3, QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS = 4, QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PARAMS = 5, QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID = 6, QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET = 7, QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET = 8, QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR = 9, }; enum qca_wlan_vendor_attr_gscan_config_params { QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1, /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command. */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND = 2, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS = 3, /* Attributes for input params used by * QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command. */ /* Unsigned 32-bit value; channel frequency */ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4, /* Unsigned 32-bit value; dwell time in ms. */ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5, /* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6, /* Unsigned 8-bit value; channel class */ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7, /* Unsigned 8-bit value; bucket index, 0 based */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8, /* Unsigned 8-bit value; band. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9, /* Unsigned 32-bit value; desired period, in ms. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10, /* Unsigned 8-bit value; report events semantics. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11, /* Unsigned 32-bit value. Followed by a nested array of * GSCAN_CHANNEL_SPEC_* attributes. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12, /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes. * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS */ QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13, /* Unsigned 32-bit value; base timer period in ms. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14, /* Unsigned 32-bit value; number of APs to store in each scan in the * BSSID/RSSI history buffer (keep the highest RSSI APs). */ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15, /* Unsigned 8-bit value; in %, when scan buffer is this much full, wake * up AP. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT = 16, /* Unsigned 8-bit value; number of scan bucket specs; followed by a * nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size * of the array is determined by NUM_BUCKETS. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17, /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes. * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18, /* Unsigned 8-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH = 19, /* Unsigned 32-bit value; maximum number of results to be returned. */ QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX = 20, /* An array of 6 x unsigned 8-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21, /* Signed 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22, /* Signed 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24, /* Number of hotlist APs as unsigned 32-bit value, followed by a nested * array of AP_THRESHOLD_PARAM attributes and values. The size of the * array is determined by NUM_AP. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25, /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes. * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS */ QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26, /* Unsigned 32-bit value; number of samples for averaging RSSI. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE = 27, /* Unsigned 32-bit value; number of samples to confirm AP loss. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE = 28, /* Unsigned 32-bit value; number of APs breaching threshold. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29, /* Unsigned 32-bit value; number of APs. Followed by an array of * AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30, /* Unsigned 32-bit value; number of samples to confirm AP loss. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE = 31, /* Unsigned 32-bit value. If max_period is non zero or different than * period, then this bucket is an exponential backoff bucket. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32, /* Unsigned 32-bit value. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33, /* Unsigned 32-bit value. For exponential back off bucket, number of * scans to perform for a given period. */ QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34, /* Unsigned 8-bit value; in number of scans, wake up AP after these * many scans. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS = 35, /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command. */ /* Unsigned 3-2bit value; number of samples to confirm SSID loss. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE = 36, /* Number of hotlist SSIDs as unsigned 32-bit value, followed by a * nested array of SSID_THRESHOLD_PARAM_* attributes and values. The * size of the array is determined by NUM_SSID. */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37, /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_* * attributes. * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38, /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39, /* Unsigned 8-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40, /* Signed 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41, /* Signed 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42, /* Unsigned 32-bit value; a bitmask with additional gscan config flag. */ QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43, /* keep last */ QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX = QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_gscan_results { QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0, /* Unsigned 32-bit value; must match the request Id supplied by * Wi-Fi HAL in the corresponding subcmd NL msg. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1, /* Unsigned 32-bit value; used to indicate the status response from * firmware/driver for the vendor sub-command. */ QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2, /* GSCAN Valid Channels attributes */ /* Unsigned 32bit value; followed by a nested array of CHANNELS. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3, /* An array of NUM_CHANNELS x unsigned 32-bit value integers * representing channel numbers. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4, /* GSCAN Capabilities attributes */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN = 7, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE = 8, /* Signed 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD = 9, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS = 11, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES = 12, /* GSCAN Attributes used with * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command. */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13, /* GSCAN attributes used with * QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command. */ /* An array of NUM_RESULTS_AVAILABLE x * QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_* */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14, /* Unsigned 64-bit value; age of sample at the time of retrieval */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15, /* 33 x unsigned 8-bit value; NULL terminated SSID */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16, /* An array of 6 x unsigned 8-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17, /* Unsigned 32-bit value; channel frequency in MHz */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18, /* Signed 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21, /* Unsigned 16-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22, /* Unsigned 16-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23, /* Unsigned 32-bit value; size of the IE DATA blob */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24, /* An array of IE_LENGTH x unsigned 8-bit value; blob of all the * information elements found in the beacon; this data should be a * packed list of wifi_information_element objects, one after the * other. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25, /* Unsigned 8-bit value; set by driver to indicate more scan results are * available. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26, /* GSCAN attributes for * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command. */ /* Unsigned 8-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28, /* GSCAN attributes for * QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command. */ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE * to indicate number of results. * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the * list of results. */ /* GSCAN attributes for * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command. */ /* An array of 6 x unsigned 8-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL = 30, /* Unsigned 32-bit value. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI = 31, /* A nested array of signed 32-bit RSSI values. Size of the array is * determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST = 32, /* GSCAN attributes used with * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command. */ /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE * to indicate number of gscan cached results returned. * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate * the list of gscan cached results. */ /* An array of NUM_RESULTS_AVAILABLE x * QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_* */ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33, /* Unsigned 32-bit value; a unique identifier for the scan unit. */ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34, /* Unsigned 32-bit value; a bitmask w/additional information about scan. */ QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35, /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE * to indicate number of wifi scan results/bssids retrieved by the scan. * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the * list of wifi scan results returned for each cached result block. */ /* GSCAN attributes for * QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command. */ /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for * number of results. * Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested * list of wifi scan results returned for each * wifi_passpoint_match_result block. * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE. */ /* GSCAN attributes for * QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command. */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES = 36, /* A nested array of * QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_* * attributes. Array size = * *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES. */ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37, /* Unsigned 32-bit value; network block id for the matched network */ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38, /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested * list of wifi scan results returned for each * wifi_passpoint_match_result block. */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39, /* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values; * ANQP data in the information_element format. */ QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40, /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41, /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42, /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID = 43, /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID = 44, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45, /* Unsigned 32-bit value; a GSCAN Capabilities attribute. * This is used to limit the maximum number of BSSIDs while sending * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with subcmd * QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID and attribute * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID. */ QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46, /* keep last */ QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX = QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1, }; enum qca_wlan_vendor_attr_pno_config_params { QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0, /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command. */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1, /* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_* * attributes. Array size = * QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM. */ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3, /* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded * realm, 0 if unspecified. */ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4, /* An array of 16 x unsigned 32-bit value; roaming consortium ids to * match, 0 if unspecified. */ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5, /* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if * unspecified. */ QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6, /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command. */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7, /* Array of nested * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_* * attributes. Array size = * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS. */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8, /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9, /* Signed 8-bit value; threshold for considering this SSID as found, * required granularity for this threshold is 4 dBm to 8 dBm. */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD = 10, /* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11, /* Unsigned 8-bit value; auth bit field for matching WPA IE */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12, /* Unsigned 8-bit to indicate ePNO type; * It takes values from qca_wlan_epno_type */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13, /* Nested attribute to send the channel list */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14, /* Unsigned 32-bit value; indicates the interval between PNO scan * cycles in msec. */ QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15, QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16, QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17, QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18, QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19, QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20, QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21, QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22, /* Unsigned 32-bit value, representing the PNO Request ID */ QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23, /* keep last */ QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_PNO_MAX = QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1, }; /** * qca_wlan_vendor_acs_select_reason: This represents the different reasons why * the ACS has to be triggered. These values are used by * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON */ enum qca_wlan_vendor_acs_select_reason { /* Represents the reason that the ACS triggered during the AP start */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT, /* Represents the reason that DFS found with the current channel */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS, /* Represents the reason that LTE co-exist in the current band. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX, /* Represents the reason that generic, uncategorized interference has * been found in the current channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_GENERIC_INTERFERENCE, /* Represents the reason that excessive 802.11 interference has been * found in the current channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_80211_INTERFERENCE, /* Represents the reason that generic Continuous Wave (CW) interference * has been found in the current channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_CW_INTERFERENCE, /* Represents the reason that Microwave Oven (MWO) interference has been * found in the current channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_MWO_INTERFERENCE, /* Represents the reason that generic Frequency-Hopping Spread Spectrum * (FHSS) interference has been found in the current channel. This may * include 802.11 waveforms. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_FHSS_INTERFERENCE, /* Represents the reason that non-802.11 generic Frequency-Hopping * Spread Spectrum (FHSS) interference has been found in the current * channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_NON_80211_FHSS_INTERFERENCE, /* Represents the reason that generic Wideband (WB) interference has * been found in the current channel. This may include 802.11 waveforms. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_WB_INTERFERENCE, /* Represents the reason that non-802.11 generic Wideband (WB) * interference has been found in the current channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_NON_80211_WB_INTERFERENCE, /* Represents the reason that Jammer interference has been found in the * current channel. */ QCA_WLAN_VENDOR_ACS_SELECT_REASON_JAMMER_INTERFERENCE, }; /** * qca_wlan_vendor_attr_external_acs_policy: Attribute values for * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the * external ACS policies to select the channels w.r.t. the PCL weights. * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and * their PCL weights.) * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to * select a channel with non-zero PCL weight. * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a * channel with non-zero PCL weight. * */ enum qca_wlan_vendor_attr_external_acs_policy { QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY, }; /** * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel. * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS. */ enum qca_wlan_vendor_channel_prop_flags { /* Bits 0, 1, 2, and 3 are reserved */ /* Turbo channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO = 1 << 4, /* CCK channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK = 1 << 5, /* OFDM channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM = 1 << 6, /* 2.4 GHz spectrum channel. */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ = 1 << 7, /* 5 GHz spectrum channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ = 1 << 8, /* Only passive scan allowed */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE = 1 << 9, /* Dynamic CCK-OFDM channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN = 1 << 10, /* GFSK channel (FHSS PHY) */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK = 1 << 11, /* Radar found on channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR = 1 << 12, /* 11a static turbo channel only */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO = 1 << 13, /* Half rate channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF = 1 << 14, /* Quarter rate channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER = 1 << 15, /* HT 20 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20 = 1 << 16, /* HT 40 with extension channel above */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS = 1 << 17, /* HT 40 with extension channel below */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS = 1 << 18, /* HT 40 intolerant */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL = 1 << 19, /* VHT 20 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20 = 1 << 20, /* VHT 40 with extension channel above */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS = 1 << 21, /* VHT 40 with extension channel below */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS = 1 << 22, /* VHT 80 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80 = 1 << 23, /* HT 40 intolerant mark bit for ACS use */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24, /* Channel temporarily blocked due to noise */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED = 1 << 25, /* VHT 160 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160 = 1 << 26, /* VHT 80+80 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80 = 1 << 27, /* HE 20 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20 = 1 << 28, /* HE 40 with extension channel above */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS = 1 << 29, /* HE 40 with extension channel below */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30, /* HE 40 intolerant */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31, }; /** * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2. */ enum qca_wlan_vendor_channel_prop_flags_2 { /* HE 40 intolerant mark bit for ACS use */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0, /* HE 80 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80 = 1 << 1, /* HE 160 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160 = 1 << 2, /* HE 80+80 channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80 = 1 << 3, }; /** * qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for * each channel. This is used by * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT. */ enum qca_wlan_vendor_channel_prop_flags_ext { /* Radar found on channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND = 1 << 0, /* DFS required on channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS = 1 << 1, /* DFS required on channel for 2nd band of 80+80 */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2 = 1 << 2, /* If channel has been checked for DFS */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR = 1 << 3, /* Excluded in 802.11d */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED = 1 << 4, /* Channel Switch Announcement received on this channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED = 1 << 5, /* Ad-hoc is not allowed */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6, /* Station only channel */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7, /* DFS radar history for client device (STA mode) */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8, /* DFS CAC valid for client device (STA mode) */ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9, }; /** * qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel * information. These attributes are sent as part of * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following * attributes correspond to a single channel. */ enum qca_wlan_vendor_external_acs_event_chan_info_attr { QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0, /* A bitmask (u32) with flags specified in * enum qca_wlan_vendor_channel_prop_flags. */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1, /* A bitmask (u32) with flags specified in * enum qca_wlan_vendor_channel_prop_flags_ext. */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2, /* frequency in MHz (u32) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3, /* maximum regulatory transmission power (u32) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4, /* maximum transmission power (u32) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5, /* minimum transmission power (u32) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6, /* regulatory class id (u8) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7, /* maximum antenna gain in (u8) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8, /* VHT segment 0 (u8) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9, /* VHT segment 1 (u8) */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10, /* A bitmask (u32) with flags specified in * enum qca_wlan_vendor_channel_prop_flags_2. */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11, /* * VHT segment 0 in MHz (u32) and the attribute is mandatory. * Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 * along with * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0. * * If both the driver and user-space application supports the 6 GHz * band, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 * is deprecated and * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 * should be used. * * To maintain backward compatibility, * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 * is still used if either of the driver or user space application * doesn't support the 6 GHz band. */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 = 12, /* * VHT segment 1 in MHz (u32) and the attribute is mandatory. * Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 * along with * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1. * * If both the driver and user-space application supports the 6 GHz * band, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 * is deprecated and * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 * should be considered. * * To maintain backward compatibility, * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 * is still used if either of the driver or user space application * doesn't support the 6 GHz band. */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13, /* keep last */ QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX = QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1, }; /** * qca_wlan_vendor_attr_pcl: Represents attributes for * preferred channel list (PCL). These attributes are sent as part of * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and * QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST. */ enum qca_wlan_vendor_attr_pcl { QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0, /* Channel number (u8) */ QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1, /* Channel weightage (u8) */ QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2, /* Channel frequency (u32) in MHz */ QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3, /* Channel flags (u32) * bit 0 set: channel to be used for GO role, * bit 1 set: channel to be used on CLI role, * bit 2 set: channel must be considered for operating channel * selection & peer chosen operating channel should be * one of the channels with this flag set, * bit 3 set: channel should be excluded in GO negotiation */ QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4, }; /** * qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by * host driver. */ enum qca_wlan_vendor_attr_external_acs_event { QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0, /* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason. * This helps ACS module to understand why ACS needs to be started. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1, /* Flag attribute to indicate if driver supports spectral scanning */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2, /* Flag attribute to indicate if 11ac is offloaded to firmware */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3, /* Flag attribute to indicate if driver provides additional channel * capability as part of scan operation */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4, /* Flag attribute to indicate interface status is UP */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5, /* Operating mode (u8) of interface. Takes one of enum nl80211_iftype * values. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6, /* Channel width (u8). It takes one of enum nl80211_chan_width values. * This is the upper bound of channel width. ACS logic should try to get * a channel with the specified width and if not found, look for lower * values. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7, /* This (u8) will hold values of one of enum nl80211_bands */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8, /* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode * values */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9, /* Array of (u32) supported frequency list among which ACS should choose * best frequency. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10, /* Preferred channel list by the driver which will have array of nested * values as per enum qca_wlan_vendor_attr_pcl attribute. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11, /* Array of nested attribute for each channel. It takes attr as defined * in enum qca_wlan_vendor_external_acs_event_chan_info_attr. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12, /* External ACS policy such as PCL mandatory, PCL preferred, etc. * It uses values defined in enum * qca_wlan_vendor_attr_external_acs_policy. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13, /* Reference RF Operating Parameter (RROP) availability information * (u16). It uses values defined in enum * qca_wlan_vendor_attr_rropavail_info. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14, /* keep last */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX = QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1, }; /** * enum qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels * in priority order as decided after ACS operation in userspace. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON: Required (u8). * One of reason code from enum qca_wlan_vendor_acs_select_reason. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST: Required * Array of nested values for each channel with following attributes: * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST is deprecated and use * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST * is still used if either of the driver or user space application doesn't * support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY: Required (u8). * Primary channel number * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY is deprecated and use * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY * is still used if either of the driver or user space application doesn't * support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY: Required (u8). * Secondary channel number, required only for 160 and 80+80 MHz bandwidths. * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY is deprecated and use * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY * is still used if either of the driver or user space application * doesn't support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0: Required (u8). * VHT seg0 channel number * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 is deprecated and use * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 * is still used if either of the driver or user space application * doesn't support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1: Required (u8). * VHT seg1 channel number * Note: If both the driver and user-space application supports the 6 GHz band, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 is deprecated and use * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1. * To maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 * is still used if either of the driver or user space application * doesn't support the 6 GHz band. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH: Required (u8). * Takes one of enum nl80211_chan_width values. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST: Required * Array of nested values for each channel with following attributes: * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY in MHz (u32), * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY in MHz (u32), * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 in MHz (u32), * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 in MHz (u32), * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH * Note: If user-space application has no support of the 6 GHz band, this * attribute is optional. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY: Required (u32) * Primary channel frequency in MHz * Note: If user-space application has no support of the 6 GHz band, this * attribute is optional. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY: Required (u32) * Secondary channel frequency in MHz used for HT 40 MHz channels. * Note: If user-space application has no support of the 6 GHz band, this * attribute is optional. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0: Required (u32) * VHT seg0 channel frequency in MHz * Note: If user-space application has no support of the 6GHz band, this * attribute is optional. * * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1: Required (u32) * VHT seg1 channel frequency in MHz * Note: If user-space application has no support of the 6 GHz band, this * attribute is optional. */ enum qca_wlan_vendor_attr_external_acs_channels { QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0, /* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1, /* Array of nested values for each channel with following attributes: * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1, * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2, /* This (u8) will hold values of one of enum nl80211_bands */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3, /* Primary channel (u8) */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4, /* Secondary channel (u8) used for HT 40 MHz channels */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5, /* VHT seg0 channel (u8) */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6, /* VHT seg1 channel (u8) */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7, /* Channel width (u8). Takes one of enum nl80211_chan_width values. */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST = 9, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY = 10, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY = 11, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13, /* keep last */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX = QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1 }; enum qca_chip_power_save_failure_reason { /* Indicates if the reason for the failure is due to a protocol * layer/module. */ QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0, /* Indicates if the reason for the failure is due to a hardware issue. */ QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1, }; /** * qca_attr_chip_power_save_failure: Attributes to vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite * information leading to the power save failure. */ enum qca_attr_chip_power_save_failure { QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0, /* Reason to cause the power save failure. * These reasons are represented by * enum qca_chip_power_save_failure_reason. */ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1, /* keep last */ QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST, QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX = QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1, }; /** * qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various * data types for which the stats have to get collected. */ enum qca_wlan_vendor_nud_stats_data_pkt_flags { QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0, QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1, QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2, QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3, QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4, /* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get * to represent the stats of respective data type. */ QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5, QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6, QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7, }; enum qca_wlan_vendor_nud_stats_set_data_pkt_info { QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0, /* Represents the data packet type to be monitored (u32). * Host driver tracks the stats corresponding to each data frame * represented by these flags. * These data packets are represented by * enum qca_wlan_vendor_nud_stats_data_pkt_flags */ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1, /* Name corresponding to the DNS frame for which the respective DNS * stats have to get monitored (string). Max string length 255. */ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2, /* source port on which the respective proto stats have to get * collected (u32). */ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3, /* destination port on which the respective proto stats have to get * collected (u32). */ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4, /* IPv4 address for which the destined data packets have to be * monitored. (in network byte order), u32. */ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5, /* IPv6 address for which the destined data packets have to be * monitored. (in network byte order), 16 bytes array. */ QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6, QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST, QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX = QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1, }; /** * qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite * information to start/stop the NUD statistics collection. */ enum qca_attr_nud_stats_set { QCA_ATTR_NUD_STATS_SET_INVALID = 0, /* Flag to start/stop the NUD statistics collection. * Start - If included, Stop - If not included */ QCA_ATTR_NUD_STATS_SET_START = 1, /* IPv4 address of the default gateway (in network byte order), u32 */ QCA_ATTR_NUD_STATS_GW_IPV4 = 2, /* Represents the list of data packet types to be monitored. * Host driver tracks the stats corresponding to each data frame * represented by these flags. * These data packets are represented by * enum qca_wlan_vendor_nud_stats_set_data_pkt_info */ QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3, /* keep last */ QCA_ATTR_NUD_STATS_SET_LAST, QCA_ATTR_NUD_STATS_SET_MAX = QCA_ATTR_NUD_STATS_SET_LAST - 1, }; enum qca_attr_nud_data_stats { QCA_ATTR_NUD_DATA_STATS_INVALID = 0, /* Data packet type for which the stats are collected (u32). * Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags */ QCA_ATTR_NUD_STATS_PKT_TYPE = 1, /* Name corresponding to the DNS frame for which the respective DNS * stats are monitored (string). Max string length 255. */ QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2, /* source port on which the respective proto stats are collected (u32). */ QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3, /* destination port on which the respective proto stats are collected * (u32). */ QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4, /* IPv4 address for which the destined data packets have to be * monitored. (in network byte order), u32. */ QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5, /* IPv6 address for which the destined data packets have to be * monitored. (in network byte order), 16 bytes array. */ QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6, /* Data packet Request count received from netdev (u32). */ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7, /* Data packet Request count sent to lower MAC from upper MAC (u32). */ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8, /* Data packet Request count received by lower MAC from upper MAC * (u32) */ QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9, /* Data packet Request count successfully transmitted by the device * (u32) */ QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10, /* Data packet Response count received by lower MAC (u32) */ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11, /* Data packet Response count received by upper MAC (u32) */ QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12, /* Data packet Response count delivered to netdev (u32) */ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13, /* Data Packet Response count that are dropped out of order (u32) */ QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14, /* keep last */ QCA_ATTR_NUD_DATA_STATS_LAST, QCA_ATTR_NUD_DATA_STATS_MAX = QCA_ATTR_NUD_DATA_STATS_LAST - 1, }; /** * qca_attr_nud_stats_get: Attributes to vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite * NUD statistics collected when queried. */ enum qca_attr_nud_stats_get { QCA_ATTR_NUD_STATS_GET_INVALID = 0, /* ARP Request count from netdev (u32) */ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1, /* ARP Request count sent to lower MAC from upper MAC (u32) */ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2, /* ARP Request count received by lower MAC from upper MAC (u32) */ QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3, /* ARP Request count successfully transmitted by the device (u32) */ QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4, /* ARP Response count received by lower MAC (u32) */ QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5, /* ARP Response count received by upper MAC (u32) */ QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6, /* ARP Response count delivered to netdev (u32) */ QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7, /* ARP Response count dropped due to out of order reception (u32) */ QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8, /* Flag indicating if the station's link to the AP is active. * Active Link - If included, Inactive link - If not included */ QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9, /* Flag indicating if there is any duplicate address detected (DAD). * Yes - If detected, No - If not detected. */ QCA_ATTR_NUD_STATS_IS_DAD = 10, /* List of Data packet types for which the stats are requested. * This list does not carry ARP stats as they are done by the * above attributes. Represented by enum qca_attr_nud_data_stats. */ QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11, /* keep last */ QCA_ATTR_NUD_STATS_GET_LAST, QCA_ATTR_NUD_STATS_GET_MAX = QCA_ATTR_NUD_STATS_GET_LAST - 1, }; enum qca_wlan_btm_candidate_status { QCA_STATUS_ACCEPT = 0, QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1, QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2, QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3, QCA_STATUS_REJECT_LOW_RSSI = 4, QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5, QCA_STATUS_REJECT_UNKNOWN = 6, }; enum qca_wlan_vendor_attr_btm_candidate_info { QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0, /* 6-byte MAC address representing the BSSID of transition candidate */ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1, /* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status * returned by the driver. It says whether the BSSID provided in * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by * the driver, if not it specifies the reason for rejection. * Note that the user-space can overwrite the transition reject reason * codes provided by driver based on more information. */ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX = QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1, }; enum qca_attr_trace_level { QCA_ATTR_TRACE_LEVEL_INVALID = 0, /* * Nested array of the following attributes: * QCA_ATTR_TRACE_LEVEL_MODULE, * QCA_ATTR_TRACE_LEVEL_MASK. */ QCA_ATTR_TRACE_LEVEL_PARAM = 1, /* * Specific QCA host driver module. Please refer to the QCA host * driver implementation to get the specific module ID. */ QCA_ATTR_TRACE_LEVEL_MODULE = 2, /* Different trace level masks represented in the QCA host driver. */ QCA_ATTR_TRACE_LEVEL_MASK = 3, /* keep last */ QCA_ATTR_TRACE_LEVEL_AFTER_LAST, QCA_ATTR_TRACE_LEVEL_MAX = QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities */ enum qca_wlan_vendor_attr_get_he_capabilities { QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0, /* Whether HE capabilities is supported * (u8 attribute: 0 = not supported, 1 = supported) */ QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1, /* HE PHY capabilities, array of 3 u32 values */ QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2, /* HE MAC capabilities (u32 attribute) */ QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3, /* HE MCS map (u32 attribute) */ QCA_WLAN_VENDOR_ATTR_HE_MCS = 4, /* Number of SS (u32 attribute) */ QCA_WLAN_VENDOR_ATTR_NUM_SS = 5, /* RU count (u32 attribute) */ QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6, /* PPE threshold data, array of 8 u32 values */ QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7, /* keep last */ QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX = QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters */ enum qca_wlan_vendor_attr_spectral_scan { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0, /* Number of times the chip enters spectral scan mode before * deactivating spectral scans. When set to 0, chip will enter spectral * scan mode continuously. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1, /* Spectral scan period. Period increment resolution is 256*Tclk, * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2, /* Spectral scan priority. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3, /* Number of FFT data points to compute. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4, /* Enable targeted gain change before starting the spectral scan FFT. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5, /* Restart a queued spectral scan. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6, /* Noise floor reference number for the calculation of bin power. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7, /* Disallow spectral scan triggers after TX/RX packets by setting * this delay value to roughly SIFS time period or greater. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8, /* Number of strong bins (inclusive) per sub-channel, below * which a signal is declared a narrow band tone. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9, /* Specify the threshold over which a bin is declared strong (for * scan bandwidth analysis). u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10, /* Spectral scan report mode. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11, /* RSSI report mode, if the ADC RSSI is below * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR, * then FFTs will not trigger, but timestamps and summaries get * reported. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12, /* ADC RSSI must be greater than or equal to this threshold (signed dB) * to ensure spectral scan reporting with normal error code. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13, /* Format of frequency bin magnitude for spectral scan triggered FFTs: * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)). * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14, /* Format of FFT report to software for spectral scan triggered FFTs. * 0: No FFT report (only spectral scan summary report) * 1: 2-dword summary of metrics for each completed FFT + spectral scan * report * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled * bins (in-band) per FFT + spectral scan summary report * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled * bins (all) per FFT + spectral scan summary report * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15, /* Number of LSBs to shift out in order to scale the FFT bins. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16, /* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes * in dBm power. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17, /* Per chain enable mask to select input ADC for search FFT. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18, /* An unsigned 64-bit integer provided by host driver to identify the * spectral scan request. This attribute is included in the scan * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START * and used as an attribute in * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the * specific scan to be stopped. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19, /* Skip interval for FFT reports. u32 attribute */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20, /* Set to report only one set of FFT results. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21, /* Debug level for spectral module in driver. * 0 : Verbosity level 0 * 1 : Verbosity level 1 * 2 : Verbosity level 2 * 3 : Matched filterID display * 4 : One time dump of FFT report * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22, /* Type of spectral scan request. u32 attribute. * It uses values defined in enum * qca_wlan_vendor_attr_spectral_scan_request_type. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23, /* This specifies the frequency span over which spectral * scan would be carried out. Its value depends on the * value of QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE and * the relation is as follows. * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL * Not applicable. Spectral scan would happen in the * operating span. * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE * Center frequency (in MHz) of the span of interest or * for convenience, center frequency (in MHz) of any channel * in the span of interest. For 80+80 MHz agile spectral scan * request it represents center frequency (in MHz) of the primary * 80 MHz span or for convenience, center frequency (in MHz) of any * channel in the primary 80 MHz span. If agile spectral scan is * initiated without setting a valid frequency it returns the * error code * (QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED). * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FREQUENCY = 24, /* Spectral scan mode. u32 attribute. * It uses values defined in enum qca_wlan_vendor_spectral_scan_mode. * If this attribute is not present, it is assumed to be * normal mode (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL). */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE = 25, /* Spectral scan error code. u32 attribute. * It uses values defined in enum * qca_wlan_vendor_spectral_scan_error_code. * This attribute is included only in failure scenarios. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE = 26, /* 8-bit unsigned value to enable/disable debug of the * Spectral DMA ring. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_RING_DEBUG = 27, /* 8-bit unsigned value to enable/disable debug of the * Spectral DMA buffers. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_BUFFER_DEBUG = 28, /* This specifies the frequency span over which spectral scan would be * carried out. Its value depends on the value of * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE and the relation is as * follows. * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL * Not applicable. Spectral scan would happen in the operating span. * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE * This attribute is applicable only for agile spectral scan * requests in 80+80 MHz mode. It represents center frequency (in * MHz) of the secondary 80 MHz span or for convenience, center * frequency (in MHz) of any channel in the secondary 80 MHz span. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FREQUENCY_2 = 29, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX = QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS. */ enum qca_wlan_vendor_attr_spectral_diag_stats { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0, /* Number of spectral TLV signature mismatches. * u64 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1, /* Number of spectral phyerror events with insufficient length when * parsing for secondary 80 search FFT report. u64 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2, /* Number of spectral phyerror events without secondary 80 * search FFT report. u64 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3, /* Number of spectral phyerror events with vht operation segment 1 id * mismatches in search fft report. u64 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4, /* Number of spectral phyerror events with vht operation segment 2 id * mismatches in search fft report. u64 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX = QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. */ enum qca_wlan_vendor_attr_spectral_cap { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0, /* Flag attribute to indicate phydiag capability */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1, /* Flag attribute to indicate radar detection capability */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2, /* Flag attribute to indicate spectral capability */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3, /* Flag attribute to indicate advanced spectral capability */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4, /* Spectral hardware generation. u32 attribute. * It uses values defined in enum * qca_wlan_vendor_spectral_scan_cap_hw_gen. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5, /* Spectral bin scaling formula ID. u16 attribute. * It uses values defined in enum * qca_wlan_vendor_spectral_scan_cap_formula_id. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID = 6, /* Spectral bin scaling param - low level offset. * s16 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_LOW_LEVEL_OFFSET = 7, /* Spectral bin scaling param - high level offset. * s16 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HIGH_LEVEL_OFFSET = 8, /* Spectral bin scaling param - RSSI threshold. * s16 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RSSI_THR = 9, /* Spectral bin scaling param - default AGC max gain. * u8 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10, /* Flag attribute to indicate agile spectral scan capability * for 20/40/80 MHz modes. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL = 11, /* Flag attribute to indicate agile spectral scan capability * for 160 MHz mode. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_160 = 12, /* Flag attribute to indicate agile spectral scan capability * for 80+80 MHz mode. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_80_80 = 13, /* Number of spectral detectors used for scan in 20 MHz. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_20_MHZ = 14, /* Number of spectral detectors used for scan in 40 MHz. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_40_MHZ = 15, /* Number of spectral detectors used for scan in 80 MHz. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_80_MHZ = 16, /* Number of spectral detectors used for scan in 160 MHz. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_160_MHZ = 17, /* Number of spectral detectors used for scan in 80+80 MHz. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_80P80_MHZ = 18, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX = QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. */ enum qca_wlan_vendor_attr_spectral_scan_status { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0, /* Flag attribute to indicate whether spectral scan is enabled */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1, /* Flag attribute to indicate whether spectral scan is in progress*/ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2, /* Spectral scan mode. u32 attribute. * It uses values defined in enum qca_wlan_vendor_spectral_scan_mode. * If this attribute is not present, normal mode * (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL is assumed to be * requested. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE = 3, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX = QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1, }; /** * qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the * spectral scan request types. * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to * set the spectral parameters and start scan. * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to * only set the spectral parameters. * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to * only start the spectral scan. */ enum qca_wlan_vendor_attr_spectral_scan_request_type { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG, }; /** * qca_wlan_vendor_spectral_scan_mode: Attribute values for * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE in the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START and * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE in the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. This represents the * spectral scan modes. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL: Normal spectral scan: * spectral scan in the current operating span. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE: Agile spectral scan: * spectral scan in the configured agile span. */ enum qca_wlan_vendor_spectral_scan_mode { QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL = 0, QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE = 1, }; /** * qca_wlan_vendor_spectral_scan_error_code: Attribute values for * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE in the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED: Changing the value * of a parameter is not supported. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED: Requested spectral scan * mode is not supported. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE: A parameter * has invalid value. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED: A parameter * is not initialized. */ enum qca_wlan_vendor_spectral_scan_error_code { QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED = 0, QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED = 1, QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE = 2, QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED = 3, }; /** * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the * spectral hardware generation. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1 * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2 * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3 */ enum qca_wlan_vendor_spectral_scan_cap_hw_gen { QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0, QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1, QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2, }; enum qca_wlan_vendor_tos { QCA_WLAN_VENDOR_TOS_BK = 0, QCA_WLAN_VENDOR_TOS_BE = 1, QCA_WLAN_VENDOR_TOS_VI = 2, QCA_WLAN_VENDOR_TOS_VO = 3, }; /** * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS. */ enum qca_wlan_vendor_attr_active_tos { QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0, /* Type Of Service - Represented by qca_wlan_vendor_tos */ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1, /* Flag attribute representing the start (attribute included) or stop * (attribute not included) of the respective TOS. */ QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2, }; enum qca_wlan_vendor_hang_reason { /* Unspecified reason */ QCA_WLAN_HANG_REASON_UNSPECIFIED = 0, /* No Map for the MAC entry for the received frame */ QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1, /* Peer deletion timeout happened */ QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2, /* Peer unmap timeout */ QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3, /* Scan request timed out */ QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4, /* Consecutive Scan attempt failures */ QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5, /* Unable to get the message buffer */ QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6, /* Current command processing is timedout */ QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7, /* Timeout for an ACK from FW for suspend request */ QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8, /* Timeout for an ACK from FW for resume request */ QCA_WLAN_HANG_RESUME_TIMEOUT = 9, /* Transmission timeout for consecutive data frames */ QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10, /* Timeout for the TX completion status of data frame */ QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11, /* DXE failure for TX/RX, DXE resource unavailability */ QCA_WLAN_HANG_DXE_FAILURE = 12, /* WMI pending commands exceed the maximum count */ QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13, /* Timeout for peer STA connection accept command's response from the * FW in AP mode. This command is triggered when a STA (peer) connects * to AP (DUT). */ QCA_WLAN_HANG_AP_STA_CONNECT_REQ_TIMEOUT = 14, /* Timeout for the AP connection accept command's response from the FW * in STA mode. This command is triggered when the STA (DUT) connects * to an AP (peer). */ QCA_WLAN_HANG_STA_AP_CONNECT_REQ_TIMEOUT = 15, /* Timeout waiting for the response to the MAC HW mode change command * sent to FW as a part of MAC mode switch among DBS (Dual Band * Simultaneous), SCC (Single Channel Concurrency), and MCC (Multi * Channel Concurrency) mode. */ QCA_WLAN_HANG_MAC_HW_MODE_CHANGE_TIMEOUT = 16, /* Timeout waiting for the response from FW to configure the MAC HW's * mode. This operation is to configure the single/two MACs in either * SCC/MCC/DBS mode. */ QCA_WLAN_HANG_MAC_HW_MODE_CONFIG_TIMEOUT = 17, /* Timeout waiting for response of VDEV start command from the FW */ QCA_WLAN_HANG_VDEV_START_RESPONSE_TIMED_OUT = 18, /* Timeout waiting for response of VDEV restart command from the FW */ QCA_WLAN_HANG_VDEV_RESTART_RESPONSE_TIMED_OUT = 19, /* Timeout waiting for response of VDEV stop command from the FW */ QCA_WLAN_HANG_VDEV_STOP_RESPONSE_TIMED_OUT = 20, /* Timeout waiting for response of VDEV delete command from the FW */ QCA_WLAN_HANG_VDEV_DELETE_RESPONSE_TIMED_OUT = 21, /* Timeout waiting for response of peer all delete request command to * the FW on a specific VDEV. */ QCA_WLAN_HANG_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT = 22, /* WMI sequence mismatch between WMI command and Tx completion */ QCA_WLAN_HANG_WMI_BUF_SEQUENCE_MISMATCH = 23, /* Write to Device HAL register failed */ QCA_WLAN_HANG_REG_WRITE_FAILURE = 24, /* No credit left to send the wow_wakeup_from_sleep to firmware */ QCA_WLAN_HANG_SUSPEND_NO_CREDIT = 25, /* Bus failure */ QCA_WLAN_HANG_BUS_FAILURE = 26, /* tasklet/credit latency found */ QCA_WLAN_HANG_TASKLET_CREDIT_LATENCY_DETECT = 27, }; /** * enum qca_wlan_vendor_attr_hang - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_HANG. */ enum qca_wlan_vendor_attr_hang { QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0, /* Reason for the hang - u32 attribute with a value from enum * qca_wlan_vendor_hang_reason. */ QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1, /* The binary blob data associated with the hang reason specified by * QCA_WLAN_VENDOR_ATTR_HANG_REASON. This binary data is expected to * contain the required dump to analyze the reason for the hang. * NLA_BINARY attribute, the max size is 1024 bytes. */ QCA_WLAN_VENDOR_ATTR_HANG_REASON_DATA = 2, QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_HANG_MAX = QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_flush_pending - Attributes for * flushing pending traffic in firmware. * * @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address. * @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending * packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK, * AC_VI, AC_VO respectively. Set the corresponding bit to 1 to * flush packets with access category. */ enum qca_wlan_vendor_attr_flush_pending { QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0, QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1, QCA_WLAN_VENDOR_ATTR_AC = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX = QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1, }; /** * qca_wlan_vendor_spectral_scan_cap_formula_id: Attribute values for * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID in the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the * Spectral bin scaling formula ID. * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING: No scaling * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED: AGC gain * and RSSI threshold based formula. */ enum qca_wlan_vendor_spectral_scan_cap_formula_id { QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING = 0, QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED = 1, }; /** * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative * RF Operating Parameter (RROP) information is available, and if so, at which * point in the application-driver interaction sequence it can be retrieved by * the application from the driver. This point may vary by architecture and * other factors. This is a u16 value. */ enum qca_wlan_vendor_attr_rropavail_info { /* RROP information is unavailable. */ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE, /* RROP information is available and the application can retrieve the * information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS * event from the driver. */ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START, /* RROP information is available only after a vendor specific scan * (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has * successfully completed. The application can retrieve the information * after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from * the driver. */ QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END, }; /** * enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific * Representative RF Operating Parameter (RROP) information. It is sent for the * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is * intended for use by external Auto Channel Selection applications. It provides * guidance values for some RF parameters that are used by the system during * operation. These values could vary by channel, band, radio, and so on. */ enum qca_wlan_vendor_attr_rrop_info { QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0, /* Representative Tx Power List (RTPL) which has an array of nested * values as per attributes in enum qca_wlan_vendor_attr_rtplinst. */ QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1, QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX = QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list * entry instances in the Representative Tx Power List (RTPL). It provides * simplified power values intended for helping external Auto channel Selection * applications compare potential Tx power performance between channels, other * operating conditions remaining identical. These values are not necessarily * the actual Tx power values that will be used by the system. They are also not * necessarily the max or average values that will be used. Instead, they are * relative, summarized keys for algorithmic use computed by the driver or * underlying firmware considering a number of vendor specific factors. */ enum qca_wlan_vendor_attr_rtplinst { QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0, /* Primary channel number (u8). * Note: If both the driver and user space application support the * 6 GHz band, this attribute is deprecated and * QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY_FREQUENCY should be used. To * maintain backward compatibility, * QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY is still used if either the * driver or user space application or both do not support the 6 GHz * band. */ QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1, /* Representative Tx power in dBm (s32) with emphasis on throughput. */ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2, /* Representative Tx power in dBm (s32) with emphasis on range. */ QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3, /* Primary channel center frequency (u32) in MHz */ QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY_FREQUENCY = 4, QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX = QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_config_latency_level - Level for * wlan latency module. * * There will be various of Wi-Fi functionality like scan/roaming/adaptive * power saving which would causing data exchange out of service, this * would be a big impact on latency. For latency sensitive applications over * Wi-Fi are intolerant to such operations and thus would configure them * to meet their respective needs. It is well understood by such applications * that altering the default behavior would degrade the Wi-Fi functionality * w.r.t the above pointed WLAN operations. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL: * Default WLAN operation level which throughput orientated. * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE: * Use moderate level to improve latency by limit scan duration. * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW: * Use low latency level to benifit application like concurrent * downloading or video streaming via constraint scan/adaptive PS. * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW: * Use ultra low latency level to benefit for gaming/voice * application via constraint scan/roaming/adaptive PS. */ enum qca_wlan_vendor_attr_config_latency_level { QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0, QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1, QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2, QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3, QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX = QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO. */ enum qca_wlan_vendor_attr_mac { QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0, /* MAC mode info list which has an array of nested values as * per attributes in enum qca_wlan_vendor_attr_mac_mode_info. */ QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1, /* keep last */ QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAC_MAX = QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected * Wi-Fi netdev interface on a respective MAC. * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO. */ enum qca_wlan_vendor_attr_mac_iface_info { QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0, /* Wi-Fi netdev's interface index (u32) */ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1, /* Associated frequency in MHz of the connected Wi-Fi interface (u32) */ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX = QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_mac_info - Points to MAC the information. * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the * vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO. */ enum qca_wlan_vendor_attr_mac_info { QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0, /* Hardware MAC ID associated for the MAC (u32) */ QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1, /* Band supported by the MAC at a given point. * This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum * nl80211_band. */ QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2, /* Refers to list of WLAN netdev interfaces associated with this MAC. * Represented by enum qca_wlan_vendor_attr_mac_iface_info. */ QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX = QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET. */ enum qca_wlan_vendor_attr_get_logger_features { QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0, /* Unsigned 32-bit enum value of wifi_logger_supported_features */ QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1, /* keep last */ QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LOGGER_MAX = QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1, }; /** * enum wifi_logger_supported_features - Values for supported logger features */ enum wifi_logger_supported_features { WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)), WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)), WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)), WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)), WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)), WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)), WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)), WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)), WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)), }; /** * enum qca_wlan_tdls_caps_features_supported - Values for TDLS get * capabilities features */ enum qca_wlan_tdls_caps_features_supported { WIFI_TDLS_SUPPORT = (1 << (0)), WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)), WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2)) }; /** * enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES. */ enum qca_wlan_vendor_attr_tdls_get_capabilities { QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0, /* Indicates the max concurrent sessions */ /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS, /* Indicates the support for features */ /* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported */ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED, /* keep last */ QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX = QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1, }; /** * enum qca_wlan_offloaded_packets_sending_control - Offload packets control * command used as value for the attribute * QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL. */ enum qca_wlan_offloaded_packets_sending_control { QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0, QCA_WLAN_OFFLOADED_PACKETS_SENDING_START, QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP }; /** * enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS. */ enum qca_wlan_vendor_attr_offloaded_packets { QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0, /* Takes valid value from the enum * qca_wlan_offloaded_packets_sending_control * Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID, /* array of u8 len: Max packet size */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA, /* 6-byte MAC address used to represent source MAC address */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR, /* 6-byte MAC address used to represent destination MAC address */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR, /* Unsigned 32-bit value, in milli seconds */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD, /* This optional unsigned 16-bit attribute is used for specifying * ethernet protocol type. If not specified ethertype defaults to IPv4. */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE, /* keep last */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX = QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1, }; /** * enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values * by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL. */ enum qca_wlan_rssi_monitoring_control { QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0, QCA_WLAN_RSSI_MONITORING_START, QCA_WLAN_RSSI_MONITORING_STOP, }; /** * enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI. */ enum qca_wlan_vendor_attr_rssi_monitoring { QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0, /* Takes valid value from the enum * qca_wlan_rssi_monitoring_control * Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL, /* Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, /* Signed 8-bit value in dBm */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI, /* Signed 8-bit value in dBm */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI, /* attributes to be used/received in callback */ /* 6-byte MAC address used to represent current BSSID MAC address */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, /* Signed 8-bit value indicating the current RSSI */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, /* keep last */ QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX = QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_NDP. */ enum qca_wlan_vendor_attr_ndp_params { QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0, /* Unsigned 32-bit value * enum of sub commands values in qca_wlan_ndp_sub_cmd */ QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, /* Unsigned 16-bit value */ QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, /* NL attributes for data used NDP SUB cmds */ /* Unsigned 32-bit value indicating a service info */ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID, /* Unsigned 32-bit value; channel frequency in MHz */ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL, /* Interface Discovery MAC address. An array of 6 Unsigned int8 */ QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR, /* Interface name on which NDP is being created */ QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, /* Unsigned 32-bit value for security */ /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY, /* Unsigned 32-bit value for QoS */ QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS, /* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */ QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO, /* Unsigned 32-bit value for NDP instance Id */ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID, /* Array of instance Ids */ QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY, /* Unsigned 32-bit value for initiator/responder NDP response code * accept/reject */ QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE, /* NDI MAC address. An array of 6 Unsigned int8 */ QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR, /* Unsigned 32-bit value errors types returned by driver * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy * NanStatusType includes these values. */ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, /* Unsigned 32-bit value error values returned by driver * The nan_i.h in AOSP project platform/hardware/qcom/wlan * NanInternalStatusType includes these values. */ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, /* Unsigned 32-bit value for Channel setup configuration * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy * NanDataPathChannelCfg includes these values. */ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG, /* Unsigned 32-bit value for Cipher Suite Shared Key Type */ QCA_WLAN_VENDOR_ATTR_NDP_CSID, /* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */ QCA_WLAN_VENDOR_ATTR_NDP_PMK, /* Security Context Identifier that contains the PMKID * Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes */ QCA_WLAN_VENDOR_ATTR_NDP_SCID, /* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */ QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE, /* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */ QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME, /* Unsigned 32-bit bitmap indicating schedule update * BIT_0: NSS Update * BIT_1: Channel list update */ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON, /* Unsigned 32-bit value for NSS */ QCA_WLAN_VENDOR_ATTR_NDP_NSS, /* Unsigned 32-bit value for NUMBER NDP CHANNEL */ QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS, /* Unsigned 32-bit value for CHANNEL BANDWIDTH * 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH, /* Array of channel/band width */ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO, /* IPv6 address used by NDP (in network byte order), 16 bytes array. * This attribute is used and optional for ndp request, ndp response, * ndp indication, and ndp confirm. */ QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27, /* Unsigned 16-bit value indicating transport port used by NDP. * This attribute is used and optional for ndp response, ndp indication, * and ndp confirm. */ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28, /* Unsigned 8-bit value indicating protocol used by NDP and assigned by * the Internet Assigned Numbers Authority (IANA) as per: * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml * This attribute is used and optional for ndp response, ndp indication, * and ndp confirm. */ QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29, /* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE. * 1:support 0:not support */ QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30, /* keep last */ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX = QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1, }; enum qca_wlan_ndp_sub_cmd { QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0, /* Command to create a NAN data path interface */ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1, /* Command to delete a NAN data path interface */ QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2, /* Command to initiate a NAN data path session */ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3, /* Command to notify if the NAN data path session was sent */ QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4, /* Command to respond to NAN data path session */ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5, /* Command to notify on the responder about the response */ QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6, /* Command to initiate a NAN data path end */ QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7, /* Command to notify the if end request was sent */ QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8, /* Command to notify the peer about the end request */ QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9, /* Command to confirm the NAN data path session is complete */ QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10, /* Command to indicate the peer about the end request being received */ QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11, /* Command to indicate the peer of schedule update */ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12 }; /** * enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD. */ enum qca_wlan_vendor_attr_nd_offload { QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0, /* Flag to set Neighbour Discovery offload */ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG, /* Keep last */ QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX = QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1, }; /** * enum packet_filter_sub_cmd - Packet filter sub commands */ enum packet_filter_sub_cmd { /** * Write packet filter program and/or data. The driver/firmware should * disable APF before writing into local buffer and re-enable APF after * writing is done. */ QCA_WLAN_SET_PACKET_FILTER = 1, /* Get packet filter feature capabilities from driver */ QCA_WLAN_GET_PACKET_FILTER = 2, /** * Write packet filter program and/or data. User space will send the * %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command * and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key * difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over * enable/disable is given to user space with this command. Also, * user space sends the length of program portion in the buffer within * %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH. */ QCA_WLAN_WRITE_PACKET_FILTER = 3, /* Read packet filter program and/or data */ QCA_WLAN_READ_PACKET_FILTER = 4, /* Enable APF feature */ QCA_WLAN_ENABLE_PACKET_FILTER = 5, /* Disable APF feature */ QCA_WLAN_DISABLE_PACKET_FILTER = 6, }; /** * enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by * vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER. */ enum qca_wlan_vendor_attr_packet_filter { QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0, /* Unsigned 32-bit enum passed using packet_filter_sub_cmd */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD, /* Unsigned 32-bit value indicating the packet filter version */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION, /* Unsigned 32-bit value indicating the packet filter id */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID, /** * Unsigned 32-bit value indicating the packet filter size including * program + data. */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE, /* Unsigned 32-bit value indicating the packet filter current offset */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET, /* Program and/or data in bytes */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM, /* Unsigned 32-bit value of the length of the program section in packet * filter buffer. */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7, /* keep last */ QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX = QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command * QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE. */ enum qca_wlan_vendor_drv_info { QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0, /* Maximum Message size info between firmware & HOST * Unsigned 32-bit value */ QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE, /* keep last */ QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX = QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor * command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS. */ enum qca_wlan_vendor_attr_wake_stats { QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0, /* Unsigned 32-bit value indicating the total count of wake event */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE, /* Array of individual wake count, each index representing wake reason */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR, /* Unsigned 32-bit value representing wake count array */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ, /* Unsigned 32-bit total wake count value of driver/fw */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE, /* Array of wake stats of driver/fw */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR, /* Unsigned 32-bit total wake count value of driver/fw */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ, /* Unsigned 32-bit total wake count value of packets received */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE, /* Unsigned 32-bit wake count value unicast packets received */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT, /* Unsigned 32-bit wake count value multicast packets received */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT, /* Unsigned 32-bit wake count value broadcast packets received */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT, /* Unsigned 32-bit wake count value of ICMP packets */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT, /* Unsigned 32-bit wake count value of ICMP6 packets */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT, /* Unsigned 32-bit value ICMP6 router advertisement */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA, /* Unsigned 32-bit value ICMP6 neighbor advertisement */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA, /* Unsigned 32-bit value ICMP6 neighbor solicitation */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS, /* Unsigned 32-bit wake count value of receive side ICMP4 multicast */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT, /* Unsigned 32-bit wake count value of receive side ICMP6 multicast */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT, /* Unsigned 32-bit wake count value of receive side multicast */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT, /* Unsigned 32-bit wake count value of a given RSSI breach */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT, /* Unsigned 32-bit wake count value of low RSSI */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT, /* Unsigned 32-bit value GSCAN count */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT, /* Unsigned 32-bit value PNO complete count */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT, /* Unsigned 32-bit value PNO match count */ QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT, /* keep last */ QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST, QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX = QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_thermal_level - Defines various thermal levels * configured by userspace to the driver/firmware. * The values can be encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL or * QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL attribute. * The driver/firmware takes actions requested by userspace such as throttling * wifi TX etc. in order to mitigate high temperature. * * @QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE: Stop/clear all throttling actions. * @QCA_WLAN_VENDOR_THERMAL_LEVEL_LIGHT: Throttle TX lightly. * @QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE: Throttle TX moderately. * @QCA_WLAN_VENDOR_THERMAL_LEVEL_SEVERE: Throttle TX severely. * @QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL: Critical thermal level reached. * @QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY: Emergency thermal level reached. */ enum qca_wlan_vendor_thermal_level { QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE = 0, QCA_WLAN_VENDOR_THERMAL_LEVEL_LIGHT = 1, QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE = 2, QCA_WLAN_VENDOR_THERMAL_LEVEL_SEVERE = 3, QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL = 4, QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY = 5, }; /** * enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set * cmd value. Used for NL attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. */ enum qca_wlan_vendor_attr_thermal_cmd { QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0, /* The value of command, driver will implement different operations * according to this value. It uses values defined in * enum qca_wlan_vendor_attr_thermal_cmd_type. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1, /* Userspace uses this attribute to configure thermal level to the * driver/firmware, or get thermal level from the driver/firmware. * Used in request or response, u32 attribute, * possible values are defined in enum qca_wlan_vendor_thermal_level. */ QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL = 2, /* Userspace uses this attribute to configure the time in which the * driver/firmware should complete applying settings it received from * userspace with QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL * command type. Used in request, u32 attribute, value is in * milliseconds. A value of zero indicates to apply the settings * immediately. The driver/firmware can delay applying the configured * thermal settings within the time specified in this attribute if * there is any critical ongoing operation. */ QCA_WLAN_VENDOR_ATTR_THERMAL_COMPLETION_WINDOW = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX = QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1 }; /** * qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the * thermal command types sent to driver. * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to * get thermal shutdown configuration parameters for display. Parameters * responded from driver are defined in * enum qca_wlan_vendor_attr_get_thermal_params_rsp. * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to * get temperature. Host should respond with a temperature data. It is defined * in enum qca_wlan_vendor_attr_thermal_get_temperature. * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal * suspend action. * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal * resume action. * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL: Configure thermal level to * the driver/firmware. * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL: Request to get the current * thermal level from the driver/firmware. The driver should respond with a * thermal level defined in enum qca_wlan_vendor_thermal_level. */ enum qca_wlan_vendor_attr_thermal_cmd_type { QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS, QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE, QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND, QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME, QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL, QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL, }; /** * enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes * to get chip temperature by user. * enum values are used for NL attributes for data used by * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used * by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. */ enum qca_wlan_vendor_attr_thermal_get_temperature { QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0, /* Temperature value (degree Celsius) from driver. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA, /* keep last */ QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX = QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes * to get configuration parameters of thermal shutdown feature. Enum values are * used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data * used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. */ enum qca_wlan_vendor_attr_get_thermal_params_rsp { QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0, /* Indicate if the thermal shutdown feature is enabled. * NLA_FLAG attribute. */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN, /* Indicate if the auto mode is enabled. * Enable: Driver triggers the suspend/resume action. * Disable: User space triggers the suspend/resume action. * NLA_FLAG attribute. */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN, /* Thermal resume threshold (degree Celsius). Issue the resume command * if the temperature value is lower than this threshold. * u16 attribute. */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH, /* Thermal warning threshold (degree Celsius). FW reports temperature * to driver if it's higher than this threshold. * u16 attribute. */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH, /* Thermal suspend threshold (degree Celsius). Issue the suspend command * if the temperature value is higher than this threshold. * u16 attribute. */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH, /* FW reports temperature data periodically at this interval (ms). * u16 attribute. */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX = QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to * report thermal events from driver to user space. * enum values are used for NL attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command. */ enum qca_wlan_vendor_attr_thermal_event { QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0, /* Temperature value (degree Celsius) from driver. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE, /* Indication of resume completion from power save mode. * NLA_FLAG attribute. */ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE, /* Thermal level from the driver. * u32 attribute. Possible values are defined in * enum qca_wlan_vendor_thermal_level. */ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX = QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1, }; /** * enum he_fragmentation_val - HE fragmentation support values * Indicates level of dynamic fragmentation that is supported by * a STA as a recipient. * HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2 * (HE MAC Capabilities Information field) and are used in HE Capabilities * element to advertise the support. These values are validated in the driver * to check the device capability and advertised in the HE Capabilities * element. These values are used to configure testbed device to allow the * advertised hardware capabilities to be downgraded for testing purposes. * * @HE_FRAG_DISABLE: no support for dynamic fragmentation * @HE_FRAG_LEVEL1: support for dynamic fragments that are * contained within an MPDU or S-MPDU, no support for dynamic fragments * within an A-MPDU that is not an S-MPDU. * @HE_FRAG_LEVEL2: support for dynamic fragments that are * contained within an MPDU or S-MPDU and support for up to one dynamic * fragment for each MSDU, each A-MSDU if supported by the recipient, and * each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an * MPDU or S-MPDU. * @HE_FRAG_LEVEL3: support for dynamic fragments that are * contained within an MPDU or S-MPDU and support for multiple dynamic * fragments for each MSDU and for each A-MSDU if supported by the * recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic * fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU. */ enum he_fragmentation_val { HE_FRAG_DISABLE, HE_FRAG_LEVEL1, HE_FRAG_LEVEL2, HE_FRAG_LEVEL3, }; /** * enum he_mcs_config - HE MCS support configuration * * Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth. * These values are used in driver to configure the HE MCS map to advertise * Tx/Rx MCS map in HE capability and these values are applied for all the * streams supported by the device. To configure MCS for different bandwidths, * vendor command needs to be sent using this attribute with appropriate value. * For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS * attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11 * send this command using HE MCS config attribute with value HE_160_MCS0_11. * These values are used to configure testbed device to allow the advertised * hardware capabilities to be downgraded for testing purposes. The enum values * are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and * 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map. * These values are validated in the driver before setting the MCS map and * driver returns error if the input is other than these enum values. * * @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7 * @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9 * @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11 * @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7 * @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9 * @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11 * @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7 * @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9 * @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11 */ enum he_mcs_config { HE_80_MCS0_7 = 0, HE_80_MCS0_9 = 1, HE_80_MCS0_11 = 2, HE_160_MCS0_7 = 4, HE_160_MCS0_9 = 5, HE_160_MCS0_11 = 6, HE_80P80_MCS0_7 = 8, HE_80P80_MCS0_9 = 9, HE_80P80_MCS0_11 = 10, }; /** * enum qca_wlan_ba_session_config - BA session configuration * * Indicates the configuration values for BA session configuration attribute. * * @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration. * @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID. */ enum qca_wlan_ba_session_config { QCA_WLAN_ADD_BA = 1, QCA_WLAN_DELETE_BA = 2, }; /** * enum qca_wlan_ac_type - Access category type * * Indicates the access category type value. * * @QCA_WLAN_AC_BE: BE access category * @QCA_WLAN_AC_BK: BK access category * @QCA_WLAN_AC_VI: VI access category * @QCA_WLAN_AC_VO: VO access category * @QCA_WLAN_AC_ALL: All ACs */ enum qca_wlan_ac_type { QCA_WLAN_AC_BE = 0, QCA_WLAN_AC_BK = 1, QCA_WLAN_AC_VI = 2, QCA_WLAN_AC_VO = 3, QCA_WLAN_AC_ALL = 4, }; /** * enum qca_wlan_he_ltf_cfg - HE LTF configuration * * Indicates the HE LTF configuration value. * * @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF, * based on the GI setting * @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF * @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF * @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF */ enum qca_wlan_he_ltf_cfg { QCA_WLAN_HE_LTF_AUTO = 0, QCA_WLAN_HE_LTF_1X = 1, QCA_WLAN_HE_LTF_2X = 2, QCA_WLAN_HE_LTF_4X = 3, }; /** * enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration * * Indicates the HE trigger frame MAC padding duration value. * * @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to * process the trigger frame. * @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for * trigger frame. * @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for * trigger frame. */ enum qca_wlan_he_mac_padding_dur { QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0, QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1, QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2, }; /** * enum qca_wlan_he_om_ctrl_ch_bw - HE OM control field BW configuration * * Indicates the HE Operating mode control channel width setting value. * * @QCA_WLAN_HE_OM_CTRL_BW_20M: Primary 20 MHz * @QCA_WLAN_HE_OM_CTRL_BW_40M: Primary 40 MHz * @QCA_WLAN_HE_OM_CTRL_BW_80M: Primary 80 MHz * @QCA_WLAN_HE_OM_CTRL_BW_160M: 160 MHz and 80+80 MHz */ enum qca_wlan_he_om_ctrl_ch_bw { QCA_WLAN_HE_OM_CTRL_BW_20M = 0, QCA_WLAN_HE_OM_CTRL_BW_40M = 1, QCA_WLAN_HE_OM_CTRL_BW_80M = 2, QCA_WLAN_HE_OM_CTRL_BW_160M = 3, }; /** * enum qca_wlan_keep_alive_data_type - Keep alive data type configuration * * Indicates the frame types to use for keep alive data. * * @QCA_WLAN_KEEP_ALIVE_DEFAULT: Driver default type used for keep alive. * @QCA_WLAN_KEEP_ALIVE_DATA: Data frame type for keep alive. * @QCA_WLAN_KEEP_ALIVE_MGMT: Management frame type for keep alive. */ enum qca_wlan_keep_alive_data_type { QCA_WLAN_KEEP_ALIVE_DEFAULT = 0, QCA_WLAN_KEEP_ALIVE_DATA = 1, QCA_WLAN_KEEP_ALIVE_MGMT = 2, }; /** * enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for * HE operating mode control transmit request. These attributes are * sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. * * @QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: Mandatory 8-bit unsigned value * indicates the maximum number of spatial streams, NSS, that the STA * supports in reception for PPDU bandwidths less than or equal to 80 MHz * and is set to NSS - 1. * * @QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: Mandatory 8-bit unsigned value * indicates the operating channel width supported by the STA for both * reception and transmission. Uses enum qca_wlan_he_om_ctrl_ch_bw values. * * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: Mandatory 8-bit unsigned value * indicates the all trigger based UL MU operations by the STA. * 0 - UL MU operations are enabled by the STA. * 1 - All triggered UL MU transmissions are suspended by the STA. * * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value * indicates the maximum number of space-time streams, NSTS, that * the STA supports in transmission and is set to NSTS - 1. * * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value * combined with the UL MU Disable subfield and the recipient's setting * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC * capabilities to determine which HE TB PPDUs are possible by the * STA to transmit. * 0 - UL MU data operations are enabled by the STA. * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable * bit is not set, else UL MU Tx is suspended. * */ enum qca_wlan_vendor_attr_he_omi_tx { QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0, QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS = 1, QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2, QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3, QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4, QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX = QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_phy_mode - Different PHY modes * These values are used with %QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE. * * @QCA_WLAN_VENDOR_PHY_MODE_AUTO: autoselect * @QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO: 2.4 GHz 802.11b/g/n/ax autoselect * @QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO: 5 GHz 802.11a/n/ac/ax autoselect * @QCA_WLAN_VENDOR_PHY_MODE_11A: 5 GHz, OFDM * @QCA_WLAN_VENDOR_PHY_MODE_11B: 2.4 GHz, CCK * @QCA_WLAN_VENDOR_PHY_MODE_11G: 2.4 GHz, OFDM * @QCA_WLAN_VENDOR_PHY_MODE_11AGN: Support 802.11n in both 2.4 GHz and 5 GHz * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20: 2.4 GHz, HT20 * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS: 2.4 GHz, HT40 (ext ch +1) * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS: 2.4 GHz, HT40 (ext ch -1) * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40: 2.4 GHz, Auto HT40 * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20: 5 GHz, HT20 * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS: 5 GHz, HT40 (ext ch +1) * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS: 5 GHz, HT40 (ext ch -1) * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40: 5 GHz, Auto HT40 * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20: 5 GHz, VHT20 * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS: 5 GHz, VHT40 (Ext ch +1) * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS: 5 GHz VHT40 (Ext ch -1) * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40: 5 GHz, VHT40 * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80: 5 GHz, VHT80 * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80: 5 GHz, VHT80+80 * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160: 5 GHz, VHT160 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20: HE20 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40: HE40 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS: HE40 (ext ch +1) * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS: HE40 (ext ch -1) * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160 */ enum qca_wlan_vendor_phy_mode { QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0, QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO = 1, QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO = 2, QCA_WLAN_VENDOR_PHY_MODE_11A = 3, QCA_WLAN_VENDOR_PHY_MODE_11B = 4, QCA_WLAN_VENDOR_PHY_MODE_11G = 5, QCA_WLAN_VENDOR_PHY_MODE_11AGN = 6, QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20 = 7, QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS = 8, QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS = 9, QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40 = 10, QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20 = 11, QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS = 12, QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS = 13, QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40 = 14, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20 = 15, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS = 16, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS = 17, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40 = 18, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80 = 19, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80 = 20, QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160 = 21, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20 = 22, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40 = 23, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS = 24, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS = 25, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28, }; /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION */ enum qca_wlan_vendor_attr_wifi_test_config { QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0, /* 8-bit unsigned value to configure the driver to enable/disable * WMM feature. This attribute is used to configure testbed device. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1, /* 8-bit unsigned value to configure the driver to accept/reject * the addba request from peer. This attribute is used to configure * the testbed device. * 1-accept addba, 0-reject addba */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2, /* 8-bit unsigned value to configure the driver to send or not to * send the addba request to peer. * This attribute is used to configure the testbed device. * 1-send addba, 0-do not send addba */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3, /* 8-bit unsigned value to indicate the HE fragmentation support. * Uses enum he_fragmentation_val values. * This attribute is used to configure the testbed device to * allow the advertised hardware capabilities to be downgraded * for testing purposes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4, /* 8-bit unsigned value to indicate the HE MCS support. * Uses enum he_mcs_config values. * This attribute is used to configure the testbed device to * allow the advertised hardware capabilities to be downgraded * for testing purposes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5, /* 8-bit unsigned value to configure the driver to allow or not to * allow the connection with WEP/TKIP in HT/VHT/HE modes. * This attribute is used to configure the testbed device. * 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6, /* 8-bit unsigned value to configure the driver to add a * new BA session or delete the existing BA session for * given TID. ADDBA command uses the buffer size and TID * configuration if user specifies the values else default * value for buffer size is used for all TIDs if the TID * also not specified. For DEL_BA command TID value is * required to process the command. * Uses enum qca_wlan_ba_session_config values. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7, /* 16-bit unsigned value to configure the buffer size in addba * request and response frames. * This attribute is used to configure the testbed device. * The range of the value is 0 to 256. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8, /* 8-bit unsigned value to configure the buffer size in addba * request and response frames. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9, /* 8-bit unsigned value to configure the no ack policy. * To configure no ack policy, access category value is * required to process the command. * This attribute is used to configure the testbed device. * 1 - enable no ack, 0 - disable no ack. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10, /* 8-bit unsigned value to configure the AC for no ack policy * This attribute is used to configure the testbed device. * Uses the enum qca_wlan_ac_type values. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11, /* 8-bit unsigned value to configure the HE LTF * This attribute is used to configure the testbed device. * Uses the enum qca_wlan_he_ltf_cfg values. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12, /* 8-bit unsigned value to configure the tx beamformee. * This attribute is used to configure the testbed device. * 1-enable, 0-disable. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13, /* 8-bit unsigned value to configure the tx beamformee number * of space-time streams. * This attribute is used to configure the testbed device. * The range of the value is 0 to 8. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14, /* 8-bit unsigned value to configure the MU EDCA params for given AC * This attribute is used to configure the testbed device. * Uses the enum qca_wlan_ac_type values. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15, /* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC * To configure MU EDCA AIFSN value, MU EDCA access category value * is required to process the command. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16, /* 8-bit unsigned value to configure the MU EDCA ECW min value for * given AC. * To configure MU EDCA ECW min value, MU EDCA access category value * is required to process the command. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17, /* 8-bit unsigned value to configure the MU EDCA ECW max value for * given AC. * To configure MU EDCA ECW max value, MU EDCA access category value * is required to process the command. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18, /* 8-bit unsigned value to configure the MU EDCA timer for given AC * To configure MU EDCA timer value, MU EDCA access category value * is required to process the command. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19, /* 8-bit unsigned value to configure the HE trigger frame MAC padding * duration. * This attribute is used to configure the testbed device. * Uses the enum qca_wlan_he_mac_padding_dur values. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20, /* 8-bit unsigned value to override the MU EDCA params to defaults * regardless of the AP beacon MU EDCA params. If it is enabled use * the default values else use the MU EDCA params from AP beacon. * This attribute is used to configure the testbed device. * 1-enable, 0-disable. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21, /* 8-bit unsigned value to configure the support for receiving * an MPDU that contains an operating mode control subfield. * This attribute is used to configure the testbed device. * 1-enable, 0-disable. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22, /* Nested attribute values required to setup the TWT session. * enum qca_wlan_vendor_attr_twt_setup provides the necessary * information to set up the session. It contains broadcast flags, * set_up flags, trigger value, flow type, flow ID, wake interval * exponent, protection, target wake time, wake duration, wake interval * mantissa. These nested attributes are used to setup a host triggered * TWT session. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23, /* This nested attribute is used to terminate the current TWT session. * It does not currently carry any attributes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24, /* This nested attribute is used to suspend the current TWT session. * It does not currently carry any attributes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25, /* Nested attribute values to indicate the request for resume. * This attribute is used to resume the TWT session. * enum qca_wlan_vendor_attr_twt_resume provides the necessary * parameters required to resume the TWT session. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26, /* 8-bit unsigned value to set the HE operating mode control * (OM CTRL) Channel Width subfield. * The Channel Width subfield indicates the operating channel width * supported by the STA for both reception and transmission. * Uses the enum qca_wlan_he_om_ctrl_ch_bw values. * This setting is cleared with the * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG * flag attribute to reset defaults. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW = 27, /* 8-bit unsigned value to configure the number of spatial * streams in HE operating mode control field. * This setting is cleared with the * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG * flag attribute to reset defaults. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS = 28, /* Flag attribute to configure the UL MU disable bit in * HE operating mode control field. * This setting is cleared with the * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG * flag attribute to reset defaults. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_UL_MU_DISABLE = 29, /* Flag attribute to clear the previously set HE operating mode * control field configuration. * This attribute is used to configure the testbed device to reset * defaults to clear any previously set HE operating mode control * field configuration. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG = 30, /* 8-bit unsigned value to configure HE single user PPDU * transmission. By default this setting is disabled and it * is disabled in the reset defaults of the device configuration. * This attribute is used to configure the testbed device. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU = 31, /* 8-bit unsigned value to configure action frame transmission * in HE trigger based PPDU transmission. * By default this setting is disabled and it is disabled in * the reset defaults of the device configuration. * This attribute is used to configure the testbed device. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32, /* Nested attribute to indicate HE operating mode control field * transmission. It contains operating mode control field Nss, * channel bandwidth, Tx Nsts and UL MU disable attributes. * These nested attributes are used to send HE operating mode control * with configured values. * Uses the enum qca_wlan_vendor_attr_he_omi_tx attributes. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX = 33, /* 8-bit unsigned value to configure +HTC_HE support to indicate the * support for the reception of a frame that carries an HE variant * HT Control field. * This attribute is used to configure the testbed device. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34, /* 8-bit unsigned value to configure VHT support in 2.4G band. * This attribute is used to configure the testbed device. * 1-enable, 0-disable */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35, /* 8-bit unsigned value to configure HE testbed defaults. * This attribute is used to configure the testbed device. * 1-set the device HE capabilities to testbed defaults. * 0-reset the device HE capabilities to supported config. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36, /* 8-bit unsigned value to configure TWT request support. * This attribute is used to configure the testbed device. * 1-enable, 0-disable. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37, /* 8-bit unsigned value to configure protection for Management * frames when PMF is enabled for the association. * This attribute is used to configure the testbed device. * 0-use the correct key, 1-use an incorrect key, 2-disable protection. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION = 38, /* Flag attribute to inject Disassociation frame to the connected AP. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX = 39, /* 8-bit unsigned value to configure an override for the RSNXE Used * subfield in the MIC control field of the FTE in FT Reassociation * Request frame. * 0 - Default behavior, 1 - override with 1, 2 - override with 0. * This attribute is used to configure the testbed device. * This attribute can be configured only when STA is in associated state * and the configuration is valid until the disconnection. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED = 40, /* 8-bit unsigned value to configure the driver to ignore CSA (Channel * Switch Announcement) when STA is in connected state. * 0 - Default behavior, 1 - Ignore CSA. * This attribute is used to configure the testbed device. * This attribute can be configured only when STA is in associated state * and the configuration is valid until the disconnection. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA = 41, /* Nested attribute values required to configure OCI (Operating Channel * Information). Attributes defined in enum * qca_wlan_vendor_attr_oci_override are nested within this attribute. * This attribute is used to configure the testbed device. * This attribute can be configured only when STA is in associated state * and the configuration is valid until the disconnection. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE = 42, /* 8-bit unsigned value to configure the driver/firmware to ignore SA * Query timeout. If this configuration is enabled STA shall not send * Deauthentication frmae when SA Query times out (mainly, after a * channel switch when OCV is enabled). * 0 - Default behavior, 1 - Ignore SA Query timeout. * This attribute is used to configure the testbed device. * This attribute can be configured only when STA is in associated state * and the configuration is valid until the disconnection. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT = 43, /* 8-bit unsigned value to configure the driver/firmware to start or * stop transmitting FILS discovery frames. * 0 - Stop transmitting FILS discovery frames * 1 - Start transmitting FILS discovery frames * This attribute is used to configure the testbed device. * This attribute can be configured only in AP mode and the * configuration is valid until AP restart. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX = 44, /* 8-bit unsigned value to configure the driver/firmware to enable or * disable full bandwidth UL MU-MIMO subfield in the HE PHY capabilities * information field. * 0 - Disable full bandwidth UL MU-MIMO subfield * 1 - Enable full bandwidth UL MU-MIMO subfield * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FULL_BW_UL_MU_MIMO = 45, /* 16-bit unsigned value to configure the driver with a specific BSS * max idle period to advertise in the BSS Max Idle Period element * (IEEE Std 802.11-2016, 9.4.2.79) in (Re)Association Request frames. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD = 46, /* 8-bit unsigned value to configure the driver to use only RU 242 tone * for data transmission. * 0 - Default behavior, 1 - Configure RU 242 tone for data Tx. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RU_242_TONE_TX = 47, /* 8-bit unsigned value to configure the driver to disable data and * management response frame transmission to test the BSS max idle * feature. * 0 - Default behavior, 1 - Disable data and management response Tx. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_DATA_MGMT_RSP_TX = 48, /* 8-bit unsigned value to configure the driver/firmware to enable or * disable Punctured Preamble Rx subfield in the HE PHY capabilities * information field. * 0 - Disable Punctured Preamble Rx subfield * 1 - Enable Punctured Preamble Rx subfield * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PUNCTURED_PREAMBLE_RX = 49, /* 8-bit unsigned value to configure the driver to ignore the SAE H2E * requirement mismatch for 6 GHz connection. * 0 - Default behavior, 1 - Ignore SAE H2E requirement mismatch. * This attribute is used to configure the testbed device. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_H2E_RSNXE = 50, /* 8-bit unsigned value to configure the driver to allow 6 GHz * connection with all security modes. * 0 - Default behavior, 1 - Allow 6 GHz connection with all security * modes. * This attribute is used for testing purposes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_6GHZ_SECURITY_TEST_MODE = 51, /* 8-bit unsigned value to configure the driver to transmit data with * ER SU PPDU type. * * 0 - Default behavior, 1 - Enable ER SU PPDU type TX. * This attribute is used for testing purposes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ER_SU_PPDU_TYPE = 52, /* 8-bit unsigned value to configure the driver to use Data or * Management frame type for keep alive data. * Uses enum qca_wlan_keep_alive_data_type values. * * This attribute is used for testing purposes. */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_KEEP_ALIVE_FRAME_TYPE = 53, + /* 8-bit unsigned value to configure the driver to use scan request + * BSSID value in Probe Request frame RA(A1) during the scan. The + * driver saves this configuration and applies this setting to all user + * space scan requests until the setting is cleared. If this + * configuration is set, the driver uses the BSSID value from the scan + * request to set the RA(A1) in the Probe Request frames during the + * scan. + * + * 0 - Default behavior uses the broadcast RA in Probe Request frames. + * 1 - Uses the scan request BSSID in RA in Probe Request frames. + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_USE_BSSID_IN_PROBE_REQ_RA = 54, + + /* 8-bit unsigned value to configure the driver to enable/disable the + * BSS max idle period support. + * + * 0 - Disable the BSS max idle support. + * 1 - Enable the BSS max idle support. + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD_ENABLE = 55, + /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1, }; /** * enum qca_wlan_twt_operation - Operation of the config TWT request * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION. * The response for the respective operations can be either synchronous or * asynchronous (wherever specified). If synchronous, the response to this * operation is obtained in the corresponding vendor command reply to the user * space. For the asynchronous case the response is obtained as an event with * the same operation type. * * Drivers shall support either of these modes but not both simultaneously. * This support for asynchronous mode is advertised through the flag * QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT. If this flag is not advertised, * the driver shall support synchronous mode. * * @QCA_WLAN_TWT_SET: Setup a TWT session. Required parameters are configured * through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_setup. Depending upon the * @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, this is either a * synchronous or asynchronous operation. * * @QCA_WLAN_TWT_GET: Get the configured TWT parameters. Required parameters are * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_setup. This is a synchronous operation. * * @QCA_WLAN_TWT_TERMINATE: Terminate the TWT session. Required parameters are * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup. * This terminate can either get triggered by the user space or can as well be * a notification from the firmware if it initiates a terminate. * Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, * the request from user space can either be a synchronous or asynchronous * operation. * * @QCA_WLAN_TWT_SUSPEND: Suspend the TWT session. Required parameters are * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup. * Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, * this is either a synchronous or asynchronous operation. * * @QCA_WLAN_TWT_RESUME: Resume the TWT session. Required parameters are * configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum * qca_wlan_vendor_attr_twt_resume. Valid only after the TWT session is setup. * This can as well be a notification from the firmware on a QCA_WLAN_TWT_NUDGE * request. Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT * capability, this is either a synchronous or asynchronous operation. * * @QCA_WLAN_TWT_NUDGE: Suspend and resume the TWT session. TWT nudge is a * combination of suspend and resume in a single request. Required parameters * are configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the * enum qca_wlan_vendor_attr_twt_nudge. Valid only after the TWT session is * setup. Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT * capability, this is either a synchronous or asynchronous operation. * * @QCA_WLAN_TWT_GET_STATS: Get the TWT session traffic statistics information. * Refers the enum qca_wlan_vendor_attr_twt_stats. Valid only after the TWT * session is setup. It's a synchronous operation. * * @QCA_WLAN_TWT_CLEAR_STATS: Clear TWT session traffic statistics information. * Valid only after the TWT session is setup. It's a synchronous operation. * * @QCA_WLAN_TWT_GET_CAPABILITIES: Get TWT capabilities of this device and its * peer. Refers the enum qca_wlan_vendor_attr_twt_capability. It's a synchronous * operation. * * @QCA_WLAN_TWT_SETUP_READY_NOTIFY: Notify userspace that the firmare is * ready for a new TWT session setup after it issued a TWT teardown. */ enum qca_wlan_twt_operation { QCA_WLAN_TWT_SET = 0, QCA_WLAN_TWT_GET = 1, QCA_WLAN_TWT_TERMINATE = 2, QCA_WLAN_TWT_SUSPEND = 3, QCA_WLAN_TWT_RESUME = 4, QCA_WLAN_TWT_NUDGE = 5, QCA_WLAN_TWT_GET_STATS = 6, QCA_WLAN_TWT_CLEAR_STATS = 7, QCA_WLAN_TWT_GET_CAPABILITIES = 8, QCA_WLAN_TWT_SETUP_READY_NOTIFY = 9, }; /** * enum qca_wlan_vendor_attr_config_twt: Defines attributes used by * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION: u8 attribute. Specify the TWT * operation of this request. Possible values are defined in enum * qca_wlan_twt_operation. The parameters for the respective operation is * specified through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the * parameters configured for TWT. These parameters are represented by * enum qca_wlan_vendor_attr_twt_setup, enum qca_wlan_vendor_attr_twt_resume, * or enum qca_wlan_vendor_attr_twt_stats based on the operation. */ enum qca_wlan_vendor_attr_config_twt { QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0, QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION = 1, QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_MAX = QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. * The user can add/delete the filter by specifying the BSSID/STA MAC address in * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the * statistics of an unassociated station by specifying the MAC address in * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get * the statistics of all unassociated stations by specifying the Broadcast MAC * address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with * above procedure. In the response, driver shall specify statistics * information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS. */ enum qca_wlan_vendor_attr_bss_filter { QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0, QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1, /* Other BSS filter type, unsigned 8 bit value. One of the values * in enum qca_wlan_vendor_bss_filter_type. */ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2, /* Other BSS filter action, unsigned 8 bit value. One of the values * in enum qca_wlan_vendor_bss_filter_action. */ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3, /* Array of nested attributes where each entry is the statistics * information of the specified station that belong to another BSS. * Attributes for each entry are taken from enum * qca_wlan_vendor_bss_filter_sta_stats. * Other BSS station configured in * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type * QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA. * Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER * with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET. */ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX = QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_bss_filter_type - Type of * filter used in other BSS filter operations. Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. * * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter */ enum qca_wlan_vendor_bss_filter_type { QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID, QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA, }; /** * enum qca_wlan_vendor_bss_filter_action - Type of * action in other BSS filter operations. Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. * * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics */ enum qca_wlan_vendor_bss_filter_action { QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD, QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL, QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET, }; /** * enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for * the statistics of a specific unassociated station belonging to another BSS. * The statistics provides information of the unassociated station * filtered by other BSS operation - such as MAC, signal value. * Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. * * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station. * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength * of the station. Unsigned 8 bit number containing RSSI. * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host * driver for the last received RSSI. Unsigned 64 bit number containing * nanoseconds from the boottime. */ enum qca_wlan_vendor_bss_filter_sta_stats { QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0, QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1, QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2, QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3, /* keep last */ QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST, QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX = QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1 }; /* enum qca_wlan_nan_subcmd_type - Type of NAN command used by attribute * QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE as a part of vendor command * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. */ enum qca_wlan_nan_ext_subcmd_type { /* Subcmd of type NAN Enable Request */ QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ = 1, /* Subcmd of type NAN Disable Request */ QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ = 2, }; /** * enum qca_wlan_vendor_attr_nan_params - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. */ enum qca_wlan_vendor_attr_nan_params { QCA_WLAN_VENDOR_ATTR_NAN_INVALID = 0, /* Carries NAN command for firmware component. Every vendor command * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT must contain this attribute with a * payload containing the NAN command. NLA_BINARY attribute. */ QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA = 1, /* Indicates the type of NAN command sent with * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. enum qca_wlan_nan_ext_subcmd_type * describes the possible range of values. This attribute is mandatory * if the command being issued is either * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ or * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ. NLA_U32 attribute. */ QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE = 2, /* Frequency (in MHz) of primary NAN discovery social channel in 2.4 GHz * band. This attribute is mandatory when command type is * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ. NLA_U32 attribute. */ QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ = 3, /* Frequency (in MHz) of secondary NAN discovery social channel in 5 GHz * band. This attribute is optional and should be included when command * type is QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ and NAN discovery * has to be started on 5GHz along with 2.4GHz. NLA_U32 attribute. */ QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX = QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1 }; /** * qca_wlan_twt_setup_state: Represents the TWT session states. * * QCA_WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED: TWT session not established. * QCA_WLAN_TWT_SETUP_STATE_ACTIVE: TWT session is active. * QCA_WLAN_TWT_SETUP_STATE_SUSPEND: TWT session is in suspended state. */ enum qca_wlan_twt_setup_state { QCA_WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED = 0, QCA_WLAN_TWT_SETUP_STATE_ACTIVE = 1, QCA_WLAN_TWT_SETUP_STATE_SUSPEND = 2, }; /** * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for * TWT (Target Wake Time) setup request. These attributes are sent as part of * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute. * Disable (flag attribute not present) - Individual TWT * Enable (flag attribute present) - Broadcast TWT. * Individual means the session is between the STA and the AP. * This session is established using a separate negotiation between * STA and AP. * Broadcast means the session is across multiple STAs and an AP. The * configuration parameters are announced in Beacon frames by the AP. * This is used in * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8). * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to * specify the TWT request type. This is used in TWT SET operation. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute * Enable (flag attribute present) - TWT with trigger support. * Disable (flag attribute not present) - TWT without trigger support. * Trigger means the AP will send the trigger frame to allow STA to send data. * Without trigger, the STA will wait for the MU EDCA timer before * transmitting the data. * This is used in * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8) * 0 - Announced TWT - In this mode, STA may skip few service periods to * save more power. If STA wants to wake up, it will send a PS-POLL/QoS * NULL frame to AP. * 1 - Unannounced TWT - The STA will wakeup during every SP. * This is a required parameter for * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8) * Flow ID is the unique identifier for each TWT session. * If not provided then dialog ID will be set to zero. * This is an optional parameter for * 1. TWT SET Request and Response * 2. TWT GET Request and Response * 3. TWT TERMINATE Request and Response * 4. TWT SUSPEND Request and Response * Flow ID values from 0 to 254 represent a single TWT session * Flow ID value of 255 represents all TWT sessions for the following * 1. TWT TERMINATE Request and Response * 2. TWT SUSPEND Request and Response * 4. TWT CLEAR STATISTICS request * 5. TWT GET STATISTICS request and response * If an invalid dialog ID is provided, status * QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8) * This attribute (exp) is used along with the mantissa to derive the * wake interval using the following formula: * pow(2,exp) = wake_intvl_us/wake_intvl_mantis * Wake interval is the interval between 2 successive SP. * This is a required parameter for * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute * Enable (flag attribute present) - Protection required. * Disable (flag attribute not present) - Protection not required. * If protection is enabled, then the AP will use protection * mechanism using RTS/CTS to self to reserve the airtime. * This is used in * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32) * This attribute is used as the SP offset which is the offset from * TSF after which the wake happens. The units are in microseconds. If * this attribute is not provided, then the value will be set to zero. * This is an optional parameter for * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32) * This is the duration of the service period. This is specified as * multiples of 256 microseconds. Valid values are 0x1 to 0xFF. * This is a required parameter for * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32) * This attribute is used to configure wake interval mantissa. * The units are in TU. * This is a required parameter for * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS: Required (u8) * This field is applicable for TWT response only. * This contains status values in enum qca_wlan_vendor_twt_status * and is passed to the userspace. This is used in TWT SET operation. * This is a required parameter for * 1. TWT SET Response * 2. TWT TERMINATE Response * 3. TWT SUSPEND Response * 4. TWT RESUME Response * 5. TWT NUDGE Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE: Required (u8) * This field is applicable for TWT response only. * This field contains response type from the TWT responder and is * passed to the userspace. The values for this field are defined in * enum qca_wlan_vendor_twt_setup_resp_type. This is used in TWT SET * response. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF: Required (u64) * This field is applicable for TWT response only. * This field contains absolute TSF value of the wake time received * from the TWT responder and is passed to the userspace. * This is a required parameter for * 1. TWT SET Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED: Flag attribute. * Enable (flag attribute present) - Indicates that the TWT responder * supports reception of TWT information frame from the TWT requestor. * Disable (flag attribute not present) - Indicates that the responder * doesn't support reception of TWT information frame from requestor. * This is used in * 1. TWT SET Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer for which the TWT session * is being configured. This is used in AP mode to represent the respective * client. * In AP mode, this is a required parameter in response for * 1. TWT SET * 2. TWT GET * 3. TWT TERMINATE * 4. TWT SUSPEND * In STA mode, this is an optional parameter in request and response for * the above four TWT operations. * In AP mode, this is a required parameter in request for * 1. TWT GET * 2. TWT TERMINATE * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_INTVL: Optional (u32) * Minimum tolerance limit of wake interval parameter in microseconds. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_INTVL: Optional (u32) * Maximum tolerance limit of wake interval parameter in microseconds. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_DURATION: Optional (u32) * Minimum tolerance limit of wake duration parameter in microseconds. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_DURATION: Optional (u32) * Maximum tolerance limit of wake duration parameter in microseconds. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATE: Optional (u32) * TWT state for the given dialog id. The values for this are represented * by enum qca_wlan_twt_setup_state. * This is obtained through TWT GET operation. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL2_MANTISSA: Optional (u32) * This attribute is used to configure wake interval mantissa. * The unit is microseconds. This attribute, when specified, takes * precedence over QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA. * This parameter is used for * 1. TWT SET Request and Response * 2. TWT GET Response * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_ID: Optional (u8) * This attribute is used to configure Broadcast TWT ID. * The Broadcast TWT ID indicates a specific Broadcast TWT for which the * transmitting STA is providing TWT parameters. The allowed values are 0 to 31. * This parameter is used for * 1. TWT SET Request * 2. TWT TERMINATE Request * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_RECOMMENDATION: Optional (u8) * This attribute is used to configure Broadcast TWT recommendation. * The Broadcast TWT Recommendation subfield contains a value that indicates * recommendations on the types of frames that are transmitted by TWT * scheduled STAs and scheduling AP during the broadcast TWT SP. * The allowed values are 0 - 3. * This parameter is used for * 1. TWT SET Request * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE: Optional (u8) * This attribute is used to configure Broadcast TWT Persistence. * The Broadcast TWT Persistence subfield indicates the number of * TBTTs during which the Broadcast TWT SPs corresponding to this * broadcast TWT Parameter set are present. The number of beacon intervals * during which the Broadcast TWT SPs are present is equal to the value in the * Broadcast TWT Persistence subfield plus 1 except that the value 255 * indicates that the Broadcast TWT SPs are present until explicitly terminated. * This parameter is used for * 1. TWT SET Request */ enum qca_wlan_vendor_attr_twt_setup { QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10, /* TWT Response only attributes */ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS = 11, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE = 12, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF = 13, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED = 14, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR = 15, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_INTVL = 16, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_INTVL = 17, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_DURATION = 18, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_DURATION = 19, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATE = 20, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL2_MANTISSA = 21, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_ID = 22, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_RECOMMENDATION = 23, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE = 24, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_twt_status - Represents the status of the requested * TWT operation * * @QCA_WLAN_VENDOR_TWT_STATUS_OK: TWT request successfully completed * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED: TWT not enabled * @QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID: TWT dialog ID is already used * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY: TWT session is busy * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST: TWT session does not exist * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED: TWT session not in suspend state * @QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM: Invalid parameters * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY: FW not ready * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE: FW resource exhausted * @QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK: Peer AP/STA did not ACK the * request/response frame * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE: Peer AP did not send the response * frame * @QCA_WLAN_VENDOR_TWT_STATUS_DENIED: AP did not accept the request * @QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR: Adding TWT dialog failed due to an * unknown reason * @QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED: TWT session already in * suspend state * @QCA_WLAN_VENDOR_TWT_STATUS_IE_INVALID: FW has dropped the frame due to * invalid IE in the received TWT frame * @QCA_WLAN_VENDOR_TWT_STATUS_PARAMS_NOT_IN_RANGE: Parameters received from * the responder are not in the specified range * @QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE: FW terminated the TWT * session due to request from the responder. Used on the TWT_TERMINATE * notification from the firmware. * @QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE: FW terminated the TWT * session due to roaming. Used on the TWT_TERMINATE notification from the * firmware. * @QCA_WLAN_VENDOR_TWT_STATUS_SCC_MCC_CONCURRENCY_TERMINATE: FW terminated the * TWT session due to SCC (Single Channel Concurrency) and MCC (Multi Channel * Concurrency). Used on the TWT_TERMINATE notification from the firmware. * @QCA_WLAN_VENDOR_TWT_STATUS_ROAMING_IN_PROGRESS: FW rejected the TWT setup * request due to roaming in progress. * @QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS: FW rejected the TWT * setup request due to channel switch in progress. * @QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS: FW rejected the TWT setup * request due to scan in progress. */ enum qca_wlan_vendor_twt_status { QCA_WLAN_VENDOR_TWT_STATUS_OK = 0, QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED = 1, QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID = 2, QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY = 3, QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST = 4, QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED = 5, QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM = 6, QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY = 7, QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE = 8, QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK = 9, QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE = 10, QCA_WLAN_VENDOR_TWT_STATUS_DENIED = 11, QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR = 12, QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED = 13, QCA_WLAN_VENDOR_TWT_STATUS_IE_INVALID = 14, QCA_WLAN_VENDOR_TWT_STATUS_PARAMS_NOT_IN_RANGE = 15, QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE = 16, QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE = 17, QCA_WLAN_VENDOR_TWT_STATUS_SCC_MCC_CONCURRENCY_TERMINATE = 18, QCA_WLAN_VENDOR_TWT_STATUS_ROAMING_IN_PROGRESS = 19, QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS = 20, QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS = 21, }; /** * enum qca_wlan_vendor_attr_twt_resume - Represents attributes for * TWT (Target Wake Time) resume request. These attributes are sent as part of * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. * * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8) * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT: Optional (u32) * These attributes are used as the SP offset which is the offset from TSF after * which the wake happens. The units are in microseconds. Please note that * _NEXT_TWT is limited to u8 whereas _NEXT2_TWT takes the u32 data. * _NEXT2_TWT takes the precedence over _NEXT_TWT and thus the recommendation * is to use _NEXT2_TWT. If neither of these attributes is provided, the value * will be set to zero. * * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32) * This attribute represents the next TWT subfield size. * Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits, * and 4 for 64 bits. * * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID: Required (u8). * Flow ID is the unique identifier for each TWT session. This attribute * represents the respective TWT session to resume. * Flow ID values from 0 to 254 represent a single TWT session * Flow ID value of 255 represents all TWT sessions. * If an invalid dialog id is provided, status * QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned. * * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer to which TWT Resume is * being sent. This is used in AP mode to represent the respective * client and is a required parameter. In STA mode, this is an optional * parameter */ enum qca_wlan_vendor_attr_twt_resume { QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1, QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2, QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID = 3, QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT = 4, QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX = QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_twt_nudge - Represents attributes for * TWT (Target Wake Time) nudge request. TWT nudge is a combination of suspend * and resume in a single request. These attributes are sent as part of * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID: Required (u8) * Flow ID is the unique identifier for each TWT session. This attribute * represents the respective TWT session to suspend and resume. * Flow ID values from 0 to 254 represent a single TWT session * Flow ID value of 255 represents all TWT sessions in TWT NUDGE request * and response. * If an invalid dialog id is provided, status * QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME: Required (u32) * This attribute is used as the SP offset which is the offset from * TSF after which the wake happens. The units are in microseconds. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE: Required (u32) * This attribute represents the next TWT subfield size. * Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits, * and 4 for 64 bits. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer to which TWT Suspend and Resume is * being sent. This is used in AP mode to represent the respective * client and is a required parameter. In STA mode, this is an optional * parameter. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF: Optional (u64) * This field contains absolute TSF value of the time at which the TWT * session will be resumed. */ enum qca_wlan_vendor_attr_twt_nudge { QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID = 1, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME = 2, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE = 3, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR = 4, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAX = QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_twt_stats: Represents attributes for * TWT (Target Wake Time) get statistics and clear statistics request. * These attributes are sent as part of * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID: Required (u8) * Flow ID is the unique identifier for each TWT session. This attribute * represents the respective TWT session for get and clear TWT statistics. * Flow ID values from 0 to 254 represent a single TWT session * Flow ID value of 255 represents all TWT sessions in * 1) TWT GET STATISTICS request and response * 2) TWT CLEAR STATISTICS request * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer for which TWT Statistics * is required. * In AP mode this is used to represent the respective * client and is a required parameter for * 1) TWT GET STATISTICS request and response * 2) TWT CLEAR STATISTICS request and response * In STA mode, this is an optional parameter. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION: Required (u32) * This is the duration of the service period in microseconds. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION: Required (u32) * Average of the actual wake duration observed so far. Unit is microseconds. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS: Required (u32) * The number of TWT service periods elapsed so far. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION: Required (u32) * This is the minimum value of the wake duration observed across * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. Unit is * microseconds. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION: Required (u32) * This is the maximum value of wake duration observed across * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. Unit is * microseconds. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU: Required (u32) * Average number of MPDUs transmitted successfully across * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU: Required (u32) * Average number of MPDUs received successfully across * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE: Required (u32) * Average number of bytes transmitted successfully across * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE: Required (u32) * Average number of bytes received successfully across * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. * * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS: Required (u32) * Status of the TWT GET STATISTICS request. * This contains status values in enum qca_wlan_vendor_twt_status * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. */ enum qca_wlan_vendor_attr_twt_stats { QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID = 1, QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR = 2, QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION = 3, QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION = 4, QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS = 5, QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION = 6, QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION = 7, QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU = 8, QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU = 9, QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10, QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11, QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX = QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST - 1, }; /** * qca_wlan_twt_get_capa - Represents the bitmap of TWT capabilities * supported by the device and the peer. * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_GET_CAPABILITIES * * @QCA_WLAN_TWT_CAPA_REQUESTOR: TWT requestor support is advertised by * TWT non-scheduling STA. This capability is advertised in the HE * Capability/Extended Capabilities information element in the * Association Request frame by the device. * * @QCA_WLAN_TWT_CAPA_RESPONDER: TWT responder support is advertised by * the TWT scheduling AP. This capability is advertised in the Extended * Capabilities/HE Capabilities information element. * * @QCA_WLAN_TWT_CAPA_BROADCAST: On the requestor side, this indicates support * for the broadcast TWT functionality. On the responder side, this indicates * support for the role of broadcast TWT scheduling functionality. This * capability is advertised in the HE Capabilities information element. * * @QCA_WLAN_TWT_CAPA_TWT_FLEXIBLE: The device supports flexible TWT schedule. * This capability is advertised in the HE Capabilities information element. * * @QCA_WLAN_TWT_CAPA_REQUIRED: The TWT Required is advertised by AP to indicate * that it mandates the associated HE STAs to support TWT. This capability is * advertised by AP in the HE Operation Parameters field of the HE Operation * information element. */ enum qca_wlan_twt_capa { QCA_WLAN_TWT_CAPA_REQUESTOR = BIT(0), QCA_WLAN_TWT_CAPA_RESPONDER = BIT(1), QCA_WLAN_TWT_CAPA_BROADCAST = BIT(2), QCA_WLAN_TWT_CAPA_FLEXIBLE = BIT(3), QCA_WLAN_TWT_CAPA_REQUIRED = BIT(4), }; /** * enum qca_wlan_vendor_attr_twt_capability - Represents attributes for TWT * get capabilities request type. Used by QCA_WLAN_TWT_GET_CAPABILITIES TWT * operation. * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer for which the TWT capabilities * are being queried. This is used in AP mode to represent the respective * client. In STA mode, this is an optional parameter. * * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF: (u16). * Self TWT capabilities. Carries a bitmap of TWT capabilities specified in * enum qca_wlan_twt_capa. * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16). * Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in * enum qca_wlan_twt_capa. */ enum qca_wlan_vendor_attr_twt_capability { QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX = QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_twt_setup_resp_type - Represents the response type by * the TWT responder * * @QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE: TWT responder suggests TWT * parameters that are different from TWT requesting STA suggested * or demanded TWT parameters * @QCA_WLAN_VENDOR_TWT_RESP_DICTATE: TWT responder demands TWT * parameters that are different from TWT requesting STA TWT suggested * or demanded parameters * @QCA_WLAN_VENDOR_TWT_RESP_REJECT: TWT responder rejects TWT * setup * @QCA_WLAN_VENDOR_TWT_RESP_ACCEPT: TWT responder accepts the TWT * setup. */ enum qca_wlan_vendor_twt_setup_resp_type { QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE = 1, QCA_WLAN_VENDOR_TWT_RESP_DICTATE = 2, QCA_WLAN_VENDOR_TWT_RESP_REJECT = 3, QCA_WLAN_VENDOR_TWT_RESP_ACCEPT = 4, }; /** * enum qca_wlan_vendor_twt_setup_req_type - Required (u8) * Represents the setup type being requested for TWT. * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT * parameters but relying on AP to fill the parameters during the negotiation. * @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested * values which the AP may accept or AP may provide alternative parameters * which the STA may accept. * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any * alternate parameters than the requested ones. */ enum qca_wlan_vendor_twt_setup_req_type { QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1, QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2, QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3, }; /** * enum qca_wlan_roam_scan_event_type - Type of roam scan event * * Indicates the type of roam scan event sent by firmware/driver. * * @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type. * @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type. */ enum qca_wlan_roam_scan_event_type { QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0, QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1, }; /** * enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason * * Indicates the reason for triggering roam scan by firmware/driver. * * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP. * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate. */ enum qca_wlan_roam_scan_trigger_reason { QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0, QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1, }; /** * enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report * roam scan related details from driver/firmware to user space. enum values * are used for NL attributes sent with * %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command. */ enum qca_wlan_vendor_attr_roam_scan { QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0, /* Encapsulates type of roam scan event being reported. enum * qca_wlan_roam_scan_event_type describes the possible range of * values. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1, /* Encapsulates reason for triggering roam scan. enum * qca_wlan_roam_scan_trigger_reason describes the possible range of * values. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX = QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_cfr_data_transport_modes - Defines QCA vendor CFR data * transport modes and is used by the attribute * QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE as a part of the vendor * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG. * @QCA_WLAN_VENDOR_CFR_DATA_RELAY_FS: Use relayfs to send CFR data. * @QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS: Use netlink events to send CFR * data. The data shall be encapsulated within * QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA along with the vendor sub command * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG as an asynchronous event. */ enum qca_wlan_vendor_cfr_data_transport_modes { QCA_WLAN_VENDOR_CFR_DATA_RELAY_FS = 0, QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS = 1, }; /** * enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG. * @QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL: CFR method using QoS Null frame * @QCA_WLAN_VENDOR_CFR_QOS_NULL_WITH_PHASE: CFR method using QoS Null frame * with phase * @QCA_WLAN_VENDOR_CFR_PROBE_RESPONSE: CFR method using Probe Response frame */ enum qca_wlan_vendor_cfr_method { QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0, QCA_WLAN_VENDOR_CFR_QOS_NULL_WITH_PHASE = 1, QCA_WLAN_VENDOR_CFR_PROBE_RESPONSE = 2, }; /** * enum qca_wlan_vendor_cfr_capture_type - QCA vendor CFR capture type used by * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE. * @QCA_WLAN_VENDOR_CFR_DIRECT_FTM: Filter directed FTM ACK frames. * @QCA_WLAN_VENDOR_CFR_ALL_FTM_ACK: Filter all FTM ACK frames. * @QCA_WLAN_VENDOR_CFR_DIRECT_NDPA_NDP: Filter NDPA NDP directed frames. * @QCA_WLAN_VENDOR_CFR_TA_RA: Filter frames based on TA/RA/Subtype which * is provided by one or more of below attributes: * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER * @QCA_WLAN_CFR_ALL_PACKET: Filter all packets. * @QCA_WLAN_VENDOR_CFR_NDPA_NDP_ALL: Filter all NDPA NDP frames. */ enum qca_wlan_vendor_cfr_capture_type { QCA_WLAN_VENDOR_CFR_DIRECT_FTM = 0, QCA_WLAN_VENDOR_CFR_ALL_FTM_ACK = 1, QCA_WLAN_VENDOR_CFR_DIRECT_NDPA_NDP = 2, QCA_WLAN_VENDOR_CFR_TA_RA = 3, QCA_WLAN_VENDOR_CFR_ALL_PACKET = 4, QCA_WLAN_VENDOR_CFR_NDPA_NDP_ALL = 5, }; /** * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer * Channel Frequency Response capture parameters and enable periodic CFR * capture. * * @QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR: Optional (6-byte MAC address) * MAC address of peer. This is for CFR version 1 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE: Required (flag) * Enable peer CFR capture. This attribute is mandatory to enable peer CFR * capture. If this attribute is not present, peer CFR capture is disabled. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH: Optional (u8) * BW of measurement, attribute uses the values in enum nl80211_chan_width * Supported values: 20, 40, 80, 80+80, 160. * Note that all targets may not support all bandwidths. * This attribute is mandatory for version 1 if attribute * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY: Optional (u32) * Periodicity of CFR measurement in milliseconds. * Periodicity should be a multiple of Base timer. * Current Base timer value supported is 10 milliseconds (default). * 0 for one shot capture. * This attribute is mandatory for version 1 if attribute * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD: Optional (u8) * Method used to capture Channel Frequency Response. * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method. * This attribute is mandatory for version 1 if attribute * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. * * @QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE: Optional (flag) * Enable periodic CFR capture. * This attribute is mandatory for version 1 to enable Periodic CFR capture. * If this attribute is not present, periodic CFR capture is disabled. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION: Optional (u8) * Value is 1 or 2 since there are two versions of CFR capture. Two versions * can't be enabled at same time. This attribute is mandatory if target * support both versions and use one of them. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP: Optional (u32) * This attribute is mandatory for version 2 if * QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY is used. * Bits 15:0 bitfield indicates which group is to be enabled. * Bits 31:16 Reserved for future use. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION: Optional (u32) * CFR capture duration in microsecond. This attribute is mandatory for * version 2 if attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL is used. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL: Optional (u32) * CFR capture interval in microsecond. This attribute is mandatory for * version 2 if attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION is used. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE: Optional (u32) * CFR capture type is defined in enum qca_wlan_vendor_cfr_capture_type. * This attribute is mandatory for version 2. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK: Optional (u64) * Bitfield indicating which user in the current UL MU transmissions are * enabled for CFR capture. Bits 36 to 0 indicate user indexes for 37 users in * a UL MU transmission. If bit 0 is set, the CFR capture will happen for user * index 0 in the current UL MU transmission. If bits 0 and 2 are set, CFR * capture for UL MU TX corresponds to user indices 0 and 2. Bits 63:37 are * reserved for future use. This is for CFR version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT: Optional (u32) * Indicates the number of consecutive RX frames to be skipped before CFR * capture is enabled again. This is for CFR version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE: Nested attribute containing * one or more %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY attributes. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY: Nested attribute containing * the following group attributes: * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER, * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER: Optional (u32) * Target supports multiple groups for some configurations. The group number * can be any value between 0 and 15. This is for CFR version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA: Optional (6-byte MAC address) * Transmitter address which is used to filter frames. This MAC address takes * effect with QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK. This is for CFR * version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA: Optional (6-byte MAC address) * Receiver address which is used to filter frames. This MAC address takes * effect with QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK. This is for CFR * version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK: Optional (6-byte MAC address) * Mask of transmitter address which is used to filter frames. This is for CFR * version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK: Optional (6-byte MAC address) * Mask of receiver address which is used to filter frames. This is for CFR * version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS: Optional (u32) * Indicates frames with a specific NSS will be filtered for CFR capture. * This is for CFR version 2 only. This is a bitmask. Bits 7:0 request CFR * capture to be done for frames matching the NSS specified within this bitmask. * Bits 31:8 are reserved for future use. Bits 7:0 map to NSS: * bit 0 : NSS 1 * bit 1 : NSS 2 * ... * bit 7 : NSS 8 * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW: Optional (u32) * Indicates frames with a specific bandwidth will be filtered for CFR capture. * This is for CFR version 2 only. This is a bitmask. Bits 4:0 request CFR * capture to be done for frames matching the bandwidths specified within this * bitmask. Bits 31:5 are reserved for future use. Bits 4:0 map to bandwidth * numerated in enum nl80211_band (although not all bands may be supported * by a given device). * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER: Optional (u32) * Management frames matching the subtype filter categories will be filtered in * by MAC for CFR capture. This is a bitmask in which each bit represents the * corresponding Management frame subtype value per IEEE Std 802.11-2016, * 9.2.4.1.3 Type and Subtype subfields. For example, Beacon frame control type * is 8 and its value is 1 << 8 = 0x100. This is for CFR version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER: Optional (u32) * Control frames matching the subtype filter categories will be filtered in by * MAC for CFR capture. This is a bitmask in which each bit represents the * corresponding Control frame subtype value per IEEE Std 802.11-2016, * 9.2.4.1.3 Type and Subtype subfields. This is for CFR version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER: Optional (u32) * Data frames matching the subtype filter categories will be filtered in by * MAC for CFR capture. This is a bitmask in which each bit represents the * corresponding Data frame subtype value per IEEE Std 802.11-2016, * 9.2.4.1.3 Type and Subtype subfields. This is for CFR version 2 only. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE: Optional (u8) * Userspace can use this attribute to specify the driver about which transport * mode shall be used by the driver to send CFR data to userspace. Uses values * from enum qca_wlan_vendor_cfr_data_transport_modes. When this attribute is * not present, the driver shall use the default transport mechanism which is * QCA_WLAN_VENDOR_CFR_DATA_RELAY_FS. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID: Optional (u32) * Userspace can use this attribute to specify the nl port id of the application * which receives the CFR data and processes it further so that the drivers can * unicast the netlink events to a specific application. Optionally included * when QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE is set to * QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS, not required otherwise. The drivers * shall multicast the netlink events when this attribute is not included. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA: Required (NLA_BINARY). * This attribute will be used by the driver to encapsulate and send CFR data * to userspace along with QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG as an * asynchronous event when the driver is configured to send CFR data using * netlink events with %QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS. */ enum qca_wlan_vendor_peer_cfr_capture_attr { QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0, QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1, QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2, QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3, QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4, QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5, QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6, QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION = 7, QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP = 8, QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION = 9, QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL = 10, QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE = 11, QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK = 12, QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT = 13, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE = 14, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY = 15, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER = 16, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA = 17, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA = 18, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK = 19, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK = 20, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS = 21, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW = 22, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER = 23, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER = 24, QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER = 25, QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE = 26, QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID = 27, QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA = 28, /* Keep last */ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX = QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1, }; /** * enum qca_wlan_throughput_level - Current throughput level * * Indicates the current level of throughput calculated by the driver. The * driver may choose different thresholds to decide whether the throughput level * is low or medium or high based on variety of parameters like physical link * capacity of the current connection, the number of packets being dispatched * per second, etc. The throughput level events might not be consistent with the * actual current throughput value being observed. * * @QCA_WLAN_THROUGHPUT_LEVEL_LOW: Low level of throughput * @QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM: Medium level of throughput * @QCA_WLAN_THROUGHPUT_LEVEL_HIGH: High level of throughput */ enum qca_wlan_throughput_level { QCA_WLAN_THROUGHPUT_LEVEL_LOW = 0, QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM = 1, QCA_WLAN_THROUGHPUT_LEVEL_HIGH = 2, }; /** * enum qca_wlan_vendor_attr_throughput_change - Vendor subcmd attributes to * report throughput changes from the driver to user space. enum values are used * for netlink attributes sent with * %QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT sub command. */ enum qca_wlan_vendor_attr_throughput_change { QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_INVALID = 0, /* Indicates the direction of throughput in which the change is being * reported. u8 attribute. Value is 0 for TX and 1 for RX. */ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION = 1, /* Indicates the newly observed throughput level. enum * qca_wlan_throughput_level describes the possible range of values. * u8 attribute. */ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL = 2, /* Indicates the driver's guidance on the new value to be set to * kernel's TCP parameter tcp_limit_output_bytes. u32 attribute. The * driver may optionally include this attribute. */ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES = 3, /* Indicates the driver's guidance on the new value to be set to * kernel's TCP parameter tcp_adv_win_scale. s8 attribute. Possible * values are from -31 to 31. The driver may optionally include this * attribute. */ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE = 4, /* Indicates the driver's guidance on the new value to be set to * kernel's TCP parameter tcp_delack_seg. u32 attribute. The driver may * optionally include this attribute. */ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX = QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST - 1, }; /** * enum qca_coex_config_profiles - This enum defines different types of * traffic streams that can be prioritized one over the other during coex * scenarios. * The types defined in this enum are categorized in the below manner. * 0 - 31 values corresponds to WLAN * 32 - 63 values corresponds to BT * 64 - 95 values corresponds to Zigbee * @QCA_WIFI_STA_DISCOVERY: Prioritize discovery frames for WLAN STA * @QCA_WIFI_STA_CONNECTION: Prioritize connection frames for WLAN STA * @QCA_WIFI_STA_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN STA * @QCA_WIFI_STA_DATA : Prioritize data frames for WLAN STA * @QCA_WIFI_STA_ALL: Priritize all frames for WLAN STA * @QCA_WIFI_SAP_DISCOVERY: Prioritize discovery frames for WLAN SAP * @QCA_WIFI_SAP_CONNECTION: Prioritize connection frames for WLAN SAP * @QCA_WIFI_SAP_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN SAP * @QCA_WIFI_SAP_DATA: Prioritize data frames for WLAN SAP * @QCA_WIFI_SAP_ALL: Prioritize all frames for WLAN SAP * @QCA_BT_A2DP: Prioritize BT A2DP * @QCA_BT_BLE: Prioritize BT BLE * @QCA_BT_SCO: Prioritize BT SCO * @QCA_ZB_LOW: Prioritize Zigbee Low * @QCA_ZB_HIGH: Prioritize Zigbee High */ enum qca_coex_config_profiles { /* 0 - 31 corresponds to WLAN */ QCA_WIFI_STA_DISCOVERY = 0, QCA_WIFI_STA_CONNECTION = 1, QCA_WIFI_STA_CLASS_3_MGMT = 2, QCA_WIFI_STA_DATA = 3, QCA_WIFI_STA_ALL = 4, QCA_WIFI_SAP_DISCOVERY = 5, QCA_WIFI_SAP_CONNECTION = 6, QCA_WIFI_SAP_CLASS_3_MGMT = 7, QCA_WIFI_SAP_DATA = 8, QCA_WIFI_SAP_ALL = 9, QCA_WIFI_CASE_MAX = 31, /* 32 - 63 corresponds to BT */ QCA_BT_A2DP = 32, QCA_BT_BLE = 33, QCA_BT_SCO = 34, QCA_BT_CASE_MAX = 63, /* 64 - 95 corresponds to Zigbee */ QCA_ZB_LOW = 64, QCA_ZB_HIGH = 65, QCA_ZB_CASE_MAX = 95, /* 0xff is default value if the u8 profile value is not set. */ QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255 }; /** * enum qca_vendor_attr_coex_config_types - Coex configurations types. * This enum defines the valid set of values of coex configuration types. These * values may used by attribute * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE. * * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the * weights to default values. * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config * weights with configurability value. */ enum qca_vendor_attr_coex_config_types { QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1, QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2, }; /** * enum qca_vendor_attr_coex_config - Specifies vendor coex config attributes * * @QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES: This attribute contains variable * length array of 8-bit values from enum qca_coex_config_profiles. * FW will prioritize the profiles in the order given in the array encapsulated * in this attribute. * For example: * ----------------------------------------------------------------------- * | 1 | 34 | 32 | 65 | * ----------------------------------------------------------------------- * If the attribute contains the values defined in above array then it means * 1) Wifi STA connection has priority over BT_SCO, BT_A2DP and ZIGBEE HIGH. * 2) BT_SCO has priority over BT_A2DP. * 3) BT_A2DP has priority over ZIGBEE HIGH. * Profiles which are not listed in this array shall not be preferred over the * profiles which are listed in the array as a part of this attribute. */ enum qca_vendor_attr_coex_config { QCA_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES = 1, /* Keep last */ QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST, QCA_VENDOR_ATTR_COEX_CONFIG_MAX = QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1, }; /** * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config * attributes * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG * * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute. * Indicate config type. * The config types are 32-bit values from qca_vendor_attr_coex_config_types * * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute. * Indicate the Priority 1 profiles. * The profiles are 8-bit values from enum qca_coex_config_profiles. * In same priority level, maximum to 4 profiles can be set here. * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute. * Indicate the Priority 2 profiles. * The profiles are 8-bit values from enum qca_coex_config_profiles. * In same priority level, maximum to 4 profiles can be set here. * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute. * Indicate the Priority 3 profiles. * The profiles are 8-bit values from enum qca_coex_config_profiles. * In same priority level, maximum to 4 profiles can be set here. * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute. * Indicate the Priority 4 profiles. * The profiles are 8-bit values from enum qca_coex_config_profiles. * In same priority level, maximum to 4 profiles can be set here. * NOTE: * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority * arrangement: * 1: In the same u32 attribute (priority x), the profiles enum values own * same priority level. * 2: 0xff is default value if the u8 profile value is not set. * 3: max to 4 rules/profiles in same priority level. * 4: max to 4 priority level (priority 1 - priority 4) * 5: one priority level only supports one scenario from WLAN/BT/ZB, * hybrid rules not support. * 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will * remain blank to reset all parameters. * For example: * * If the attributes as follow: * priority 1: * ------------------------------------ * | 0xff | 0 | 1 | 2 | * ------------------------------------ * priority 2: * ------------------------------------- * | 0xff | 0xff | 0xff | 32 | * ------------------------------------- * priority 3: * ------------------------------------- * | 0xff | 0xff | 0xff | 65 | * ------------------------------------- * then it means: * 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION * owns same priority level. * 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION * has priority over BT_A2DP and ZB_HIGH. * 3: BT_A2DP has priority over ZB_HIGH. */ enum qca_vendor_attr_coex_config_three_way { QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0, QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1, QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2, QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3, QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4, QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5, /* Keep last */ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST, QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX = QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_link_properties - Represent the link properties. * * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer * (STA/AP) for the connected link. * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS: Attribute containing a * &struct nl80211_sta_flag_update for the respective connected link. MAC * address of the peer represented by * QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR. */ enum qca_wlan_vendor_attr_link_properties { QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_INVALID = 0, /* 1 - 3 are reserved */ QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR = 4, QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS = 5, /* Keep last */ QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST, QCA_VENDOR_ATTR_LINK_PROPERTIES_MAX = QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1, }; /** * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type * This enum defines the valid set of values of peer stats cache types. These * values are used by attribute * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE. * * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn * statistics */ enum qca_vendor_attr_peer_stats_cache_type { QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0, QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS, QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS, QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS, }; /** * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH * Information in these attributes is used to flush peer rate statistics from * the driver to user application. * * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute * Indicate peer statistics cache type. * The statistics types are 32-bit values from * enum qca_vendor_attr_peer_stats_cache_type. * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array * of size 6 octets, representing the peer MAC address. * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute * containing buffer of statistics to send to application layer entity. * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute * representing a cookie for peer unique session. */ enum qca_wlan_vendor_attr_peer_stats_cache_params { QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0, QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1, QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2, QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3, QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4, /* Keep last */ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST, QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX = QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1 }; /** * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state * This enum defines all the possible states of Zigbee, which can be * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute. * * @ZIGBEE_IDLE: Zigbee in idle state * @ZIGBEE_FORM_NETWORK: Zigbee forming network * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network * @ZIGBEE_JOIN: Zigbee joining network * @ZIGBEE_NETWORK_UP: Zigbee network is up * @ZIGBEE_HMI: Zigbee in HMI mode */ enum qca_mpta_helper_attr_zigbee_state { ZIGBEE_IDLE = 0, ZIGBEE_FORM_NETWORK = 1, ZIGBEE_WAIT_JOIN = 2, ZIGBEE_JOIN = 3, ZIGBEE_NETWORK_UP = 4, ZIGBEE_HMI = 5, }; /* * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG. */ enum qca_mpta_helper_vendor_attr { QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0, /* Optional attribute used to update Zigbee state. * enum qca_mpta_helper_attr_zigbee_state. * NLA_U32 attribute. */ QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1, /* Optional attribute used to configure WLAN duration for Shape-OCS * during interrupt. * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION. * Value range 0 ~ 300 (ms). * NLA_U32 attribute. */ QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2, /* Optional attribute used to configure non-WLAN duration for Shape-OCS * during interrupt. * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION. * Value range 0 ~ 300 (ms). * NLA_U32 attribute. */ QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3, /* Optional attribute used to configure WLAN duration for Shape-OCS * monitor period. * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION. * Value range 0 ~ 300 (ms) * NLA_U32 attribute */ QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4, /* Optional attribute used to configure non-WLAN duration for Shape-OCS * monitor period. * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION. * Value range 0 ~ 300 (ms) * NLA_U32 attribute */ QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5, /* Optional attribute used to configure OCS interrupt duration. * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION. * Value range 1000 ~ 20000 (ms) * NLA_U32 attribute */ QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6, /* Optional attribute used to configure OCS monitor duration. * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION. * Value range 1000 ~ 20000 (ms) * NLA_U32 attribute */ QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7, /* Optional attribute used to notify WLAN firmware the current Zigbee * channel. * Value range 11 ~ 26 * NLA_U32 attribute */ QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8, /* Optional attribute used to configure WLAN mute duration. * Value range 0 ~ 400 (ms) * NLA_U32 attribute */ QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9, /* keep last */ QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST, QCA_MPTA_HELPER_VENDOR_ATTR_MAX = QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_beacon_reporting_op_types - Defines different types of * operations for which %QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING can be used. * Will be used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE. * * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START: Sent by userspace to the driver * to request the driver to start reporting Beacon frames. * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP: Sent by userspace to the driver to * request the driver to stop reporting Beacon frames. * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO: Sent by the driver to * userspace to report received Beacon frames. * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE: Sent by the driver to userspace * to indicate that the driver is going to pause reporting Beacon frames. */ enum qca_wlan_vendor_beacon_reporting_op_types { QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START = 0, QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP = 1, QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO = 2, QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE = 3, }; /** * enum qca_wlan_vendor_beacon_reporting_pause_reasons - Defines different types * of reasons for which the driver is pausing reporting Beacon frames. Will be * used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON. * * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED: For unspecified * reasons. * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED: When the * driver/firmware is starting a scan. * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED: When the * driver/firmware disconnects from the ESS and indicates the disconnection to * userspace (non-seamless roaming case). This reason code will be used by the * driver/firmware to indicate stopping of beacon report events. Userspace will * need to start beacon reporting again (if desired) by sending vendor command * QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING with * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START after the next connection is * completed. */ enum qca_wlan_vendor_beacon_reporting_pause_reasons { QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED = 0, QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED = 1, QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED = 2, }; /* * enum qca_wlan_vendor_attr_beacon_reporting_params - List of attributes used * in vendor sub-command QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING. */ enum qca_wlan_vendor_attr_beacon_reporting_params { QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_INVALID = 0, /* Specifies the type of operation that the vendor command/event is * intended for. Possible values for this attribute are defined in * enum qca_wlan_vendor_beacon_reporting_op_types. u32 attribute. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE = 1, /* Optionally set by userspace to request the driver to report Beacon * frames using asynchronous vendor events when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute. * If this flag is not set, the driver will only update Beacon frames in * cfg80211 scan cache but not send any vendor events. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_ACTIVE_REPORTING = 2, /* Optionally used by userspace to request the driver/firmware to report * Beacon frames periodically when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. * u32 attribute, indicates the period of Beacon frames to be reported * and in the units of beacon interval. * If this attribute is missing in the command, then the default value * of 1 will be assumed by driver, i.e., to report every Beacon frame. * Zero is an invalid value. * If a valid value is received for this attribute, the driver will * update the cfg80211 scan cache periodically as per the value received * in this attribute in addition to updating the cfg80211 scan cache * when there is significant change in Beacon frame IEs. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD = 3, /* Used by the driver to encapsulate the SSID when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. * u8 array with a maximum size of 32. * * When generating beacon report from non-MBSSID Beacon frame, the SSID * will be taken from the SSID element of the received Beacon frame. * * When generating beacon report from Multiple BSSID Beacon frame and if * the BSSID of the current connected BSS matches the BSSID of the * transmitting BSS, the SSID will be taken from the SSID element of the * received Beacon frame. * * When generating beacon report from Multiple BSSID Beacon frame and if * the BSSID of the current connected BSS matches the BSSID of one of * the* nontransmitting BSSs, the SSID will be taken from the SSID field * included in the nontransmitted BSS profile whose derived BSSID is * same as the BSSID of the current connected BSS. When there is no * nontransmitted BSS profile whose derived BSSID is same as the BSSID * of current connected* BSS, this attribute will not be present. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_SSID = 4, /* Used by the driver to encapsulate the BSSID of the AP to which STA is * currently connected to when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array with a * fixed size of 6 bytes. * * When generating beacon report from a Multiple BSSID beacon and the * current connected BSSID matches one of the nontransmitted BSSIDs in a * Multiple BSSID set, this BSSID will be that particular nontransmitted * BSSID and not the transmitted BSSID (i.e., the transmitting address * of the Beacon frame). */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BSSID = 5, /* Used by the driver to encapsulate the frequency in MHz on which * the Beacon frame was received when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is * set to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. * u32 attribute. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_FREQ = 6, /* Used by the driver to encapsulate the Beacon interval * when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. * u16 attribute. The value will be copied from the Beacon frame and the * units are TUs. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BI = 7, /* Used by the driver to encapsulate the Timestamp field from the Beacon * frame when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set * to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. * u64 attribute. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_TSF = 8, /* Used by the driver to encapsulate the CLOCK_BOOTTIME when this * Beacon frame is received in the driver when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u64 attribute, in * the units of nanoseconds. This value is expected to have accuracy of * about 10 ms. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BOOTTIME_WHEN_RECEIVED = 9, /* Used by the driver to encapsulate the IEs of the Beacon frame from * which this event is generated when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_IES = 10, /* Used by the driver to specify the reason for the driver/firmware to * pause sending beacons to userspace when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. Possible values are * defined in enum qca_wlan_vendor_beacon_reporting_pause_reasons, u32 * attribute. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON = 11, /* Used by the driver to specify whether the driver will automatically * resume reporting beacon events to userspace later (for example after * the ongoing off-channel activity is completed etc.) when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. NLA_FLAG attribute. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES = 12, /* Optionally set by userspace to request the driver not to resume * beacon reporting after a pause is completed, when the * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute. * If this flag is set, the driver will not resume beacon reporting * after any pause in beacon reporting is completed. Userspace has to * send QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command again in order * to initiate beacon reporting again. If this flag is set in the recent * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command, then in the * subsequent QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE event (if any) * the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES shall not be * set by the driver. Setting this flag until and unless there is a * specific need is not recommended as there is a chance of some beacons * received after pause command and next start command being not * reported. */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME = 13, /* Keep last */ QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST, QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX = QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST - 1 }; /** * enum qca_vendor_interop_issues_ap_type - Interop issue types * This enum defines the valid set of values of interop issue types. These * values are used by attribute %QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE. * * @QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS: The AP has power save interop issue * when the STA's Qpower feature is enabled. */ enum qca_vendor_interop_issues_ap_type { QCA_VENDOR_INTEROP_ISSUES_AP_INVALID = 0, QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS = 1, }; /** * enum qca_vendor_attr_interop_issues_ap - attribute for AP with interop issues * Values are used by %QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP. * * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID: Invalid value * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE: Interop issue type * 32-bit unsigned value. The values defined in enum * qca_vendor_interop_issues_ap_type are used. * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST: APs' BSSID container * array of nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID attributes. * It is present and mandatory for the command but is not used for the event * since only a single BSSID is reported in an event. * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID: AP's BSSID 6-byte MAC address. * It is used within the nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST * attribute in command case and without such encapsulation in the event case. * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST: last value * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX: max value */ enum qca_vendor_attr_interop_issues_ap { QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID, /* keep last */ QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX = QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST - 1 }; /** * enum qca_vendor_oem_device_type - Represents the target device in firmware. * It is used by QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO. * * @QCA_VENDOR_OEM_DEVICE_VIRTUAL: The command is intended for * a virtual device. * * @QCA_VENDOR_OEM_DEVICE_PHYSICAL: The command is intended for * a physical device. */ enum qca_vendor_oem_device_type { QCA_VENDOR_OEM_DEVICE_VIRTUAL = 0, QCA_VENDOR_OEM_DEVICE_PHYSICAL = 1, }; /** * enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command/event * QCA_NL80211_VENDOR_SUBCMD_OEM_DATA. * * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: This NLA_BINARY attribute is * used to set/query the data to/from the firmware. On query, the same * attribute is used to carry the respective data in the reply sent by the * driver to userspace. The request to set/query the data and the format of the * respective data from the firmware are embedded in the attribute. The * maximum size of the attribute payload is 1024 bytes. * Userspace has to set the QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED * attribute when the data is queried from the firmware. * * @QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO: The binary blob will be routed * based on this field. This optional attribute is included to specify whether * the device type is a virtual device or a physical device for the * command/event. This attribute can be omitted for a virtual device (default) * command/event. * This u8 attribute is used to carry information for the device type using * values defined by enum qca_vendor_oem_device_type. * * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED: This NLA_FLAG attribute * is set when the userspace queries data from the firmware. This attribute * should not be set when userspace sets the OEM data to the firmware. */ enum qca_wlan_vendor_attr_oem_data_params { QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1, QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO = 2, QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX = QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST - 1 }; /** * enum qca_wlan_vendor_attr_avoid_frequency_ext - Defines attributes to be * used with vendor command/event QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT. * * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE: Required * Nested attribute containing multiple ranges with following attributes: * QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START and * QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END. * * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START: Required (u32) * Starting center frequency in MHz. * * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END: Required (u32) * Ending center frequency in MHz. */ enum qca_wlan_vendor_attr_avoid_frequency_ext { QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_INVALID = 0, QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE = 1, QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START = 2, QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END = 3, QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX = QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST - 1 }; /* * enum qca_wlan_vendor_attr_add_sta_node_params - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE. */ enum qca_wlan_vendor_attr_add_sta_node_params { QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_INVALID = 0, /* 6 byte MAC address of STA */ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR = 1, /* Authentication algorithm used by the station of size u16; * defined in enum nl80211_auth_type. */ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_MAX = QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST - 1 }; /** * enum qca_btc_chain_mode - Specifies BT coex chain mode. * This enum defines the valid set of values of BT coex chain mode. * These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE. * * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4G are shared. * @QCA_BTC_CHAIN_SEPARATED: chains of BT and WLAN 2.4G are separated. */ enum qca_btc_chain_mode { QCA_BTC_CHAIN_SHARED = 0, QCA_BTC_CHAIN_SEPARATED = 1, }; /** * enum qca_vendor_attr_btc_chain_mode - Specifies attributes for BT coex * chain mode. * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE. * * @QCA_VENDOR_ATTR_COEX_BTC_CHAIN_MODE: u32 attribute. * Indicates the BT coex chain mode, are 32-bit values from * enum qca_btc_chain_mode. This attribute is mandatory. * * @QCA_VENDOR_ATTR_COEX_BTC_CHAIN_MODE_RESTART: flag attribute. * If set, vdev should be restarted when BT coex chain mode is updated. * This attribute is optional. */ enum qca_vendor_attr_btc_chain_mode { QCA_VENDOR_ATTR_BTC_CHAIN_MODE_INVALID = 0, QCA_VENDOR_ATTR_BTC_CHAIN_MODE = 1, QCA_VENDOR_ATTR_BTC_CHAIN_MODE_RESTART = 2, /* Keep last */ QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST, QCA_VENDOR_ATTR_BTC_CHAIN_MODE_MAX = QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST - 1, }; /** * enum qca_vendor_wlan_sta_flags - Station feature flags * Bits will be set to 1 if the corresponding features are enabled. * @QCA_VENDOR_WLAN_STA_FLAG_AMPDU: AMPDU is enabled for the station * @QCA_VENDOR_WLAN_STA_FLAG_TX_STBC: TX Space-time block coding is enabled for the station * @QCA_VENDOR_WLAN_STA_FLAG_RX_STBC: RX Space-time block coding is enabled for the station */ enum qca_vendor_wlan_sta_flags { QCA_VENDOR_WLAN_STA_FLAG_AMPDU = BIT(0), QCA_VENDOR_WLAN_STA_FLAG_TX_STBC = BIT(1), QCA_VENDOR_WLAN_STA_FLAG_RX_STBC = BIT(2), }; /** * enum qca_vendor_wlan_sta_guard_interval - Station guard interval * @QCA_VENDOR_WLAN_STA_GI_800_NS: Legacy normal guard interval * @QCA_VENDOR_WLAN_STA_GI_400_NS: Legacy short guard interval * @QCA_VENDOR_WLAN_STA_GI_1600_NS: Guard interval used by HE * @QCA_VENDOR_WLAN_STA_GI_3200_NS: Guard interval used by HE */ enum qca_vendor_wlan_sta_guard_interval { QCA_VENDOR_WLAN_STA_GI_800_NS = 0, QCA_VENDOR_WLAN_STA_GI_400_NS = 1, QCA_VENDOR_WLAN_STA_GI_1600_NS = 2, QCA_VENDOR_WLAN_STA_GI_3200_NS = 3, }; /** * enum qca_wlan_vendor_attr_get_sta_info - Defines attributes * used by QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC: * Required attribute in request for AP mode only, 6-byte MAC address, * corresponding to the station's MAC address for which information is * requested. For STA mode this is not required as the info always correspond * to the self STA and the current/last association. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS: * Optionally used in response, u32 attribute, contains a bitmap of different * fields defined in enum qca_vendor_wlan_sta_flags, used in AP mode only. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL: * Optionally used in response, u32 attribute, possible values are defined in * enum qca_vendor_wlan_sta_guard_interval, used in AP mode only. * Guard interval used by the station. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT: * Optionally used in response, u32 attribute, used in AP mode only. * Value indicates the number of data frames received from station with retry * bit set to 1 in FC. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT: * Optionally used in response, u32 attribute, used in AP mode only. * Counter for number of data frames with broadcast or multicast address in * the destination address received from the station. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED: * Optionally used in response, u32 attribute, used in both STA and AP modes. * Value indicates the number of data frames successfully transmitted only * after retrying the packets and for which the TX status has been updated * back to host from target. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED: * Optionally used in response, u32 attribute, used in both STA and AP mode. * Value indicates the number of data frames not transmitted successfully even * after retrying the packets for the number of times equal to the total number * of retries allowed for that packet and for which the TX status has been * updated back to host from target. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL: * Optionally used in response, u32 attribute, used in AP mode only. * Counter in the target for the number of data frames successfully transmitted * to the station. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY: * Optionally used in response, u32 attribute, used in AP mode only. * Value indicates the number of data frames successfully transmitted only * after retrying the packets. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED: * Optionally used in response, u32 attribute, used in both STA & AP mode. * Value indicates the number of data frames not transmitted successfully even * after retrying the packets for the number of times equal to the total number * of retries allowed for that packet. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_PROBE_REQ_BMISS_COUNT: u32, used in * the STA mode only. Represent the number of probe requests sent by the STA * while attempting to roam on missing certain number of beacons from the * connected AP. If queried in the disconnected state, this represents the * count for the last connected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_PROBE_RESP_BMISS_COUNT: u32, used in * the STA mode. Represent the number of probe responses received by the station * while attempting to roam on missing certain number of beacons from the * connected AP. When queried in the disconnected state, this represents the * count when in last connected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_ALL_COUNT: u32, used in the * STA mode only. Represents the total number of frames sent out by STA * including Data, ACK, RTS, CTS, Control Management. This data is maintained * only for the connect session. Represents the count of last connected session, * when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_COUNT: u32, used in the STA mode. * Total number of RTS sent out by the STA. This data is maintained per connect * session. Represents the count of last connected session, when queried in the * disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_RETRY_FAIL_COUNT: u32, used in the * STA mode.Represent the number of RTS transmission failure that reach retry * limit. This data is maintained per connect session. Represents the count of * last connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_NON_AGGREGATED_COUNT: u32, used in * the STA mode. Represent the total number of non aggregated frames transmitted * by the STA. This data is maintained per connect session. Represents the count * of last connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_AGGREGATED_COUNT: u32, used in the * STA mode. Represent the total number of aggregated frames transmitted by the * STA. This data is maintained per connect session. Represents the count of * last connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_GOOD_PLCP_COUNT: u32, used in * the STA mode. Represents the number of received frames with a good PLCP. This * data is maintained per connect session. Represents the count of last * connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_INVALID_DELIMITER_COUNT: u32, * used in the STA mode. Represents the number of occasions that no valid * delimiter is detected by A-MPDU parser. This data is maintained per connect * session. Represents the count of last connected session, when queried in the * disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_CRC_FAIL_COUNT: u32, used in the * STA mode. Represents the number of frames for which CRC check failed in the * MAC. This data is maintained per connect session. Represents the count of * last connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_ACKS_GOOD_FCS_COUNT: u32, used in the * STA mode. Represents the number of unicast ACKs received with good FCS. This * data is maintained per connect session. Represents the count of last * connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BLOCKACK_COUNT: u32, used in the STA * mode. Represents the number of received Block Acks. This data is maintained * per connect session. Represents the count of last connected session, when * queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BEACON_COUNT: u32, used in the STA * mode. Represents the number of beacons received from the connected BSS. This * data is maintained per connect session. Represents the count of last * connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_OTHER_BEACON_COUNT: u32, used in the * STA mode. Represents the number of beacons received by the other BSS when in * connected state (through the probes done by the STA). This data is maintained * per connect session. Represents the count of last connected session, when * queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_UCAST_DATA_GOOD_FCS_COUNT: u64, used in * the STA mode. Represents the number of received DATA frames with good FCS and * matching Receiver Address when in connected state. This data is maintained * per connect session. Represents the count of last connected session, when * queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_DATA_BC_MC_DROP_COUNT: u32, used in the * STA mode. Represents the number of RX Data multicast frames dropped by the HW * when in the connected state. This data is maintained per connect session. * Represents the count of last connected session, when queried in the * disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_1MBPS: u32, used in the * STA mode. This represents the target power in dBm for the transmissions done * to the AP in 2.4 GHz at 1 Mbps (DSSS) rate. This data is maintained per * connect session. Represents the count of last connected session, when * queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_6MBPS: u32, used in the * STA mode. This represents the Target power in dBm for transmissions done to * the AP in 2.4 GHz at 6 Mbps (OFDM) rate. This data is maintained per connect * session. Represents the count of last connected session, when queried in the * disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_MCS0: u32, used in the * STA mode. This represents the Target power in dBm for transmissions done to * the AP in 2.4 GHz at MCS0 rate. This data is maintained per connect session. * Represents the count of last connected session, when queried in the * disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_6MBPS: u32, used in the * STA mode. This represents the Target power in dBm for transmissions done to * the AP in 5 GHz at 6 Mbps (OFDM) rate. This data is maintained per connect * session. Represents the count of last connected session, when queried in * the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_MCS0: u32, used in the * STA mode. This represents the Target power in dBm for for transmissions done * to the AP in 5 GHz at MCS0 rate. This data is maintained per connect session. * Represents the count of last connected session, when queried in the * disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_HW_BUFFERS_OVERFLOW_COUNT: u32, used * in the STA mode. This represents the Nested attribute representing the * overflow counts of each receive buffer allocated to the hardware during the * STA's connection. The number of hw buffers might vary for each WLAN * solution and hence this attribute represents the nested array of all such * HW buffer count. This data is maintained per connect session. Represents * the count of last connected session, when queried in the disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX_TX_POWER: u32, Max TX power (dBm) * allowed as per the regulatory requirements for the current or last connected * session. Used in the STA mode. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_POWER: u32, Latest TX power * (dBm) used by the station in its latest unicast frame while communicating * to the AP in the connected state. When queried in the disconnected state, * this represents the TX power used by the STA with last AP communication * when in connected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ANI_LEVEL: u32, Adaptive noise immunity * level used to adjust the RX sensitivity. Represents the current ANI level * when queried in the connected state. When queried in the disconnected * state, this corresponds to the latest ANI level at the instance of * disconnection. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES: Binary attribute containing * the raw information elements from Beacon frames. Represents the Beacon frames * of the current BSS in the connected state. When queried in the disconnected * state, these IEs correspond to the last connected BSSID. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES: Binary attribute * containing the raw information elements from Probe Response frames. * Represents the Probe Response frames of the current BSS in the connected * state. When queried in the disconnected state, these IEs correspond to the * last connected BSSID. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON: u32, Driver * disconnect reason for the last disconnection if the disconnection is * triggered from the host driver. The values are referred from * enum qca_disconnect_reason_codes. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT: u32, used in STA mode * only. This represents the number of group addressed robust management frames * received from this station with an invalid MIC or a missing MME when PMF is * enabled. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT: u32, used in STA mode * only. This represents the number of group addressed robust management frames * received from this station with the packet number less than or equal to the * last received packet number when PMF is enabled. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT: u32, used in STA * mode only. This represents the number of Beacon frames received from this * station with an invalid MIC or a missing MME when beacon protection is * enabled. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT: u32, used in STA mode * only. This represents number of Beacon frames received from this station with * the packet number less than or equal to the last received packet number when * beacon protection is enabled. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE: u32, used in * STA mode only. The driver uses this attribute to populate the connection * failure reason codes and the values are defined in * enum qca_sta_connect_fail_reason_codes. Userspace applications can send * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command after receiving * a connection failure indication from the driver. The driver shall not * include this attribute in response to the * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO command if there is no connection * failure observed in the last attempted connection. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_RATE: u32, latest TX rate (Kbps) * used by the station in its last TX frame while communicating to the AP in the * connected state. When queried in the disconnected state, this represents the * rate used by the STA in the last TX frame to the AP when it was connected. * This attribute is used for STA mode only. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_RIX: u32, used in STA mode only. * This represents the rate index used by the STA for the last TX frame to the * AP. When queried in the disconnected state, this gives the last RIX used by * the STA in the last TX frame to the AP when it was connected. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TSF_OUT_OF_SYNC_COUNT: u32, used in STA * mode only. This represents the number of times the STA TSF goes out of sync * from the AP after the connection. If queried in the disconnected state, this * gives the count of TSF out of sync for the last connection. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_TRIGGER_REASON: u32, used in STA * mode only. This represents the roam trigger reason for the last roaming * attempted by the firmware. This can be queried either in connected state or * disconnected state. Each bit of this attribute represents the different * roam trigger reason code which are defined in enum qca_vendor_roam_triggers. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON: u32, used in STA mode * only. This represents the roam fail reason for the last failed roaming * attempt by the firmware. Different roam failure reason codes are specified * in enum qca_vendor_roam_fail_reasons. This can be queried either in * connected state or disconnected state. * * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON: u32, used in * STA mode only. This represents the roam invoke fail reason for the last * failed roam invoke. Different roam invoke failure reason codes * are specified in enum qca_vendor_roam_invoke_fail_reasons. This can be * queried either in connected state or disconnected state. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY: u32, used in STA mode only. + * This represents the average congestion duration of uplink frames in MAC + * queue in unit of ms. This can be queried either in connected state or + * disconnected state. */ enum qca_wlan_vendor_attr_get_sta_info { QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC = 1, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS = 2, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL = 3, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT = 4, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT = 5, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED = 6, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED = 7, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL = 8, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY = 9, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED = 10, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_PROBE_REQ_BMISS_COUNT = 11, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_PROBE_RESP_BMISS_COUNT = 12, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_ALL_COUNT = 13, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_COUNT = 14, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_RETRY_FAIL_COUNT = 15, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_NON_AGGREGATED_COUNT = 16, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_AGGREGATED_COUNT = 17, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_GOOD_PLCP_COUNT = 18, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_INVALID_DELIMITER_COUNT = 19, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_CRC_FAIL_COUNT = 20, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_ACKS_GOOD_FCS_COUNT = 21, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BLOCKACK_COUNT = 22, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BEACON_COUNT = 23, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_OTHER_BEACON_COUNT = 24, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_UCAST_DATA_GOOD_FCS_COUNT = 25, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_DATA_BC_MC_DROP_COUNT = 26, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_1MBPS = 27, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_6MBPS = 28, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_MCS0 = 29, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_6MBPS = 30, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_MCS0 = 31, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_HW_BUFFERS_OVERFLOW_COUNT = 32, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX_TX_POWER = 33, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_POWER = 34, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ANI_LEVEL = 35, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES = 36, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES = 37, QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON = 38, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT = 39, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT = 40, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT = 41, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT = 42, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE = 43, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_RATE = 44, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_RIX = 45, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TSF_OUT_OF_SYNC_COUNT = 46, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_TRIGGER_REASON = 47, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON = 48, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON = 49, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY = 50, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX = QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_update_sta_info - Defines attributes * used by QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO vendor command. * * @QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_CONNECT_CHANNELS: Type is NLA_UNSPEC. * Used in STA mode. This attribute represents the list of channel center * frequencies in MHz (u32) the station has learnt during the last connection * or roaming attempt. This information shall not signify the channels for * an explicit scan request from the user space. Host drivers can update this * information to the user space in both connected and disconnected state. * In the disconnected state this information shall signify the channels * scanned in the last connection/roam attempt that lead to the disconnection. */ enum qca_wlan_vendor_attr_update_sta_info { QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_INVALID = 0, QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_CONNECT_CHANNELS = 1, /* keep last */ QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_MAX = QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_AFTER_LAST - 1, }; /** * enum qca_disconnect_reason_codes - Specifies driver disconnect reason codes. * Used when the driver triggers the STA to disconnect from the AP. * * @QCA_DISCONNECT_REASON_UNSPECIFIED: The host driver triggered the * disconnection with the AP due to unspecified reasons. * * @QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE: The host driver triggered the * disconnection with the AP due to a roaming failure. This roaming is triggered * internally (host driver/firmware). * * @QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE: The driver disconnected from * the AP when the user/external triggered roaming fails. * * @QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE: This reason code is used * by the host driver whenever gateway reachability failure is detected and the * driver disconnects with AP. * * @QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA: The driver disconnected from * the AP on a channel switch announcement from it with an unsupported channel. * * @QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR: On a concurrent AP start * with indoor channels disabled and if the STA is connected on one of these * disabled channels, the host driver disconnected the STA with this reason * code. * * @QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED: Disconnection due to an * explicit request from the user to disable the current operating channel. * * @QCA_DISCONNECT_REASON_DEVICE_RECOVERY: STA disconnected from the AP due to * the internal host driver/firmware recovery. * * @QCA_DISCONNECT_REASON_KEY_TIMEOUT: The driver triggered the disconnection on * a timeout for the key installations from the user space. * * @QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE: The dDriver disconnected the * STA on a band change request from the user space to a different band from the * current operation channel/band. * * @QCA_DISCONNECT_REASON_IFACE_DOWN: The STA disconnected from the AP on an * interface down trigger from the user space. * * @QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL: The host driver disconnected the * STA on getting continuous transmission failures for multiple Data frames. * * @QCA_DISCONNECT_REASON_PEER_INACTIVITY: The STA does a keep alive * notification to the AP by transmitting NULL/G-ARP frames. This disconnection * represents inactivity from AP on such transmissions. * @QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT: This reason code is used on * disconnection when SA Query times out (AP does not respond to SA Query). * * @QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE: The host driver disconnected the * STA on missing the beacons continuously from the AP. * * @QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE: Disconnection due to STA not * able to move to the channel mentioned by the AP in CSA. * * @QCA_DISCONNECT_REASON_USER_TRIGGERED: User triggered disconnection. */ enum qca_disconnect_reason_codes { QCA_DISCONNECT_REASON_UNSPECIFIED = 0, QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE = 1, QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE = 2, QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE = 3, QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA = 4, QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR = 5, QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED = 6, QCA_DISCONNECT_REASON_DEVICE_RECOVERY = 7, QCA_DISCONNECT_REASON_KEY_TIMEOUT = 8, QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE = 9, QCA_DISCONNECT_REASON_IFACE_DOWN = 10, QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL = 11, QCA_DISCONNECT_REASON_PEER_INACTIVITY = 12, QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT = 13, QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE = 14, QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE = 15, QCA_DISCONNECT_REASON_USER_TRIGGERED = 16, }; /** * enum qca_wlan_vendor_attr_driver_disconnect_reason - Defines attributes * used by %QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON vendor command. * * @QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASCON_CODE: u32 attribute. * This attribute represents the driver specific reason codes (local * driver/firmware initiated reasons for disconnection) defined * in enum qca_disconnect_reason_codes. */ enum qca_wlan_vendor_attr_driver_disconnect_reason { QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_INVALID = 0, QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASCON_CODE = 1, /* keep last */ QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_MAX = QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST - 1, }; /** * enum qca_wlan_tspec_operation - Operation of the config TSPEC request * * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION. */ enum qca_wlan_tspec_operation { QCA_WLAN_TSPEC_ADD = 0, QCA_WLAN_TSPEC_DEL = 1, QCA_WLAN_TSPEC_GET = 2, }; /** * enum qca_wlan_tspec_direction - Direction in TSPEC * As what is defined in IEEE Std 802.11-2016, Table 9-139. * * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION. */ enum qca_wlan_tspec_direction { QCA_WLAN_TSPEC_DIRECTION_UPLINK = 0, QCA_WLAN_TSPEC_DIRECTION_DOWNLINK = 1, QCA_WLAN_TSPEC_DIRECTION_DIRECT = 2, QCA_WLAN_TSPEC_DIRECTION_BOTH = 3, }; /** * enum qca_wlan_tspec_ack_policy - MAC acknowledgement policy in TSPEC * As what is defined in IEEE Std 802.11-2016, Table 9-141. * * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY. */ enum qca_wlan_tspec_ack_policy { QCA_WLAN_TSPEC_NORMAL_ACK = 0, QCA_WLAN_TSPEC_NO_ACK = 1, /* Reserved */ QCA_WLAN_TSPEC_BLOCK_ACK = 3, }; /** * enum qca_wlan_vendor_attr_config_tspec - Defines attributes * used by %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC vendor command. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION: * u8 attribute. Specify the TSPEC operation of this request. Possible values * are defined in enum qca_wlan_tspec_operation. * Mandatory attribute for all kinds of config TSPEC requests. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID: * u8 attribute. TS ID. Possible values are 0-7. * Applicable for operation: QCA_WLAN_TSPEC_ADD, QCA_WLAN_TSPEC_DEL, * QCA_WLAN_TSPEC_GET. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION: * u8 attribute. Direction of data carried by the TS. Possible values are * defined in enum qca_wlan_tspec_direction. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD: * Flag attribute. Indicate whether APSD is enabled for the traffic associated * with the TS. set - enabled, not set - disabled. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY: * u8 attribute. User priority to be used for the transport of MSDUs/A-MSDUs * belonging to this TS. Possible values are 0-7. * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY: * u8 attribute. Indicate whether MAC acknowledgements are required for * MPDUs/A-MSDUs belonging to this TS and the form of those acknowledgements. * Possible values are defined in enum qca_wlan_tspec_ack_policy. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE: * u16 attribute. Specify the nominal size in bytes of MSDUs/A-MSDUs * belonging to this TS. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE: * u16 attribute. Specify the maximum size in bytes of MSDUs/A-MSDUs * belonging to this TS. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL: * u32 attribute. Specify the minimum interval in microseconds between the * start of two successive SPs. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL: * u32 attribute. Specify the maximum interval in microseconds between the * start of two successive SPs. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL: * u32 attribute. Specify the minimum interval in microseconds that can elapse * without arrival or transfer of an MPDU belonging to the TS before this TS * is deleted by the MAC entity at the HC. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL: * u32 attribute. Specify the minimum interval in microseconds that can elapse * without arrival or transfer of an MSDU belonging to the TS before the * generation of successive QoS(+)CF-Poll is stopped for this TS. A value of * 0xFFFFFFFF disables the suspension interval. The value of the suspension * interval is always less than or equal to the inactivity interval. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE: * u32 attribute. Indicate the lowest data rate in bps specified at the MAC * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the * bounds of this TSPEC. * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE: * u32 attribute. Indicate the average data rate in bps specified at the MAC * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the * bounds of this TSPEC. * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE: * u32 attribute. Indicate the maximum allowable data rate in bps specified at * the MAC SAP for transport of MSDUs or A-MSDUs belonging to this TS within * the bounds of this TSPEC. * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE: * u32 attribute. Specify the maximum burst size in bytes of the MSDUs/A-MSDUs * belonging to this TS that arrive at the MAC SAP at the peak data rate. A * value of 0 indicates that there are no bursts. * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE: * u32 attribute. Indicate the minimum PHY rate in bps for transport of * MSDUs/A-MSDUs belonging to this TS within the bounds of this TSPEC. * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE: * u16 attribute. Specify the excess allocation of time (and bandwidth) over * and above the stated application rates required to transport an MSDU/A-MSDU * belonging to the TS in this TSPEC. * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute. */ enum qca_wlan_vendor_attr_config_tspec { QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INVALID = 0, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION = 1, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID = 2, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION = 3, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD = 4, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY = 5, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY = 6, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE = 7, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE = 8, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL = 9, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL = 10, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL = 11, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL = 12, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE = 13, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE = 14, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE = 15, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE = 16, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE = 17, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE = 18, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX = QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_oci_override_frame_type - OCI override frame type * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ: SA Query Request frame * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP: SA Query Response frame * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ: FT Reassociation Request * frame * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ: FILS Reassociation * Request frame. */ enum qca_wlan_vendor_oci_override_frame_type { QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ = 1, QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP = 2, QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ = 3, QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ = 4, }; /** * enum qca_wlan_vendor_attr_oci_override: Represents attributes for * OCI override request. These attributes are used inside nested attribute * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE in QCA vendor command * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. * * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE: Required attribute, u8. * Values from enum qca_wlan_vendor_oci_override_frame_type used in this * attribute to specify the frame type in which the OCI is to be overridden. * * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY: Required (u32) * OCI frequency (in MHz) to override in the specified frame type. */ enum qca_wlan_vendor_attr_oci_override { QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_INVALID = 0, QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE = 1, QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY = 2, /* keep last */ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX = QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST - 1, }; /** * enum qca_wlan_medium_assess_type - Type of medium assess request * * Values for %QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE. */ enum qca_wlan_medium_assess_type { QCA_WLAN_MEDIUM_ASSESS_CCA = 0, QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT = 1, }; /** * enum qca_wlan_vendor_attr_medium_assess - Attributes used by * %QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS vendor command. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE: * u8 attribute. Mandatory in all kinds of medium assess requests/responses. * Specify the type of medium assess request and indicate its type in response. * Possible values are defined in enum qca_wlan_medium_assess_type. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD: * u32 attribute. Mandatory in CCA request. * Specify the assessment period in terms of seconds. Assessment result will be * sent as the response to the CCA request after the assessment period. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT: * u32 attribute. Mandatory in response to CCA request. * Total timer tick count of the assessment cycle. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT: * u32 attribute. Mandatory in response to CCA request. * Timer tick count of idle time in the assessment cycle. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT: * u32 attribute. Mandatory in response to CCA request. * Timer tick count of Intra BSS traffic RX time in the assessment cycle. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT: * u32 attribute. Mandatory in response to CCA request. * Timer tick count of Overlapping BSS traffic RX time in the assessment cycle. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI: * s32 attribute. Mandatory in response to CCA request. * Maximum RSSI of Intra BSS traffic in the assessment cycle. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI: * s32 attribute. Mandatory in response to CCA request. * Minimum RSSI of Intra BSS traffic in the assessment cycle. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE: * u8 attribute. Mandatory in congestion report request. * 1-enable 0-disable. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD: * u8 attribute. Mandatory in congestion report enable request and will be * ignored if present in congestion report disable request. Possible values are * 0-100. A vendor event QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS with the type * QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT will be sent to userspace if * congestion percentage reaches the configured threshold. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL: * u8 attribute. Optional in congestion report enable request and will be * ignored if present in congestion report disable request. * Specify the interval of congestion report event in terms of seconds. Possible * values are 1-255. Default value 1 will be used if this attribute is omitted * or using invalid values. * * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE: * u8 attribute. Mandatory in congestion report event. * Indicate the actual congestion percentage. Possible values are 0-100. */ enum qca_wlan_vendor_attr_medium_assess { QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_INVALID = 0, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE = 1, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD = 2, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT = 3, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT = 4, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT = 5, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT = 6, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI = 7, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI = 8, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE = 9, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD = 10, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL = 11, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE = 12, /* keep last */ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX = QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_mbssid_tx_vdev_status - Defines attributes * used by QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS vendor command. * * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL: * u8 attribute. Notify the TX VDEV status. Possible values 0, 1 * belonging to MBSSID/EMA_AP configuration. 0 means Non-Tx VDEV, * 1 means Tx VDEV. Mandatory attribute for all MBSSID VDEV status events. */ enum qca_wlan_vendor_attr_mbssid_tx_vdev_status { QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_INVALID = 0, QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL = 1, /* keep last */ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_MAX = QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST - 1, }; /** * enum qca_wlan_concurrent_sta_policy_config - Concurrent STA policies * * @QCA_WLAN_CONCURRENT_STA_POLICY_PREFER_PRIMARY: Preference to the primary * STA interface has to be given while selecting the connection policies * (e.g., BSSID, band, TX/RX chains, etc.) for the subsequent STA interface. * An interface is set as primary through the attribute * QCA_WLAN_VENDOR_ATTR_CONFIG_CONCURRENT_STA_PRIMARY. This policy is not * applicable if the primary interface has not been set earlier. * * The intention is not to downgrade the primary STA performance, such as: * - Do not reduce the number of TX/RX chains of primary connection. * - Do not optimize DBS vs. MCC/SCC, if DBS ends up reducing the number of * chains. * - If using MCC, should set the MCC duty cycle of the primary connection to * be higher than the secondary connection. * * @QCA_WLAN_CONCURRENT_STA_POLICY_UNBIASED: The connection policies for the * subsequent STA connection shall be chosen to balance with the existing * concurrent STA's performance. * Such as * - Can choose MCC or DBS mode depending on the MCC efficiency and hardware * capability. * - If using MCC, set the MCC duty cycle of the primary connection to be equal * to the secondary. * - Prefer BSSID candidates which will help provide the best "overall" * performance for all the STA connections. */ enum qca_wlan_concurrent_sta_policy_config { QCA_WLAN_CONCURRENT_STA_POLICY_PREFER_PRIMARY = 0, QCA_WLAN_CONCURRENT_STA_POLICY_UNBIASED = 1, }; /** * enum qca_wlan_vendor_attr_concurrent_sta_policy - Defines attributes * used by QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY vendor command. * * @QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG: * u8 attribute. Configures the concurrent STA policy configuration. * Possible values are defined in enum qca_wlan_concurrent_sta_policy_config. */ enum qca_wlan_vendor_attr_concurrent_sta_policy { QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_INVALID = 0, QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG = 1, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX = QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_AFTER_LAST - 1, }; /** * enum qca_sta_connect_fail_reason_codes - Defines values carried * by QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE vendor * attribute. * @QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received * for unicast Probe Request frame. * @QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request. * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for * auth request. * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not * received from AP. * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send * Association Request frame. * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for * Association Request frame. * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response * frame is not received from AP. */ enum qca_sta_connect_fail_reason_codes { QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1, QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2, QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3, QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4, QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5, QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6, QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7, }; /** * enum qca_wlan_vendor_usable_channels_filter - Bitmask of different * filters defined in this enum are used in attribute * %QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK. * * @QCA_WLAN_VENDOR_FILTER_CELLULAR_COEX: When this bit is set, the driver * shall filter the channels which are not usable because of coexistence with * cellular radio. * @QCA_WLAN_VENDOR_FILTER_WLAN_CONCURRENCY: When this bit is set, the driver * shall filter the channels which are not usable because of existing active * interfaces in the driver and will result in Multi Channel Concurrency, etc. * */ enum qca_wlan_vendor_usable_channels_filter { QCA_WLAN_VENDOR_FILTER_CELLULAR_COEX = 0, QCA_WLAN_VENDOR_FILTER_WLAN_CONCURRENCY = 1, }; /** * enum qca_wlan_vendor_attr_chan_info - Attributes used inside * %QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO nested attribute. * * @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ: * u32 attribute, required. Indicates the center frequency of the primary * channel in MHz. * * @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG0_FREQ: * u32 attribute. Indicates the center frequency of the primary segment of the * channel in MHz. This attribute is required when reporting 40 MHz, 80 MHz, * 160 MHz, and 320 MHz channels. * * @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG1_FREQ: * u32 attribute. Indicates the center frequency of the secondary segment of * 80+80 channel in MHz. This attribute is required only when * QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH is set to NL80211_CHAN_WIDTH_80P80. * * @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH: * u32 attribute, required. Indicates the bandwidth of the channel, possible * values are defined in enum nl80211_chan_width. * * @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK: * u32 attribute, required. Indicates all the interface types for which this * channel is usable. This attribute encapsulates bitmasks of interface types * defined in enum nl80211_iftype. * */ enum qca_wlan_vendor_attr_chan_info { QCA_WLAN_VENDOR_ATTR_CHAN_INFO_INVALID = 0, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ = 1, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG0_FREQ = 2, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG1_FREQ = 3, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH = 4, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK = 5, /* keep last */ QCA_WLAN_VENDOR_ATTR_CHAN_INFO_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX = QCA_WLAN_VENDOR_ATTR_CHAN_INFO_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_usable_channels - Attributes used by * %QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS vendor command. * * @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK: * u32 attribute. Indicates the bands from which the channels should be reported * in response. This attribute encapsulates bit masks of bands defined in enum * nl80211_band. Optional attribute, if not present in the request the driver * shall return channels from all supported bands. * * @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK: * u32 attribute. Indicates all the interface types for which the usable * channels information is requested. This attribute encapsulates bitmasks of * interface types defined in enum nl80211_iftype. Optional attribute, if not * present in the request the driver shall send information of all supported * interface modes. * * @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK: * u32 attribute. This attribute carries information of all filters that shall * be applied while populating usable channels information by the driver. This * attribute carries bit masks of different filters defined in enum * qca_wlan_vendor_usable_channels_filter. Optional attribute, if not present * in the request the driver shall send information of channels without applying * any of the filters that can be configured through this attribute. * * @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO: * Nested attribute. This attribute shall be used by the driver to send * usability information of each channel. The attributes defined in enum * qca_wlan_vendor_attr_chan_info are used inside this attribute. */ enum qca_wlan_vendor_attr_usable_channels { QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_INVALID = 0, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK = 1, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK = 2, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK = 3, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX = QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_AFTER_LAST - 1, }; /** * enum qca_wlan_vendor_attr_radar_history: Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY to get DFS radar history. * * @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES: Nested attribute to carry * the list of radar history entries. * Each entry contains freq, timestamp, and radar signal detect flag. * The driver shall add an entry when CAC has finished, or radar signal * has been detected post AP beaconing. The driver shall maintain at least * 8 entries in order to save CAC result for a 160 MHz channel. * @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ: u32 attribute. * Channel frequency in MHz. * @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP: u64 nanoseconds. * CLOCK_BOOTTIME timestamp when this entry is updated due to CAC * or radar detection. * @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED: NLA_FLAG attribute. * This flag indicates radar signal has been detected. */ enum qca_wlan_vendor_attr_radar_history { QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_INVALID = 0, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES = 1, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ = 2, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP = 3, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_LAST, QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX = QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_LAST - 1, }; #endif /* QCA_VENDOR_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 561882d0d024..804ac6806f61 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1,6148 +1,6154 @@ /* * Driver interface definition * Copyright (c) 2003-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file defines a driver interface used by both %wpa_supplicant and * hostapd. The first part of the file defines data structures used in various * driver operations. This is followed by the struct wpa_driver_ops that each * driver wrapper will beed to define with callback functions for requesting * driver operations. After this, there are definitions for driver event * reporting with wpa_supplicant_event() and some convenience helper functions * that can be used to report events. */ #ifndef DRIVER_H #define DRIVER_H #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" #ifdef CONFIG_MACSEC #include "pae/ieee802_1x_kay.h" #endif /* CONFIG_MACSEC */ #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 #define HOSTAPD_CHAN_NO_IR 0x00000002 #define HOSTAPD_CHAN_RADAR 0x00000008 #define HOSTAPD_CHAN_HT40PLUS 0x00000010 #define HOSTAPD_CHAN_HT40MINUS 0x00000020 #define HOSTAPD_CHAN_HT40 0x00000040 #define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080 #define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000 #define HOSTAPD_CHAN_DFS_USABLE 0x00000100 #define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200 #define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300 #define HOSTAPD_CHAN_DFS_MASK 0x00000300 #define HOSTAPD_CHAN_VHT_10_70 0x00000800 #define HOSTAPD_CHAN_VHT_30_50 0x00001000 #define HOSTAPD_CHAN_VHT_50_30 0x00002000 #define HOSTAPD_CHAN_VHT_70_10 0x00004000 #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000 #define HOSTAPD_CHAN_VHT_10_150 0x00100000 #define HOSTAPD_CHAN_VHT_30_130 0x00200000 #define HOSTAPD_CHAN_VHT_50_110 0x00400000 #define HOSTAPD_CHAN_VHT_70_90 0x00800000 #define HOSTAPD_CHAN_VHT_90_70 0x01000000 #define HOSTAPD_CHAN_VHT_110_50 0x02000000 #define HOSTAPD_CHAN_VHT_130_30 0x04000000 #define HOSTAPD_CHAN_VHT_150_10 0x08000000 /* Allowed bandwidth mask */ enum hostapd_chan_width_attr { HOSTAPD_CHAN_WIDTH_10 = BIT(0), HOSTAPD_CHAN_WIDTH_20 = BIT(1), HOSTAPD_CHAN_WIDTH_40P = BIT(2), HOSTAPD_CHAN_WIDTH_40M = BIT(3), HOSTAPD_CHAN_WIDTH_80 = BIT(4), HOSTAPD_CHAN_WIDTH_160 = BIT(5), }; /* Filter gratuitous ARP */ #define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0) /* Filter unsolicited Neighbor Advertisement */ #define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1) /* Filter unicast IP packets encrypted using the GTK */ #define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2) #define HOSTAPD_DFS_REGION_FCC 1 #define HOSTAPD_DFS_REGION_ETSI 2 #define HOSTAPD_DFS_REGION_JP 3 /** * enum reg_change_initiator - Regulatory change initiator */ enum reg_change_initiator { REGDOM_SET_BY_CORE, REGDOM_SET_BY_USER, REGDOM_SET_BY_DRIVER, REGDOM_SET_BY_COUNTRY_IE, REGDOM_BEACON_HINT, }; /** * enum reg_type - Regulatory change types */ enum reg_type { REGDOM_TYPE_UNKNOWN, REGDOM_TYPE_COUNTRY, REGDOM_TYPE_WORLD, REGDOM_TYPE_CUSTOM_WORLD, REGDOM_TYPE_INTERSECTION, }; /** * struct hostapd_wmm_rule - WMM regulatory rule * @min_cwmin: Lower bound of CW_min value * @min_cwmax: Lower bound of CW_max value * @min_aifs: Lower bound of AIFS value * @max_txop: Upper bound of TXOP, value in units of 32 usec */ struct hostapd_wmm_rule { int min_cwmin; int min_cwmax; int min_aifs; int max_txop; }; /** * struct hostapd_channel_data - Channel information */ struct hostapd_channel_data { /** * chan - Channel number (IEEE 802.11) */ short chan; /** * freq - Frequency in MHz */ int freq; /** * flag - Channel flags (HOSTAPD_CHAN_*) */ int flag; /** * allowed_bw - Allowed channel width bitmask * * See enum hostapd_chan_width_attr. */ u32 allowed_bw; /** * max_tx_power - Regulatory transmit power limit in dBm */ u8 max_tx_power; /** * survey_list - Linked list of surveys (struct freq_survey) */ struct dl_list survey_list; /** * min_nf - Minimum observed noise floor, in dBm, based on all * surveyed channel data */ s8 min_nf; #ifdef CONFIG_ACS /** * interference_factor - Computed interference factor on this * channel (used internally in src/ap/acs.c; driver wrappers do not * need to set this) */ long double interference_factor; #endif /* CONFIG_ACS */ /** * dfs_cac_ms - DFS CAC time in milliseconds */ unsigned int dfs_cac_ms; /** * wmm_rules_valid - Indicates wmm_rules state */ int wmm_rules_valid; /** * wmm_rules - WMM regulatory rules */ struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM]; }; #define HE_MAC_CAPAB_0 0 #define HE_MAX_MAC_CAPAB_SIZE 6 #define HE_MAX_PHY_CAPAB_SIZE 11 #define HE_MAX_MCS_CAPAB_SIZE 12 #define HE_MAX_PPET_CAPAB_SIZE 25 /** * struct he_capabilities - IEEE 802.11ax HE capabilities */ struct he_capabilities { u8 he_supported; u8 phy_cap[HE_MAX_PHY_CAPAB_SIZE]; u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE]; u8 mcs[HE_MAX_MCS_CAPAB_SIZE]; u8 ppet[HE_MAX_PPET_CAPAB_SIZE]; u16 he_6ghz_capa; }; #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) enum ieee80211_op_mode { IEEE80211_MODE_INFRA = 0, IEEE80211_MODE_IBSS = 1, IEEE80211_MODE_AP = 2, IEEE80211_MODE_MESH = 5, /* only add new entries before IEEE80211_MODE_NUM */ IEEE80211_MODE_NUM }; /** * struct ieee80211_edmg_config - EDMG configuration * * This structure describes most essential parameters needed * for IEEE 802.11ay EDMG configuration * * @channels: Bitmap that indicates the 2.16 GHz channel(s) * that are allowed to be used for transmissions. * Bit 0 indicates channel 1, bit 1 indicates channel 2, etc. * Set to 0 to indicate EDMG not supported. * @bw_config: Channel BW Configuration subfield encodes * the allowed channel bandwidth configurations */ struct ieee80211_edmg_config { u8 channels; enum edmg_bw_config bw_config; }; /** * struct hostapd_hw_modes - Supported hardware mode information */ struct hostapd_hw_modes { /** * mode - Hardware mode */ enum hostapd_hw_mode mode; /** * num_channels - Number of entries in the channels array */ int num_channels; /** * channels - Array of supported channels */ struct hostapd_channel_data *channels; /** * num_rates - Number of entries in the rates array */ int num_rates; /** * rates - Array of supported rates in 100 kbps units */ int *rates; /** * ht_capab - HT (IEEE 802.11n) capabilities */ u16 ht_capab; /** * mcs_set - MCS (IEEE 802.11n) rate parameters */ u8 mcs_set[16]; /** * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters */ u8 a_mpdu_params; /** * vht_capab - VHT (IEEE 802.11ac) capabilities */ u32 vht_capab; /** * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters */ u8 vht_mcs_set[8]; unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ /** * he_capab - HE (IEEE 802.11ax) capabilities */ struct he_capabilities he_capab[IEEE80211_MODE_NUM]; /** * This structure describes the most essential parameters needed * for IEEE 802.11ay EDMG configuration. */ struct ieee80211_edmg_config edmg; }; #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 #define IEEE80211_CAP_RRM 0x1000 /* DMG (60 GHz) IEEE 802.11ad */ /* type - bits 0..1 */ #define IEEE80211_CAP_DMG_MASK 0x0003 #define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */ #define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */ #define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */ #define WPA_SCAN_QUAL_INVALID BIT(0) #define WPA_SCAN_NOISE_INVALID BIT(1) #define WPA_SCAN_LEVEL_INVALID BIT(2) #define WPA_SCAN_LEVEL_DBM BIT(3) #define WPA_SCAN_ASSOCIATED BIT(5) /** * struct wpa_scan_res - Scan result for an BSS/IBSS * @flags: information flags about the BSS/IBSS (WPA_SCAN_*) * @bssid: BSSID * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) * @beacon_int: beacon interval in TUs (host byte order) * @caps: capability information field in host byte order * @qual: signal quality * @noise: noise level * @level: signal level * @tsf: Timestamp * @age: Age of the information in milliseconds (i.e., how many milliseconds * ago the last Beacon or Probe Response frame was received) * @est_throughput: Estimated throughput in kbps (this is calculated during * scan result processing if left zero by the driver wrapper) * @snr: Signal-to-noise ratio in dB (calculated during scan result processing) * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms * of TSF of the BSS specified by %tsf_bssid. * @tsf_bssid: The BSS that %parent_tsf TSF time refers to. * @ie_len: length of the following IE field in octets * @beacon_ie_len: length of the following Beacon IE field in octets * * This structure is used as a generic format for scan results from the * driver. Each driver interface implementation is responsible for converting * the driver or OS specific scan results into this format. * * If the driver does not support reporting all IEs, the IE data structure is * constructed of the IEs that are available. This field will also need to * include SSID in IE format. All drivers are encouraged to be extended to * report all IEs to make it easier to support future additions. * * This structure data is followed by ie_len octets of IEs from Probe Response * frame (or if the driver does not indicate source of IEs, these may also be * from Beacon frame). After the first set of IEs, another set of IEs may follow * (with beacon_ie_len octets of data) if the driver provides both IE sets. */ struct wpa_scan_res { unsigned int flags; u8 bssid[ETH_ALEN]; int freq; u16 beacon_int; u16 caps; int qual; int noise; int level; u64 tsf; unsigned int age; unsigned int est_throughput; int snr; u64 parent_tsf; u8 tsf_bssid[ETH_ALEN]; size_t ie_len; size_t beacon_ie_len; /* Followed by ie_len + beacon_ie_len octets of IE data */ }; /** * struct wpa_scan_results - Scan results * @res: Array of pointers to allocated variable length scan result entries * @num: Number of entries in the scan result array * @fetch_time: Time when the results were fetched from the driver */ struct wpa_scan_results { struct wpa_scan_res **res; size_t num; struct os_reltime fetch_time; }; /** * struct wpa_interface_info - Network interface information * @next: Pointer to the next interface or NULL if this is the last one * @ifname: Interface name that can be used with init() or init2() * @desc: Human readable adapter description (e.g., vendor/model) or NULL if * not available * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one * is not an allocated copy, i.e., get_interfaces() caller will not free * this) */ struct wpa_interface_info { struct wpa_interface_info *next; char *ifname; char *desc; const char *drv_name; }; #define WPAS_MAX_SCAN_SSIDS 16 /** * struct wpa_driver_scan_ssid - SSIDs to scan for * @ssid - specific SSID to scan for (ProbeReq) * %NULL or zero-length SSID is used to indicate active scan * with wildcard SSID. * @ssid_len - Length of the SSID in octets */ struct wpa_driver_scan_ssid { const u8 *ssid; size_t ssid_len; }; /** * struct wpa_driver_scan_params - Scan parameters * Data for struct wpa_driver_ops::scan2(). */ struct wpa_driver_scan_params { /** * ssids - SSIDs to scan for */ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; /** * num_ssids - Number of entries in ssids array * Zero indicates a request for a passive scan. */ size_t num_ssids; /** * extra_ies - Extra IE(s) to add into Probe Request or %NULL */ const u8 *extra_ies; /** * extra_ies_len - Length of extra_ies in octets */ size_t extra_ies_len; /** * freqs - Array of frequencies to scan or %NULL for all frequencies * * The frequency is set in MHz. The array is zero-terminated. */ int *freqs; /** * filter_ssids - Filter for reporting SSIDs * * This optional parameter can be used to request the driver wrapper to * filter scan results to include only the specified SSIDs. %NULL * indicates that no filtering is to be done. This can be used to * reduce memory needs for scan results in environments that have large * number of APs with different SSIDs. * * The driver wrapper is allowed to take this allocated buffer into its * own use by setting the pointer to %NULL. In that case, the driver * wrapper is responsible for freeing the buffer with os_free() once it * is not needed anymore. */ struct wpa_driver_scan_filter { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; } *filter_ssids; /** * num_filter_ssids - Number of entries in filter_ssids array */ size_t num_filter_ssids; /** * filter_rssi - Filter by RSSI * * The driver may filter scan results in firmware to reduce host * wakeups and thereby save power. Specify the RSSI threshold in s32 * dBm. */ s32 filter_rssi; /** * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes * * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 * Mbps from the support rates element(s) in the Probe Request frames * and not to transmit the frames at any of those rates. */ unsigned int p2p_probe:1; /** * only_new_results - Request driver to report only new results * * This is used to request the driver to report only BSSes that have * been detected after this scan request has been started, i.e., to * flush old cached BSS entries. */ unsigned int only_new_results:1; /** * low_priority - Requests driver to use a lower scan priority * * This is used to request the driver to use a lower scan priority * if it supports such a thing. */ unsigned int low_priority:1; /** * mac_addr_rand - Requests driver to randomize MAC address */ unsigned int mac_addr_rand:1; /** * mac_addr - MAC address used with randomization. The address cannot be * a multicast one, i.e., bit 0 of byte 0 should not be set. */ u8 *mac_addr; /** * mac_addr_mask - MAC address mask used with randomization. * * Bits that are 0 in the mask should be randomized. Bits that are 1 in * the mask should be taken as is from mac_addr. The mask should not * allow the generation of a multicast address, i.e., bit 0 of byte 0 * must be set. */ const u8 *mac_addr_mask; /** * sched_scan_plans - Scan plans for scheduled scan * * Each scan plan consists of the number of iterations to scan and the * interval between scans. When a scan plan finishes (i.e., it was run * for the specified number of iterations), the next scan plan is * executed. The scan plans are executed in the order they appear in * the array (lower index first). The last scan plan will run infinitely * (until requested to stop), thus must not specify the number of * iterations. All other scan plans must specify the number of * iterations. */ struct sched_scan_plan { u32 interval; /* In seconds */ u32 iterations; /* Zero to run infinitely */ } *sched_scan_plans; /** * sched_scan_plans_num - Number of scan plans in sched_scan_plans array */ unsigned int sched_scan_plans_num; /** * sched_scan_start_delay - Delay to use before starting the first scan * * Delay (in seconds) before scheduling first scan plan cycle. The * driver may ignore this parameter and start immediately (or at any * other time), if this feature is not supported. */ u32 sched_scan_start_delay; /** * bssid - Specific BSSID to scan for * * This optional parameter can be used to replace the default wildcard * BSSID with a specific BSSID to scan for if results are needed from * only a single BSS. */ const u8 *bssid; /** * scan_cookie - Unique identification representing the scan request * * This scan_cookie carries a unique identification representing the * scan request if the host driver/kernel supports concurrent scan * requests. This cookie is returned from the corresponding driver * interface. * * Note: Unlike other parameters in this structure, scan_cookie is used * only to return information instead of setting parameters for the * scan. */ u64 scan_cookie; /** * duration - Dwell time on each channel * * This optional parameter can be used to set the dwell time on each * channel. In TUs. */ u16 duration; /** * duration_mandatory - Whether the specified duration is mandatory * * If this is set, the duration specified by the %duration field is * mandatory (and the driver should reject the scan request if it is * unable to comply with the specified duration), otherwise it is the * maximum duration and the actual duration may be shorter. */ unsigned int duration_mandatory:1; /** * relative_rssi_set - Whether relative RSSI parameters are set */ unsigned int relative_rssi_set:1; /** * relative_rssi - Relative RSSI for reporting better BSSs * * Amount of RSSI by which a BSS should be better than the current * connected BSS to report the new BSS to user space. */ s8 relative_rssi; /** * relative_adjust_band - Band to which RSSI should be adjusted * * The relative_adjust_rssi should be added to the band specified * by relative_adjust_band. */ enum set_band relative_adjust_band; /** * relative_adjust_rssi - RSSI to be added to relative_adjust_band * * An amount of relative_band_rssi should be added to the BSSs that * belong to the band specified by relative_adjust_band while comparing * with other bands for BSS reporting. */ s8 relative_adjust_rssi; /** * oce_scan * * Enable the following OCE scan features: (WFA OCE TechSpec v1.0) * - Accept broadcast Probe Response frame. * - Probe Request frame deferral and suppression. * - Max Channel Time - driver fills FILS request params IE with * Maximum Channel Time. * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps. */ unsigned int oce_scan:1; + /** + * p2p_include_6ghz - Include 6 GHz channels for P2P full scan + * + */ + unsigned int p2p_include_6ghz:1; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with * matching changes. */ }; /** * struct wpa_driver_auth_params - Authentication parameters * Data for struct wpa_driver_ops::authenticate(). */ struct wpa_driver_auth_params { int freq; const u8 *bssid; const u8 *ssid; size_t ssid_len; int auth_alg; const u8 *ie; size_t ie_len; const u8 *wep_key[4]; size_t wep_key_len[4]; int wep_tx_keyidx; int local_state_change; /** * p2p - Whether this connection is a P2P group */ int p2p; /** * auth_data - Additional elements for Authentication frame * * This buffer starts with the Authentication transaction sequence * number field. If no special handling of such elements is needed, this * pointer is %NULL. This is used with SAE and FILS. */ const u8 *auth_data; /** * auth_data_len - Length of auth_data buffer in octets */ size_t auth_data_len; }; /** * enum wps_mode - WPS mode */ enum wps_mode { /** * WPS_MODE_NONE - No WPS provisioning being used */ WPS_MODE_NONE, /** * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode */ WPS_MODE_OPEN, /** * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection */ WPS_MODE_PRIVACY }; /** * struct hostapd_freq_params - Channel parameters */ struct hostapd_freq_params { /** * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..) */ enum hostapd_hw_mode mode; /** * freq - Primary channel center frequency in MHz */ int freq; /** * channel - Channel number */ int channel; /** * ht_enabled - Whether HT is enabled */ int ht_enabled; /** * sec_channel_offset - Secondary channel offset for HT40 * * 0 = HT40 disabled, * -1 = HT40 enabled, secondary channel below primary, * 1 = HT40 enabled, secondary channel above primary */ int sec_channel_offset; /** * vht_enabled - Whether VHT is enabled */ int vht_enabled; /** * he_enabled - Whether HE is enabled */ int he_enabled; /** * center_freq1 - Segment 0 center frequency in MHz * * Valid for both HT and VHT. */ int center_freq1; /** * center_freq2 - Segment 1 center frequency in MHz * * Non-zero only for bandwidth 80 and an 80+80 channel */ int center_freq2; /** * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160) */ int bandwidth; /** * This structure describes the most essential parameters needed * for IEEE 802.11ay EDMG configuration. */ struct ieee80211_edmg_config edmg; }; /** * struct wpa_driver_sta_auth_params - Authentication parameters * Data for struct wpa_driver_ops::sta_auth(). */ struct wpa_driver_sta_auth_params { /** * own_addr - Source address and BSSID for authentication frame */ const u8 *own_addr; /** * addr - MAC address of the station to associate */ const u8 *addr; /** * seq - authentication sequence number */ u16 seq; /** * status - authentication response status code */ u16 status; /** * ie - authentication frame ie buffer */ const u8 *ie; /** * len - ie buffer length */ size_t len; /** * fils_auth - Indicates whether FILS authentication is being performed */ int fils_auth; /** * fils_anonce - ANonce (required for FILS) */ u8 fils_anonce[WPA_NONCE_LEN]; /** * fils_snonce - SNonce (required for FILS) */ u8 fils_snonce[WPA_NONCE_LEN]; /** * fils_kek - key for encryption (required for FILS) */ u8 fils_kek[WPA_KEK_MAX_LEN]; /** * fils_kek_len - Length of the fils_kek in octets (required for FILS) */ size_t fils_kek_len; }; /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). */ struct wpa_driver_associate_params { /** * bssid - BSSID of the selected AP * This can be %NULL, if ap_scan=2 mode is used and the driver is * responsible for selecting with which BSS to associate. */ const u8 *bssid; /** * bssid_hint - BSSID of a proposed AP * * This indicates which BSS has been found a suitable candidate for * initial association for drivers that use driver/firmwate-based BSS * selection. Unlike the @bssid parameter, @bssid_hint does not limit * the driver from selecting other BSSes in the ESS. */ const u8 *bssid_hint; /** * ssid - The selected SSID */ const u8 *ssid; /** * ssid_len - Length of the SSID (1..32) */ size_t ssid_len; /** * freq - channel parameters */ struct hostapd_freq_params freq; /** * freq_hint - Frequency of the channel the proposed AP is using * * This provides a channel on which a suitable BSS has been found as a * hint for the driver. Unlike the @freq parameter, @freq_hint does not * limit the driver from selecting other channels for * driver/firmware-based BSS selection. */ int freq_hint; /** * bg_scan_period - Background scan period in seconds, 0 to disable * background scan, or -1 to indicate no change to default driver * configuration */ int bg_scan_period; /** * beacon_int - Beacon interval for IBSS or 0 to use driver default */ int beacon_int; /** * wpa_ie - WPA information element for (Re)Association Request * WPA information element to be included in (Re)Association * Request (including information element id and length). Use * of this WPA IE is optional. If the driver generates the WPA * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and * key_mgmt_suite to select proper algorithms. In this case, * the driver has to notify wpa_supplicant about the used WPA * IE by generating an event that the interface code will * convert into EVENT_ASSOCINFO data (see below). * * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE * instead. The driver can determine which version is used by * looking at the first byte of the IE (0xdd for WPA, 0x30 for * WPA2/RSN). * * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. */ const u8 *wpa_ie; /** * wpa_ie_len - length of the wpa_ie */ size_t wpa_ie_len; /** * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 */ unsigned int wpa_proto; /** * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int pairwise_suite; /** * group_suite - Selected group cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int group_suite; /** * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int mgmt_group_suite; /** * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * * This is usually ignored if @wpa_ie is used. */ unsigned int key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms * Bit field of WPA_AUTH_ALG_* */ int auth_alg; /** * mode - Operation mode (infra/ibss) IEEE80211_MODE_* */ int mode; /** * wep_key - WEP keys for static WEP configuration */ const u8 *wep_key[4]; /** * wep_key_len - WEP key length for static WEP configuration */ size_t wep_key_len[4]; /** * wep_tx_keyidx - WEP TX key index for static WEP configuration */ int wep_tx_keyidx; /** * mgmt_frame_protection - IEEE 802.11w management frame protection */ enum mfp_options mgmt_frame_protection; /** * passphrase - RSN passphrase for PSK * * This value is made available only for WPA/WPA2-Personal (PSK) and * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This * is the 8..63 character ASCII passphrase, if available. Please note * that this can be %NULL if passphrase was not used to generate the * PSK. In that case, the psk field must be used to fetch the PSK. */ const char *passphrase; /** * psk - RSN PSK (alternative for passphrase for PSK) * * This value is made available only for WPA/WPA2-Personal (PSK) and * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This * is the 32-octet (256-bit) PSK, if available. The driver wrapper * should be prepared to handle %NULL value as an error. */ const u8 *psk; /** * drop_unencrypted - Enable/disable unencrypted frame filtering * * Configure the driver to drop all non-EAPOL frames (both receive and * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must * still be allowed for key negotiation. */ int drop_unencrypted; /** * prev_bssid - Previously used BSSID in this ESS * * When not %NULL, this is a request to use reassociation instead of * association. */ const u8 *prev_bssid; /** * wps - WPS mode * * If the driver needs to do special configuration for WPS association, * this variable provides more information on what type of association * is being requested. Most drivers should not need ot use this. */ enum wps_mode wps; /** * p2p - Whether this connection is a P2P group */ int p2p; /** * uapsd - UAPSD parameters for the network * -1 = do not change defaults * AP mode: 1 = enabled, 0 = disabled * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE */ int uapsd; /** * fixed_bssid - Whether to force this BSSID in IBSS mode * 1 = Fix this BSSID and prevent merges. * 0 = Do not fix BSSID. */ int fixed_bssid; /** * fixed_freq - Fix control channel in IBSS mode * 0 = don't fix control channel (default) * 1 = fix control channel; this prevents IBSS merging with another * channel */ int fixed_freq; /** * disable_ht - Disable HT (IEEE 802.11n) for this connection */ int disable_ht; /** * htcaps - HT Capabilities over-rides * * Only bits set in the mask will be used, and not all values are used * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used. * * Pointer to struct ieee80211_ht_capabilities. */ const u8 *htcaps; /** * htcaps_mask - HT Capabilities over-rides mask * * Pointer to struct ieee80211_ht_capabilities. */ const u8 *htcaps_mask; #ifdef CONFIG_VHT_OVERRIDES /** * disable_vht - Disable VHT for this connection */ int disable_vht; /** * VHT capability overrides. */ const struct ieee80211_vht_capabilities *vhtcaps; const struct ieee80211_vht_capabilities *vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ #ifdef CONFIG_HE_OVERRIDES /** * disable_he - Disable HE for this connection */ int disable_he; #endif /* CONFIG_HE_OVERRIDES */ /** * req_key_mgmt_offload - Request key management offload for connection * * Request key management offload for this connection if the device * supports it. */ int req_key_mgmt_offload; /** * req_handshake_offload - Request EAPOL handshake offload * * Request EAPOL handshake offload for this connection if the device * supports it. */ int req_handshake_offload; /** * Flag for indicating whether this association includes support for * RRM (Radio Resource Measurements) */ int rrm_used; /** * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an * AP as usual. Valid for DMG network only. */ int pbss; /** * fils_kek - KEK for FILS association frame protection (AES-SIV) */ const u8 *fils_kek; /** * fils_kek_len: Length of fils_kek in bytes */ size_t fils_kek_len; /** * fils_nonces - Nonces for FILS association frame protection * (AES-SIV AAD) */ const u8 *fils_nonces; /** * fils_nonces_len: Length of fils_nonce in bytes */ size_t fils_nonces_len; /** * fils_erp_username - Username part of keyName-NAI */ const u8 *fils_erp_username; /** * fils_erp_username_len - Length of fils_erp_username in bytes */ size_t fils_erp_username_len; /** * fils_erp_realm - Realm/domain name to use in FILS ERP */ const u8 *fils_erp_realm; /** * fils_erp_realm_len - Length of fils_erp_realm in bytes */ size_t fils_erp_realm_len; /** * fils_erp_next_seq_num - The next sequence number to use in FILS ERP * messages */ u16 fils_erp_next_seq_num; /** * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI * specified by fils_erp_username@fils_erp_realm. */ const u8 *fils_erp_rrk; /** * fils_erp_rrk_len - Length of fils_erp_rrk in bytes */ size_t fils_erp_rrk_len; /** * sae_pwe - SAE mechanism for PWE derivation * 0 = hunting-and-pecking loop only * 1 = hash-to-element only * 2 = both hunting-and-pecking loop and hash-to-element enabled */ int sae_pwe; }; enum hide_ssid { NO_SSID_HIDING, HIDDEN_SSID_ZERO_LEN, HIDDEN_SSID_ZERO_CONTENTS }; enum ch_switch_state { CH_SW_STARTED, CH_SW_FINISHED }; struct wowlan_triggers { u8 any; u8 disconnect; u8 magic_pkt; u8 gtk_rekey_failure; u8 eap_identity_req; u8 four_way_handshake; u8 rfkill_release; }; struct wpa_driver_ap_params { /** * head - Beacon head from IEEE 802.11 header to IEs before TIM IE */ u8 *head; /** * head_len - Length of the head buffer in octets */ size_t head_len; /** * tail - Beacon tail following TIM IE */ u8 *tail; /** * tail_len - Length of the tail buffer in octets */ size_t tail_len; /** * dtim_period - DTIM period */ int dtim_period; /** * beacon_int - Beacon interval */ int beacon_int; /** * basic_rates: -1 terminated array of basic rates in 100 kbps * * This parameter can be used to set a specific basic rate set for the * BSS. If %NULL, default basic rate set is used. */ int *basic_rates; /** * beacon_rate: Beacon frame data rate * * This parameter can be used to set a specific Beacon frame data rate * for the BSS. The interpretation of this value depends on the * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS, * HE: HE-MCS). If beacon_rate == 0 and rate_type == 0 * (BEACON_RATE_LEGACY), the default Beacon frame data rate is used. */ unsigned int beacon_rate; /** * beacon_rate_type: Beacon data rate type (legacy/HT/VHT/HE) */ enum beacon_rate_type rate_type; /** * proberesp - Probe Response template * * This is used by drivers that reply to Probe Requests internally in * AP mode and require the full Probe Response template. */ u8 *proberesp; /** * proberesp_len - Length of the proberesp buffer in octets */ size_t proberesp_len; /** * ssid - The SSID to use in Beacon/Probe Response frames */ const u8 *ssid; /** * ssid_len - Length of the SSID (1..32) */ size_t ssid_len; /** * hide_ssid - Whether to hide the SSID */ enum hide_ssid hide_ssid; /** * pairwise_ciphers - WPA_CIPHER_* bitfield */ unsigned int pairwise_ciphers; /** * group_cipher - WPA_CIPHER_* */ unsigned int group_cipher; /** * key_mgmt_suites - WPA_KEY_MGMT_* bitfield */ unsigned int key_mgmt_suites; /** * auth_algs - WPA_AUTH_ALG_* bitfield */ unsigned int auth_algs; /** * wpa_version - WPA_PROTO_* bitfield */ unsigned int wpa_version; /** * privacy - Whether privacy is used in the BSS */ int privacy; /** * beacon_ies - WPS/P2P IE(s) for Beacon frames * * This is used to add IEs like WPS IE and P2P IE by drivers that do * not use the full Beacon template. */ const struct wpabuf *beacon_ies; /** * proberesp_ies - P2P/WPS IE(s) for Probe Response frames * * This is used to add IEs like WPS IE and P2P IE by drivers that * reply to Probe Request frames internally. */ const struct wpabuf *proberesp_ies; /** * assocresp_ies - WPS IE(s) for (Re)Association Response frames * * This is used to add IEs like WPS IE by drivers that reply to * (Re)Association Request frames internally. */ const struct wpabuf *assocresp_ies; /** * isolate - Whether to isolate frames between associated stations * * If this is non-zero, the AP is requested to disable forwarding of * frames between associated stations. */ int isolate; /** * cts_protect - Whether CTS protection is enabled */ int cts_protect; /** * preamble - Whether short preamble is enabled */ int preamble; /** * short_slot_time - Whether short slot time is enabled * * 0 = short slot time disable, 1 = short slot time enabled, -1 = do * not set (e.g., when 802.11g mode is not in use) */ int short_slot_time; /** * ht_opmode - HT operation mode or -1 if HT not in use */ int ht_opmode; /** * interworking - Whether Interworking is enabled */ int interworking; /** * hessid - Homogeneous ESS identifier or %NULL if not set */ const u8 *hessid; /** * access_network_type - Access Network Type (0..15) * * This is used for filtering Probe Request frames when Interworking is * enabled. */ u8 access_network_type; /** * ap_max_inactivity - Timeout in seconds to detect STA's inactivity * * This is used by driver which advertises this capability. */ int ap_max_inactivity; /** * ctwindow - Client Traffic Window (in TUs) */ u8 p2p_go_ctwindow; /** * disable_dgaf - Whether group-addressed frames are disabled */ int disable_dgaf; /** * osen - Whether OSEN security is enabled */ int osen; /** * freq - Channel parameters for dynamic bandwidth changes */ struct hostapd_freq_params *freq; /** * reenable - Whether this is to re-enable beaconing */ int reenable; /** * pbss - Whether to start a PCP (in PBSS) instead of an AP in * infrastructure BSS. Valid only for DMG network. */ int pbss; /** * multicast_to_unicast - Whether to use multicast_to_unicast * * If this is non-zero, the AP is requested to perform multicast to * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within * 802.1Q). If enabled, such frames are to be sent to each station * separately, with the DA replaced by their own MAC address rather * than the group address. * * Note that this may break certain expectations of the receiver, such * as the ability to drop unicast IP packets received within multicast * L2 frames, or the ability to not send ICMP destination unreachable * messages for packets received in L2 multicast (which is required, * but the receiver can't tell the difference if this new option is * enabled.) * * This also doesn't implement the 802.11 DMS (directed multicast * service). */ int multicast_to_unicast; /** * ftm_responder - Whether FTM responder is enabled */ int ftm_responder; /** * lci - Binary data, the content of an LCI report IE with type 8 as * defined in IEEE Std 802.11-2016, 9.4.2.22.10 */ const struct wpabuf *lci; /** * civic - Binary data, the content of a measurement report IE with * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13 */ const struct wpabuf *civic; /** * he_spr_ctrl - Spatial Reuse control field of SPR element */ u8 he_spr_ctrl; /** * he_spr_non_srg_obss_pd_max_offset - Non-SRG Maximum TX power offset */ u8 he_spr_non_srg_obss_pd_max_offset; /** * he_spr_srg_obss_pd_min_offset - Minimum TX power offset */ u8 he_spr_srg_obss_pd_min_offset; /** * he_spr_srg_obss_pd_max_offset - Maximum TX power offset */ u8 he_spr_srg_obss_pd_max_offset; /** * he_spr_bss_color_bitmap - BSS color values used by members of the * SRG. */ u8 he_spr_bss_color_bitmap[8]; /** * he_spr_partial_bssid_bitmap - Partial BSSID values used by members * of the SRG. */ u8 he_spr_partial_bssid_bitmap[8]; /** * he_bss_color - Whether the BSS Color is disabled */ int he_bss_color_disabled; /** * he_bss_color_partial - The BSS Color AID equation */ int he_bss_color_partial; /** * he_bss_color - The BSS Color of the AP */ int he_bss_color; /** * twt_responder - Whether Target Wait Time responder is enabled */ int twt_responder; /** * sae_pwe - SAE mechanism for PWE derivation * 0 = hunting-and-pecking loop only * 1 = hash-to-element only * 2 = both hunting-and-pecking loop and hash-to-element enabled */ int sae_pwe; /** * FILS Discovery frame minimum interval in TUs */ u32 fd_min_int; /** * FILS Discovery frame maximum interval in TUs (0 = FD frame disabled) */ u32 fd_max_int; /** * FILS Discovery frame template data */ u8 *fd_frame_tmpl; /** * FILS Discovery frame template length */ size_t fd_frame_tmpl_len; /** * Unsolicited broadcast Probe Response interval in TUs */ unsigned int unsol_bcast_probe_resp_interval; /** * Unsolicited broadcast Probe Response template data */ u8 *unsol_bcast_probe_resp_tmpl; /** * Unsolicited broadcast Probe Response template length */ size_t unsol_bcast_probe_resp_tmpl_len; }; struct wpa_driver_mesh_bss_params { #define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 #define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002 #define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004 #define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008 #define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010 /* * TODO: Other mesh configuration parameters would go here. * See NL80211_MESHCONF_* for all the mesh config parameters. */ unsigned int flags; int auto_plinks; int peer_link_timeout; int max_peer_links; int rssi_threshold; u16 ht_opmode; }; struct wpa_driver_mesh_join_params { const u8 *meshid; int meshid_len; const int *basic_rates; const u8 *ies; int ie_len; struct hostapd_freq_params freq; int beacon_int; int dtim_period; struct wpa_driver_mesh_bss_params conf; #define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001 #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002 #define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004 #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008 unsigned int flags; bool handle_dfs; }; struct wpa_driver_set_key_params { /** * ifname - Interface name (for multi-SSID/VLAN support) */ const char *ifname; /** * alg - Encryption algorithm * * (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP, * %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, * %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, * %WPA_ALG_BIP_CMAC_256); * %WPA_ALG_NONE clears the key. */ enum wpa_alg alg; /** * addr - Address of the peer STA * * (BSSID of the current AP when setting pairwise key in station mode), * ff:ff:ff:ff:ff:ff for broadcast keys, %NULL for default keys that * are used both for broadcast and unicast; when clearing keys, %NULL * is used to indicate that both the broadcast-only and default key of * the specified key index is to be cleared */ const u8 *addr; /** * key_idx - Key index * * (0..3), usually 0 for unicast keys; 4..5 for IGTK; 6..7 for BIGTK */ int key_idx; /** * set_tx - Configure this key as the default Tx key * * Only used when driver does not support separate unicast/individual * key */ int set_tx; /** * seq - Sequence number/packet number * * seq_len octets, the next packet number to be used for in replay * protection; configured for Rx keys (in most cases, this is only used * with broadcast keys and set to zero for unicast keys); %NULL if not * set */ const u8 *seq; /** * seq_len - Length of the seq, depends on the algorithm * * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets */ size_t seq_len; /** * key - Key buffer * * TKIP: 16-byte temporal key, 8-byte Tx Mic key, 8-byte Rx Mic Key */ const u8 *key; /** * key_len - Length of the key buffer in octets * * WEP: 5 or 13, TKIP: 32, CCMP/GCMP: 16, IGTK: 16 */ size_t key_len; /** * vlan_id - VLAN index (0..4095) for VLAN offload cases */ int vlan_id; /** * key_flag - Additional key flags * * %KEY_FLAG_MODIFY * Set when an already installed key must be updated. * So far the only use-case is changing RX/TX status for * pairwise keys. Must not be set when deleting a key. * %KEY_FLAG_DEFAULT * Set when the key is also a default key. Must not be set when * deleting a key. * %KEY_FLAG_RX * The key is valid for RX. Must not be set when deleting a key. * %KEY_FLAG_TX * The key is valid for TX. Must not be set when deleting a key. * %KEY_FLAG_GROUP * The key is a broadcast or group key. * %KEY_FLAG_PAIRWISE * The key is a pairwise key. * %KEY_FLAG_PMK * The key is a Pairwise Master Key (PMK). * * Valid and pre-defined combinations are: * %KEY_FLAG_GROUP_RX_TX * WEP key not to be installed as default key. * %KEY_FLAG_GROUP_RX_TX_DEFAULT * Default WEP or WPA-NONE key. * %KEY_FLAG_GROUP_RX * GTK key valid for RX only. * %KEY_FLAG_GROUP_TX_DEFAULT * GTK key valid for TX only, immediately taking over TX. * %KEY_FLAG_PAIRWISE_RX_TX * Pairwise key immediately becoming the active pairwise key. * %KEY_FLAG_PAIRWISE_RX * Pairwise key not yet valid for TX. (Only usable when Extended * Key ID is supported by the driver.) * %KEY_FLAG_PAIRWISE_RX_TX_MODIFY * Enable TX for a pairwise key installed with * KEY_FLAG_PAIRWISE_RX. * * Not a valid standalone key type but pre-defined to be combined * with other key_flags: * %KEY_FLAG_RX_TX * RX/TX key. */ enum key_flag key_flag; }; enum wpa_driver_if_type { /** * WPA_IF_STATION - Station mode interface */ WPA_IF_STATION, /** * WPA_IF_AP_VLAN - AP mode VLAN interface * * This interface shares its address and Beacon frame with the main * BSS. */ WPA_IF_AP_VLAN, /** * WPA_IF_AP_BSS - AP mode BSS interface * * This interface has its own address and Beacon frame. */ WPA_IF_AP_BSS, /** * WPA_IF_P2P_GO - P2P Group Owner */ WPA_IF_P2P_GO, /** * WPA_IF_P2P_CLIENT - P2P Client */ WPA_IF_P2P_CLIENT, /** * WPA_IF_P2P_GROUP - P2P Group interface (will become either * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) */ WPA_IF_P2P_GROUP, /** * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the * abstracted P2P Device function in the driver */ WPA_IF_P2P_DEVICE, /* * WPA_IF_MESH - Mesh interface */ WPA_IF_MESH, /* * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only) */ WPA_IF_TDLS, /* * WPA_IF_IBSS - IBSS interface (used for pref freq only) */ WPA_IF_IBSS, /* * WPA_IF_NAN - NAN Device */ WPA_IF_NAN, /* keep last */ WPA_IF_MAX }; /** * struct wpa_driver_capa - Driver capability information */ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008 #define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 #define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200 #define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400 #define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800 #define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000 #define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000 #define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000 #define WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256 0x00020000 #define WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 0x00040000 #define WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE 0x00080000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE 0x00100000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384 0x00200000 #define WPA_DRIVER_CAPA_KEY_MGMT_CCKM 0x00400000 #define WPA_DRIVER_CAPA_KEY_MGMT_OSEN 0x00800000 /** Bitfield of supported key management suites */ unsigned int key_mgmt; unsigned int key_mgmt_iftype[WPA_IF_MAX]; #define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 #define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 #define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 #define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 #define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 #define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 #define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040 #define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080 #define WPA_DRIVER_CAPA_ENC_BIP 0x00000100 #define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200 #define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400 #define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800 #define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000 /** Bitfield of supported cipher suites */ unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 #define WPA_DRIVER_AUTH_SHARED 0x00000002 #define WPA_DRIVER_AUTH_LEAP 0x00000004 /** Bitfield of supported IEEE 802.11 authentication algorithms */ unsigned int auth; /** Driver generated WPA/RSN IE */ #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 /** Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 /** Driver takes care of all DFS operations */ #define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004 /** Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using key_flag = KEY_FLAG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008 /** Driver is for a wired Ethernet interface */ #define WPA_DRIVER_FLAGS_WIRED 0x00000010 /** Driver provides separate commands for authentication and association (SME in * wpa_supplicant). */ #define WPA_DRIVER_FLAGS_SME 0x00000020 /** Driver supports AP mode */ #define WPA_DRIVER_FLAGS_AP 0x00000040 /** Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 /** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */ #define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100 /** Driver supports concurrent P2P operations */ #define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 /** * Driver uses the initial interface as a dedicated management interface, i.e., * it cannot be used for P2P group operations or non-P2P purposes. */ #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 /** This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 /** Driver supports station and key removal when stopping an AP */ #define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 0x00001000 /** * Driver uses the initial interface for P2P management interface and non-P2P * purposes (e.g., connect to infra AP), but this interface cannot be used for * P2P group operations. */ #define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 /** * Driver is known to use sane error codes, i.e., when it indicates that * something (e.g., association) fails, there was indeed a failure and the * operation does not end up getting completed successfully later. */ #define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 /** Driver supports off-channel TX */ #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 /** Driver indicates TX status events for EAPOL Data frames */ #define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 /** Driver indicates TX status events for Deauth/Disassoc frames */ #define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 /** Driver supports roaming (BSS selection) in firmware */ #define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 /** Driver supports operating as a TDLS peer */ #define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 /** Driver requires external TDLS setup/teardown/discovery */ #define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 /** Driver indicates support for Probe Response offloading in AP mode */ #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 /** Driver supports U-APSD in AP mode */ #define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 /** Driver supports inactivity timer in AP mode */ #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 /** Driver expects user space implementation of MLME in AP mode */ #define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 /** Driver supports SAE with user space SME */ #define WPA_DRIVER_FLAGS_SAE 0x02000000 /** Driver makes use of OBSS scan mechanism in wpa_supplicant */ #define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 /** Driver supports IBSS (Ad-hoc) mode */ #define WPA_DRIVER_FLAGS_IBSS 0x08000000 /** Driver supports radar detection */ #define WPA_DRIVER_FLAGS_RADAR 0x10000000 /** Driver supports a dedicated interface for P2P Device */ #define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 /** Driver supports QoS Mapping */ #define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000 /** Driver supports CSA in AP mode */ #define WPA_DRIVER_FLAGS_AP_CSA 0x80000000 /** Driver supports mesh */ #define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL /** Driver support ACS offload */ #define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL /** Driver supports key management offload */ #define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL /** Driver supports TDLS channel switching */ #define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH 0x0000000800000000ULL /** Driver supports IBSS with HT datarates */ #define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL /** Driver supports IBSS with VHT datarates */ #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL /** Driver supports automatic band selection */ #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL /** Driver supports simultaneous off-channel operations */ #define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL /** Driver supports full AP client state */ #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL /** Driver supports P2P Listen offload */ #define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL /** Driver supports FILS */ #define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL /** Driver supports Beacon frame TX rate configuration (legacy rates) */ #define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL /** Driver supports Beacon frame TX rate configuration (HT rates) */ #define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL /** Driver supports Beacon frame TX rate configuration (VHT rates) */ #define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL /** Driver supports mgmt_tx with random TX address in non-connected state */ #define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL /** Driver supports mgmt_tx with random TX addr in connected state */ #define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL /** Driver supports better BSS reporting with sched_scan in connected mode */ #define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL /** Driver supports HE capabilities */ #define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL /** Driver supports FILS shared key offload */ #define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL /** Driver supports all OCE STA specific mandatory features */ #define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL /** Driver supports all OCE AP specific mandatory features */ #define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL /** * Driver supports all OCE STA-CFON specific mandatory features only. * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the * userspace shall assume that this driver may not support all OCE AP * functionality but can support only OCE STA-CFON functionality. */ #define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL /** Driver supports MFP-optional in the connect command */ #define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL /** Driver is a self-managed regulatory device */ #define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL /** Driver supports FTM responder functionality */ #define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL /** Driver support 4-way handshake offload for WPA-Personal */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL /** Driver supports a separate control port TX for EAPOL frames */ #define WPA_DRIVER_FLAGS_CONTROL_PORT 0x0400000000000000ULL /** Driver supports VLAN offload */ #define WPA_DRIVER_FLAGS_VLAN_OFFLOAD 0x0800000000000000ULL /** Driver supports UPDATE_FT_IES command */ #define WPA_DRIVER_FLAGS_UPDATE_FT_IES 0x1000000000000000ULL /** Driver can correctly rekey PTKs without Extended Key ID */ #define WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS 0x2000000000000000ULL /** Driver supports Beacon protection */ #define WPA_DRIVER_FLAGS_BEACON_PROTECTION 0x4000000000000000ULL /** Driver supports Extended Key ID */ #define WPA_DRIVER_FLAGS_EXTENDED_KEY_ID 0x8000000000000000ULL u64 flags; /** Driver supports a separate control port RX for EAPOL frames */ #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL /** Driver supports TX status reports for EAPOL frames through control port */ #define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL /** Driver supports secure LTF */ #define WPA_DRIVER_FLAGS2_SEC_LTF 0x0000000000000004ULL /** Driver supports secure RTT measurement exchange */ #define WPA_DRIVER_FLAGS2_SEC_RTT 0x0000000000000008ULL /** * Driver supports protection of range negotiation and measurement management * frames */ #define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG 0x0000000000000010ULL /** Driver supports Beacon frame TX rate configuration (HE rates) */ #define WPA_DRIVER_FLAGS2_BEACON_RATE_HE 0x0000000000000020ULL /** Driver supports Beacon protection only in client mode */ #define WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT 0x0000000000000040ULL /** Driver supports Operating Channel Validation */ #define WPA_DRIVER_FLAGS2_OCV 0x0000000000000080ULL /** Driver expects user space implementation of SME in AP mode */ #define WPA_DRIVER_FLAGS2_AP_SME 0x0000000000000100ULL u64 flags2; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE) unsigned int wmm_ac_supported:1; unsigned int mac_addr_rand_scan_supported:1; unsigned int mac_addr_rand_sched_scan_supported:1; /** Maximum number of supported active probe SSIDs */ int max_scan_ssids; /** Maximum number of supported active probe SSIDs for sched_scan */ int max_sched_scan_ssids; /** Maximum number of supported scan plans for scheduled scan */ unsigned int max_sched_scan_plans; /** Maximum interval in a scan plan. In seconds */ u32 max_sched_scan_plan_interval; /** Maximum number of iterations in a single scan plan */ u32 max_sched_scan_plan_iterations; /** Whether sched_scan (offloaded scanning) is supported */ int sched_scan_supported; /** Maximum number of supported match sets for sched_scan */ int max_match_sets; /** * max_remain_on_chan - Maximum remain-on-channel duration in msec */ unsigned int max_remain_on_chan; /** * max_stations - Maximum number of associated stations the driver * supports in AP mode */ unsigned int max_stations; /** * probe_resp_offloads - Bitmap of supported protocols by the driver * for Probe Response offloading. */ /** Driver Probe Response offloading support for WPS ver. 1 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 /** Driver Probe Response offloading support for WPS ver. 2 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 /** Driver Probe Response offloading support for P2P */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 /** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 unsigned int probe_resp_offloads; unsigned int max_acl_mac_addrs; /** * Number of supported concurrent channels */ unsigned int num_multichan_concurrent; /** * extended_capa - extended capabilities in driver/device * * Must be allocated and freed by driver and the pointers must be * valid for the lifetime of the driver, i.e., freed in deinit() */ const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; struct wowlan_triggers wowlan_triggers; /** Driver adds the DS Params Set IE in Probe Request frames */ #define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES 0x00000001 /** Driver adds the WFA TPC IE in Probe Request frames */ #define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES 0x00000002 /** Driver handles quiet period requests */ #define WPA_DRIVER_FLAGS_QUIET 0x00000004 /** * Driver is capable of inserting the current TX power value into the body of * transmitted frames. * Background: Some Action frames include a TPC Report IE. This IE contains a * TX power field, which has to be updated by lower layers. One such Action * frame is Link Measurement Report (part of RRM). Another is TPC Report (part * of spectrum management). Note that this insertion takes place at a fixed * offset, namely the 6th byte in the Action frame body. */ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 /** * Driver supports RRM. With this support, the driver will accept to use RRM in * (Re)Association Request frames, without supporting quiet period. */ #define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010 /** Driver supports setting the scan dwell time */ #define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020 /** Driver supports Beacon Report Measurement */ #define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040 u32 rrm_flags; /* Driver concurrency capabilities */ unsigned int conc_capab; /* Maximum number of concurrent channels on 2.4 GHz */ unsigned int max_conc_chan_2_4; /* Maximum number of concurrent channels on 5 GHz */ unsigned int max_conc_chan_5_0; /* Maximum number of supported CSA counters */ u16 max_csa_counters; }; struct hostapd_data; #define STA_DRV_DATA_TX_MCS BIT(0) #define STA_DRV_DATA_RX_MCS BIT(1) #define STA_DRV_DATA_TX_VHT_MCS BIT(2) #define STA_DRV_DATA_RX_VHT_MCS BIT(3) #define STA_DRV_DATA_TX_VHT_NSS BIT(4) #define STA_DRV_DATA_RX_VHT_NSS BIT(5) #define STA_DRV_DATA_TX_SHORT_GI BIT(6) #define STA_DRV_DATA_RX_SHORT_GI BIT(7) #define STA_DRV_DATA_LAST_ACK_RSSI BIT(8) struct hostap_sta_driver_data { unsigned long rx_packets, tx_packets; unsigned long long rx_bytes, tx_bytes; unsigned long long rx_airtime, tx_airtime; int bytes_64bit; /* whether 64-bit byte counters are supported */ unsigned long current_tx_rate; unsigned long current_rx_rate; unsigned long inactive_msec; unsigned long flags; /* bitfield of STA_DRV_DATA_* */ unsigned long num_ps_buf_frames; unsigned long tx_retry_failed; unsigned long tx_retry_count; s8 last_ack_rssi; unsigned long backlog_packets; unsigned long backlog_bytes; s8 signal; u8 rx_vhtmcs; u8 tx_vhtmcs; u8 rx_mcs; u8 tx_mcs; u8 rx_vht_nss; u8 tx_vht_nss; }; struct hostapd_sta_add_params { const u8 *addr; u16 aid; u16 capability; const u8 *supp_rates; size_t supp_rates_len; u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; const struct ieee80211_vht_capabilities *vht_capabilities; int vht_opmode_enabled; u8 vht_opmode; const struct ieee80211_he_capabilities *he_capab; size_t he_capab_len; const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab; u32 flags; /* bitmask of WPA_STA_* flags */ u32 flags_mask; /* unset bits in flags */ #ifdef CONFIG_MESH enum mesh_plink_state plink_state; u16 peer_aid; #endif /* CONFIG_MESH */ int set; /* Set STA parameters instead of add */ u8 qosinfo; const u8 *ext_capab; size_t ext_capab_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; size_t supp_oper_classes_len; int support_p2p_ps; }; struct mac_address { u8 addr[ETH_ALEN]; }; struct hostapd_acl_params { u8 acl_policy; unsigned int num_mac_acl; struct mac_address mac_acl[0]; }; struct wpa_init_params { void *global_priv; const u8 *bssid; const char *ifname; const char *driver_params; int use_pae_group_addr; char **bridge; size_t num_bridge; u8 *own_addr; /* buffer for writing own MAC address */ }; struct wpa_bss_params { /** Interface name (for multi-SSID/VLAN support) */ const char *ifname; /** Whether IEEE 802.1X or WPA/WPA2 is enabled */ int enabled; int wpa; int ieee802_1x; int wpa_group; int wpa_pairwise; int wpa_key_mgmt; int rsn_preauth; enum mfp_options ieee80211w; }; #define WPA_STA_AUTHORIZED BIT(0) #define WPA_STA_WMM BIT(1) #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) #define WPA_STA_AUTHENTICATED BIT(5) #define WPA_STA_ASSOCIATED BIT(6) enum tdls_oper { TDLS_DISCOVERY_REQ, TDLS_SETUP, TDLS_TEARDOWN, TDLS_ENABLE_LINK, TDLS_DISABLE_LINK, TDLS_ENABLE, TDLS_DISABLE }; enum wnm_oper { WNM_SLEEP_ENTER_CONFIRM, WNM_SLEEP_ENTER_FAIL, WNM_SLEEP_EXIT_CONFIRM, WNM_SLEEP_EXIT_FAIL, WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for * a STA */ WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE * for a STA */ WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE * for a STA */ WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ }; /* enum smps_mode - SMPS mode definitions */ enum smps_mode { SMPS_AUTOMATIC, SMPS_OFF, SMPS_DYNAMIC, SMPS_STATIC, /* Keep last */ SMPS_INVALID, }; #define WPA_INVALID_NOISE 9999 /** * struct wpa_signal_info - Information about channel signal quality * @frequency: control frequency * @above_threshold: true if the above threshold was crossed * (relevant for a CQM event) * @current_signal: in dBm * @avg_signal: in dBm * @avg_beacon_signal: in dBm * @current_noise: %WPA_INVALID_NOISE if not supported * @current_txrate: current TX rate * @chanwidth: channel width * @center_frq1: center frequency for the first segment * @center_frq2: center frequency for the second segment (if relevant) */ struct wpa_signal_info { u32 frequency; int above_threshold; int current_signal; int avg_signal; int avg_beacon_signal; int current_noise; int current_txrate; enum chan_width chanwidth; int center_frq1; int center_frq2; }; /** * struct wpa_channel_info - Information about the current channel * @frequency: Center frequency of the primary 20 MHz channel * @chanwidth: Width of the current operating channel * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1). * This field is only filled in when using a 40 MHz channel. * @center_frq1: Center frequency of frequency segment 0 * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels) * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is * derived from center_frq2 for convenience. */ struct wpa_channel_info { u32 frequency; enum chan_width chanwidth; int sec_channel; int center_frq1; int center_frq2; u8 seg1_idx; }; /** * struct beacon_data - Beacon data * @head: Head portion of Beacon frame (before TIM IE) * @tail: Tail portion of Beacon frame (after TIM IE) * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL * @proberesp_ies: Extra information element(s) to add into Probe Response * frames or %NULL * @assocresp_ies: Extra information element(s) to add into (Re)Association * Response frames or %NULL * @probe_resp: Probe Response frame template * @head_len: Length of @head * @tail_len: Length of @tail * @beacon_ies_len: Length of beacon_ies in octets * @proberesp_ies_len: Length of proberesp_ies in octets * @proberesp_ies_len: Length of proberesp_ies in octets * @probe_resp_len: Length of probe response template (@probe_resp) */ struct beacon_data { u8 *head, *tail; u8 *beacon_ies; u8 *proberesp_ies; u8 *assocresp_ies; u8 *probe_resp; size_t head_len, tail_len; size_t beacon_ies_len; size_t proberesp_ies_len; size_t assocresp_ies_len; size_t probe_resp_len; }; /** * struct csa_settings - Settings for channel switch command * @cs_count: Count in Beacon frames (TBTT) to perform the switch * @block_tx: 1 - block transmission for CSA period * @freq_params: Next channel frequency parameter * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period * @beacon_after: Next beacon/probe resp/asooc resp info * @counter_offset_beacon: Offset to the count field in beacon's tail * @counter_offset_presp: Offset to the count field in probe resp. */ struct csa_settings { u8 cs_count; u8 block_tx; struct hostapd_freq_params freq_params; struct beacon_data beacon_csa; struct beacon_data beacon_after; u16 counter_offset_beacon[2]; u16 counter_offset_presp[2]; }; /* TDLS peer capabilities for send_tdls_mgmt() */ enum tdls_peer_capability { TDLS_PEER_HT = BIT(0), TDLS_PEER_VHT = BIT(1), TDLS_PEER_WMM = BIT(2), TDLS_PEER_HE = BIT(3), }; /* valid info in the wmm_params struct */ enum wmm_params_valid_info { WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0), }; /** * struct wmm_params - WMM parameterss configured for this association * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields * of the struct contain valid information. * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if * %WMM_PARAMS_UAPSD_QUEUES_INFO is set) */ struct wmm_params { u8 info_bitmap; u8 uapsd_queues; }; #ifdef CONFIG_MACSEC struct macsec_init_params { bool always_include_sci; bool use_es; bool use_scb; }; #endif /* CONFIG_MACSEC */ enum drv_br_port_attr { DRV_BR_PORT_ATTR_PROXYARP, DRV_BR_PORT_ATTR_HAIRPIN_MODE, }; enum drv_br_net_param { DRV_BR_NET_PARAM_GARP_ACCEPT, DRV_BR_MULTICAST_SNOOPING, }; struct drv_acs_params { /* Selected mode (HOSTAPD_MODE_*) */ enum hostapd_hw_mode hw_mode; /* Indicates whether HT is enabled */ int ht_enabled; /* Indicates whether HT40 is enabled */ int ht40_enabled; /* Indicates whether VHT is enabled */ int vht_enabled; /* Configured ACS channel width */ u16 ch_width; /* ACS frequency list info */ const int *freq_list; /* Indicates whether EDMG is enabled */ int edmg_enabled; }; struct wpa_bss_trans_info { u8 mbo_transition_reason; u8 n_candidates; u8 *bssid; }; struct wpa_bss_candidate_info { u8 num; struct candidate_list { u8 bssid[ETH_ALEN]; u8 is_accept; u32 reject_reason; } *candidates; }; struct wpa_pmkid_params { const u8 *bssid; const u8 *ssid; size_t ssid_len; const u8 *fils_cache_id; const u8 *pmkid; const u8 *pmk; size_t pmk_len; u32 pmk_lifetime; u8 pmk_reauth_threshold; }; /* Mask used to specify which connection parameters have to be updated */ enum wpa_drv_update_connect_params_mask { WPA_DRV_UPDATE_ASSOC_IES = BIT(0), WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1), WPA_DRV_UPDATE_AUTH_TYPE = BIT(2), }; /** * struct external_auth - External authentication trigger parameters * * These are used across the external authentication request and event * interfaces. * @action: Action type / trigger for external authentication. Only significant * for the event interface. * @bssid: BSSID of the peer with which the authentication has to happen. Used * by both the request and event interface. * @ssid: SSID of the AP. Used by both the request and event interface. * @ssid_len: SSID length in octets. * @key_mgmt_suite: AKM suite of the respective authentication. Optional for * the request interface. * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication, * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give * the real status code for failures. Used only for the request interface * from user space to the driver. * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE). */ struct external_auth { enum { EXT_AUTH_START, EXT_AUTH_ABORT, } action; const u8 *bssid; const u8 *ssid; size_t ssid_len; unsigned int key_mgmt_suite; u16 status; const u8 *pmkid; }; /* enum nested_attr - Used to specify if subcommand uses nested attributes */ enum nested_attr { NESTED_ATTR_NOT_USED = 0, NESTED_ATTR_USED = 1, NESTED_ATTR_UNSPECIFIED = 2, }; /** * struct wpa_driver_ops - Driver interface API definition * * This structure defines the API that each driver interface needs to implement * for core wpa_supplicant code. All driver specific functionality is captured * in this wrapper. */ struct wpa_driver_ops { /** Name of the driver interface */ const char *name; /** One line description of the driver interface */ const char *desc; /** * get_bssid - Get the current BSSID * @priv: private driver interface data * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) * * Returns: 0 on success, -1 on failure * * Query kernel driver for the current BSSID and copy it to bssid. * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not * associated. */ int (*get_bssid)(void *priv, u8 *bssid); /** * get_ssid - Get the current SSID * @priv: private driver interface data * @ssid: buffer for SSID (at least 32 bytes) * * Returns: Length of the SSID on success, -1 on failure * * Query kernel driver for the current SSID and copy it to ssid. * Returning zero is recommended if the STA is not associated. * * Note: SSID is an array of octets, i.e., it is not nul terminated and * can, at least in theory, contain control characters (including nul) * and as such, should be processed as binary data, not a printable * string. */ int (*get_ssid)(void *priv, u8 *ssid); /** * set_key - Configure encryption key * @priv: private driver interface data * @params: Key parameters * Returns: 0 on success, -1 on failure * * Configure the given key for the kernel driver. If the driver * supports separate individual keys (4 default keys + 1 individual), * addr can be used to determine whether the key is default or * individual. If only 4 keys are supported, the default key with key * index 0 is used as the individual key. STA must be configured to use * it as the default Tx key (set_tx is set) and accept Rx for all the * key indexes. In most cases, WPA uses only key indexes 1 and 2 for * broadcast keys, so key index 0 is available for this kind of * configuration. * * Please note that TKIP keys include separate TX and RX MIC keys and * some drivers may expect them in different order than wpa_supplicant * is using. If the TX/RX keys are swapped, all TKIP encrypted packets * will trigger Michael MIC errors. This can be fixed by changing the * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key * in driver_*.c set_key() implementation, see driver_ndis.c for an * example on how this can be done. */ int (*set_key)(void *priv, struct wpa_driver_set_key_params *params); /** * init - Initialize driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * * Returns: Pointer to private data, %NULL on failure * * Initialize driver interface, including event processing for kernel * driver events (e.g., associated, scan results, Michael MIC failure). * This function can allocate a private configuration data area for * @ctx, file descriptor, interface name, etc. information that may be * needed in future driver operations. If this is not used, non-NULL * value will need to be returned because %NULL is used to indicate * failure. The returned value will be used as 'void *priv' data for * all other driver_ops functions. * * The main event loop (eloop.c) of wpa_supplicant can be used to * register callback for read sockets (eloop_register_read_sock()). * * See below for more information about events and * wpa_supplicant_event() function. */ void * (*init)(void *ctx, const char *ifname); /** * deinit - Deinitialize driver interface * @priv: private driver interface data from init() * * Shut down driver interface and processing of driver events. Free * private data buffer if one was allocated in init() handler. */ void (*deinit)(void *priv); /** * set_param - Set driver configuration parameters * @priv: private driver interface data from init() * @param: driver specific configuration parameters * * Returns: 0 on success, -1 on failure * * Optional handler for notifying driver interface about configuration * parameters (driver_param). */ int (*set_param)(void *priv, const char *param); /** * set_countermeasures - Enable/disable TKIP countermeasures * @priv: private driver interface data * @enabled: 1 = countermeasures enabled, 0 = disabled * * Returns: 0 on success, -1 on failure * * Configure TKIP countermeasures. When these are enabled, the driver * should drop all received and queued frames that are using TKIP. */ int (*set_countermeasures)(void *priv, int enabled); /** * deauthenticate - Request driver to deauthenticate * @priv: private driver interface data * @addr: peer address (BSSID of the AP) * @reason_code: 16-bit reason code to be sent in the deauthentication * frame * * Returns: 0 on success, -1 on failure */ int (*deauthenticate)(void *priv, const u8 *addr, u16 reason_code); /** * associate - Request driver to associate * @priv: private driver interface data * @params: association parameters * * Returns: 0 on success, -1 on failure */ int (*associate)(void *priv, struct wpa_driver_associate_params *params); /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data * @params: PMKSA parameters * * Returns: 0 on success, -1 on failure * * This function is called when a new PMK is received, as a result of * either normal authentication or RSN pre-authentication. The PMKSA * parameters are either a set of bssid, pmkid, and pmk; or a set of * ssid, fils_cache_id, pmkid, and pmk. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), add_pmkid() can be used to add new PMKSA cache entries * in the driver. If the driver uses wpa_ie from wpa_supplicant, this * driver_ops function does not need to be implemented. Likewise, if * the driver does not support WPA, this function is not needed. */ int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params); /** * remove_pmkid - Remove PMKSA cache entry to the driver * @priv: private driver interface data * @params: PMKSA parameters * * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops a PMKSA cache * entry for any reason. The PMKSA parameters are either a set of * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches * between the driver and wpa_supplicant. If the driver uses wpa_ie * from wpa_supplicant, this driver_ops function does not need to be * implemented. Likewise, if the driver does not support WPA, this * function is not needed. */ int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params); /** * flush_pmkid - Flush PMKSA cache * @priv: private driver interface data * * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops all PMKSA cache * entries for any reason. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches * between the driver and wpa_supplicant. If the driver uses wpa_ie * from wpa_supplicant, this driver_ops function does not need to be * implemented. Likewise, if the driver does not support WPA, this * function is not needed. */ int (*flush_pmkid)(void *priv); /** * get_capa - Get driver capabilities * @priv: private driver interface data * * Returns: 0 on success, -1 on failure * * Get driver/firmware/hardware capabilities. */ int (*get_capa)(void *priv, struct wpa_driver_capa *capa); /** * poll - Poll driver for association information * @priv: private driver interface data * * This is an option callback that can be used when the driver does not * provide event mechanism for association events. This is called when * receiving WPA EAPOL-Key messages that require association * information. The driver interface is supposed to generate associnfo * event before returning from this callback function. In addition, the * driver interface should generate an association event after having * sent out associnfo. */ void (*poll)(void *priv); /** * get_ifindex - Get interface index * @priv: private driver interface data * * Returns: Interface index */ unsigned int (*get_ifindex)(void *priv); /** * get_ifname - Get interface name * @priv: private driver interface data * * Returns: Pointer to the interface name. This can differ from the * interface name used in init() call. Init() is called first. * * This optional function can be used to allow the driver interface to * replace the interface name with something else, e.g., based on an * interface mapping from a more descriptive name. */ const char * (*get_ifname)(void *priv); /** * get_mac_addr - Get own MAC address * @priv: private driver interface data * * Returns: Pointer to own MAC address or %NULL on failure * * This optional function can be used to get the own MAC address of the * device from the driver interface code. This is only needed if the * l2_packet implementation for the OS does not provide easy access to * a MAC address. */ const u8 * (*get_mac_addr)(void *priv); /** * set_operstate - Sets device operating state to DORMANT or UP * @priv: private driver interface data * @state: 0 = dormant, 1 = up * Returns: 0 on success, -1 on failure * * This is an optional function that can be used on operating systems * that support a concept of controlling network device state from user * space applications. This function, if set, gets called with * state = 1 when authentication has been completed and with state = 0 * when connection is lost. */ int (*set_operstate)(void *priv, int state); /** * mlme_setprotection - MLME-SETPROTECTION.request primitive * @priv: Private driver interface data * @addr: Address of the station for which to set protection (may be * %NULL for group keys) * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_* * @key_type: MLME_SETPROTECTION_KEY_TYPE_* * Returns: 0 on success, -1 on failure * * This is an optional function that can be used to set the driver to * require protection for Tx and/or Rx frames. This uses the layer * interface defined in IEEE 802.11i-2004 clause 10.3.22.1 * (MLME-SETPROTECTION.request). Many drivers do not use explicit * set protection operation; instead, they set protection implicitly * based on configured keys. */ int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type, int key_type); /** * get_hw_feature_data - Get hardware support data (channels and rates) * @priv: Private driver interface data * @num_modes: Variable for returning the number of returned modes * flags: Variable for returning hardware feature flags * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*) * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, u16 *flags, u8 *dfs); /** * send_mlme - Send management frame from MLME * @priv: Private driver interface data * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame * @noack: Do not wait for this frame to be acked (disable retries) * @freq: Frequency (in MHz) to send the frame on, or 0 to let the * driver decide * @csa_offs: Array of CSA offsets or %NULL * @csa_offs_len: Number of elements in csa_offs * @no_encrypt: Do not encrypt frame even if appropriate key exists * (used only for testing purposes) * @wait: Time to wait off-channel for a response (in ms), or zero * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, int noack, unsigned int freq, const u16 *csa_offs, size_t csa_offs_len, int no_encrypt, unsigned int wait); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs * @priv: Private driver interface data * @md: Mobility domain (2 octets) (also included inside ies) * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs * @ies_len: Length of FT IEs in bytes * Returns: 0 on success, -1 on failure * * The supplicant uses this callback to let the driver know that keying * material for FT is available and that the driver can use the * provided IEs in the next message in FT authentication sequence. * * This function is only needed for driver that support IEEE 802.11r * (Fast BSS Transition). */ int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies, size_t ies_len); /** * get_scan_results2 - Fetch the latest scan results * @priv: private driver interface data * * Returns: Allocated buffer of scan results (caller is responsible for * freeing the data structure) on success, NULL on failure */ struct wpa_scan_results * (*get_scan_results2)(void *priv); /** * set_country - Set country * @priv: Private driver interface data * @alpha2: country to which to switch to * Returns: 0 on success, -1 on failure * * This function is for drivers which support some form * of setting a regulatory domain. */ int (*set_country)(void *priv, const char *alpha2); /** * get_country - Get country * @priv: Private driver interface data * @alpha2: Buffer for returning country code (at least 3 octets) * Returns: 0 on success, -1 on failure */ int (*get_country)(void *priv, char *alpha2); /** * global_init - Global driver initialization * @ctx: wpa_global pointer * Returns: Pointer to private data (global), %NULL on failure * * This optional function is called to initialize the driver wrapper * for global data, i.e., data that applies to all interfaces. If this * function is implemented, global_deinit() will also need to be * implemented to free the private data. The driver will also likely * use init2() function instead of init() to get the pointer to global * data available to per-interface initializer. */ void * (*global_init)(void *ctx); /** * global_deinit - Global driver deinitialization * @priv: private driver global data from global_init() * * Terminate any global driver related functionality and free the * global data structure. */ void (*global_deinit)(void *priv); /** * init2 - Initialize driver interface (with global data) * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * @global_priv: private driver global data from global_init() * Returns: Pointer to private data, %NULL on failure * * This function can be used instead of init() if the driver wrapper * uses global data. */ void * (*init2)(void *ctx, const char *ifname, void *global_priv); /** * get_interfaces - Get information about available interfaces * @global_priv: private driver global data from global_init() * Returns: Allocated buffer of interface information (caller is * responsible for freeing the data structure) on success, NULL on * failure */ struct wpa_interface_info * (*get_interfaces)(void *global_priv); /** * scan2 - Request the driver to initiate scan * @priv: private driver interface data * @params: Scan parameters * * Returns: 0 on success, -1 on failure * * Once the scan results are ready, the driver should report scan * results event for wpa_supplicant which will eventually request the * results with wpa_driver_get_scan_results2(). */ int (*scan2)(void *priv, struct wpa_driver_scan_params *params); /** * authenticate - Request driver to authenticate * @priv: private driver interface data * @params: authentication parameters * Returns: 0 on success, -1 on failure * * This is an optional function that can be used with drivers that * support separate authentication and association steps, i.e., when * wpa_supplicant can act as the SME. If not implemented, associate() * function is expected to take care of IEEE 802.11 authentication, * too. */ int (*authenticate)(void *priv, struct wpa_driver_auth_params *params); /** * set_ap - Set Beacon and Probe Response information for AP mode * @priv: Private driver interface data * @params: Parameters to use in AP mode * * This function is used to configure Beacon template and/or extra IEs * to add for Beacon and Probe Response frames for the driver in * AP mode. The driver is responsible for building the full Beacon * frame by concatenating the head part with TIM IE generated by the * driver/firmware and finishing with the tail part. Depending on the * driver architectue, this can be done either by using the full * template or the set of additional IEs (e.g., WPS and P2P IE). * Similarly, Probe Response processing depends on the driver design. * If the driver (or firmware) takes care of replying to Probe Request * frames, the extra IEs provided here needs to be added to the Probe * Response frames. * * Returns: 0 on success, -1 on failure */ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** * set_acl - Set ACL in AP mode * @priv: Private driver interface data * @params: Parameters to configure ACL * Returns: 0 on success, -1 on failure * * This is used only for the drivers which support MAC address ACL. */ int (*set_acl)(void *priv, struct hostapd_acl_params *params); /** * hapd_init - Initialize driver interface (hostapd only) * @hapd: Pointer to hostapd context * @params: Configuration for the driver wrapper * Returns: Pointer to private data, %NULL on failure * * This function is used instead of init() or init2() when the driver * wrapper is used with hostapd. */ void * (*hapd_init)(struct hostapd_data *hapd, struct wpa_init_params *params); /** * hapd_deinit - Deinitialize driver interface (hostapd only) * @priv: Private driver interface data from hapd_init() */ void (*hapd_deinit)(void *priv); /** * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only) * @priv: Private driver interface data * @params: BSS parameters * Returns: 0 on success, -1 on failure * * This is an optional function to configure the kernel driver to * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This * can be left undefined (set to %NULL) if IEEE 802.1X support is * always enabled and the driver uses set_ap() to set WPA/RSN IE * for Beacon frames. * * DEPRECATED - use set_ap() instead */ int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); /** * set_privacy - Enable/disable privacy (AP only) * @priv: Private driver interface data * @enabled: 1 = privacy enabled, 0 = disabled * Returns: 0 on success, -1 on failure * * This is an optional function to configure privacy field in the * kernel driver for Beacon frames. This can be left undefined (set to * %NULL) if the driver uses the Beacon template from set_ap(). * * DEPRECATED - use set_ap() instead */ int (*set_privacy)(void *priv, int enabled); /** * get_seqnum - Fetch the current TSC/packet number (AP only) * @ifname: The interface name (main or virtual) * @priv: Private driver interface data * @addr: MAC address of the station or %NULL for group keys * @idx: Key index * @seq: Buffer for returning the latest used TSC/packet number * Returns: 0 on success, -1 on failure * * This function is used to fetch the last used TSC/packet number for * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group * keys, so there is no strict requirement on implementing support for * unicast keys (i.e., addr != %NULL). */ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq); /** * flush - Flush all association stations (AP only) * @priv: Private driver interface data * Returns: 0 on success, -1 on failure * * This function requests the driver to disassociate all associated * stations. This function does not need to be implemented if the * driver does not process association frames internally. */ int (*flush)(void *priv); /** * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP) * @priv: Private driver interface data * @elem: Information elements * @elem_len: Length of the elem buffer in octets * Returns: 0 on success, -1 on failure * * This is an optional function to add information elements in the * kernel driver for Beacon and Probe Response frames. This can be left * undefined (set to %NULL) if the driver uses the Beacon template from * set_ap(). * * DEPRECATED - use set_ap() instead */ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); /** * read_sta_data - Fetch station data * @priv: Private driver interface data * @data: Buffer for returning station information * @addr: MAC address of the station * Returns: 0 on success, -1 on failure */ int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, const u8 *addr); /** * tx_control_port - Send a frame over the 802.1X controlled port * @priv: Private driver interface data * @dest: Destination MAC address * @proto: Ethertype in host byte order * @buf: Frame payload starting from IEEE 802.1X header * @len: Frame payload length * @no_encrypt: Do not encrypt frame * * Returns 0 on success, else an error * * This is like a normal Ethernet send except that the driver is aware * (by other means than the Ethertype) that this frame is special, * and more importantly it gains an ordering between the transmission of * the frame and other driver management operations such as key * installations. This can be used to work around known limitations in * IEEE 802.11 protocols such as race conditions between rekeying 4-way * handshake message 4/4 and a PTK being overwritten. * * This function is only used for a given interface if the driver * instance reports WPA_DRIVER_FLAGS_CONTROL_PORT capability. Otherwise, * API users will fall back to sending the frame via a normal socket. */ int (*tx_control_port)(void *priv, const u8 *dest, u16 proto, const u8 *buf, size_t len, int no_encrypt); /** * hapd_send_eapol - Send an EAPOL packet (AP only) * @priv: private driver interface data * @addr: Destination MAC address * @data: EAPOL packet starting with IEEE 802.1X header * @data_len: Length of the EAPOL packet in octets * @encrypt: Whether the frame should be encrypted * @own_addr: Source MAC address * @flags: WPA_STA_* flags for the destination station * * Returns: 0 on success, -1 on failure */ int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags); /** * sta_deauth - Deauthenticate a station (AP only) * @priv: Private driver interface data * @own_addr: Source address and BSSID for the Deauthentication frame * @addr: MAC address of the station to deauthenticate * @reason: Reason code for the Deauthentiation frame * Returns: 0 on success, -1 on failure * * This function requests a specific station to be deauthenticated and * a Deauthentication frame to be sent to it. */ int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr, u16 reason); /** * sta_disassoc - Disassociate a station (AP only) * @priv: Private driver interface data * @own_addr: Source address and BSSID for the Disassociation frame * @addr: MAC address of the station to disassociate * @reason: Reason code for the Disassociation frame * Returns: 0 on success, -1 on failure * * This function requests a specific station to be disassociated and * a Disassociation frame to be sent to it. */ int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, u16 reason); /** * sta_remove - Remove a station entry (AP only) * @priv: Private driver interface data * @addr: MAC address of the station to be removed * Returns: 0 on success, -1 on failure */ int (*sta_remove)(void *priv, const u8 *addr); /** * hapd_get_ssid - Get the current SSID (AP only) * @priv: Private driver interface data * @buf: Buffer for returning the SSID * @len: Maximum length of the buffer * Returns: Length of the SSID on success, -1 on failure * * This function need not be implemented if the driver uses Beacon * template from set_ap() and does not reply to Probe Request frames. */ int (*hapd_get_ssid)(void *priv, u8 *buf, int len); /** * hapd_set_ssid - Set SSID (AP only) * @priv: Private driver interface data * @buf: SSID * @len: Length of the SSID in octets * Returns: 0 on success, -1 on failure * * DEPRECATED - use set_ap() instead */ int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); /** * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP) * @priv: Private driver interface data * @enabled: 1 = countermeasures enabled, 0 = disabled * Returns: 0 on success, -1 on failure * * This need not be implemented if the driver does not take care of * association processing. */ int (*hapd_set_countermeasures)(void *priv, int enabled); /** * sta_add - Add a station entry * @priv: Private driver interface data * @params: Station parameters * Returns: 0 on success, -1 on failure * * This function is used to add or set (params->set 1) a station * entry in the driver. Adding STA entries is used only if the driver * does not take care of association processing. * * With drivers that don't support full AP client state, this function * is used to add a station entry to the driver once the station has * completed association. * * With TDLS, this function is used to add or set (params->set 1) * TDLS peer entries (even with drivers that do not support full AP * client state). */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); /** * get_inact_sec - Get station inactivity duration (AP only) * @priv: Private driver interface data * @addr: Station address * Returns: Number of seconds station has been inactive, -1 on failure */ int (*get_inact_sec)(void *priv, const u8 *addr); /** * sta_clear_stats - Clear station statistics (AP only) * @priv: Private driver interface data * @addr: Station address * Returns: 0 on success, -1 on failure */ int (*sta_clear_stats)(void *priv, const u8 *addr); /** * set_freq - Set channel/frequency (AP only) * @priv: Private driver interface data * @freq: Channel parameters * Returns: 0 on success, -1 on failure */ int (*set_freq)(void *priv, struct hostapd_freq_params *freq); /** * set_rts - Set RTS threshold * @priv: Private driver interface data * @rts: RTS threshold in octets * Returns: 0 on success, -1 on failure */ int (*set_rts)(void *priv, int rts); /** * set_frag - Set fragmentation threshold * @priv: Private driver interface data * @frag: Fragmentation threshold in octets * Returns: 0 on success, -1 on failure */ int (*set_frag)(void *priv, int frag); /** * sta_set_flags - Set station flags (AP only) * @priv: Private driver interface data * @addr: Station address * @total_flags: Bitmap of all WPA_STA_* flags currently set * @flags_or: Bitmap of WPA_STA_* flags to add * @flags_and: Bitmap of WPA_STA_* flags to us as a mask * Returns: 0 on success, -1 on failure */ int (*sta_set_flags)(void *priv, const u8 *addr, unsigned int total_flags, unsigned int flags_or, unsigned int flags_and); /** * sta_set_airtime_weight - Set station airtime weight (AP only) * @priv: Private driver interface data * @addr: Station address * @weight: New weight for station airtime assignment * Returns: 0 on success, -1 on failure */ int (*sta_set_airtime_weight)(void *priv, const u8 *addr, unsigned int weight); /** * set_tx_queue_params - Set TX queue parameters * @priv: Private driver interface data * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) * @aifs: AIFS * @cw_min: cwMin * @cw_max: cwMax * @burst_time: Maximum length for bursting in 0.1 msec units */ int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, int cw_max, int burst_time); /** * if_add - Add a virtual interface * @priv: Private driver interface data * @type: Interface type * @ifname: Interface name for the new virtual interface * @addr: Local address to use for the interface or %NULL to use the * parent interface address * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces * @drv_priv: Pointer for overwriting the driver context or %NULL if * not allowed (applies only to %WPA_IF_AP_BSS type) * @force_ifname: Buffer for returning an interface name that the * driver ended up using if it differs from the requested ifname * @if_addr: Buffer for returning the allocated interface address * (this may differ from the requested addr if the driver cannot * change interface address) * @bridge: Bridge interface to use or %NULL if no bridge configured * @use_existing: Whether to allow existing interface to be used * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge, int use_existing, int setup_ap); /** * if_remove - Remove a virtual interface * @priv: Private driver interface data * @type: Interface type * @ifname: Interface name of the virtual interface to be removed * Returns: 0 on success, -1 on failure */ int (*if_remove)(void *priv, enum wpa_driver_if_type type, const char *ifname); /** * set_sta_vlan - Bind a station into a specific interface (AP only) * @priv: Private driver interface data * @ifname: Interface (main or virtual BSS or VLAN) * @addr: MAC address of the associated station * @vlan_id: VLAN ID * Returns: 0 on success, -1 on failure * * This function is used to bind a station to a specific virtual * interface. It is only used if when virtual interfaces are supported, * e.g., to assign stations to different VLAN interfaces based on * information from a RADIUS server. This allows separate broadcast * domains to be used with a single BSS. */ int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, int vlan_id); /** * commit - Optional commit changes handler (AP only) * @priv: driver private data * Returns: 0 on success, -1 on failure * * This optional handler function can be registered if the driver * interface implementation needs to commit changes (e.g., by setting * network interface up) at the end of initial configuration. If set, * this handler will be called after initial setup has been completed. */ int (*commit)(void *priv); /** * set_radius_acl_auth - Notification of RADIUS ACL change * @priv: Private driver interface data * @mac: MAC address of the station * @accepted: Whether the station was accepted * @session_timeout: Session timeout for the station * Returns: 0 on success, -1 on failure */ int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, u32 session_timeout); /** * set_radius_acl_expire - Notification of RADIUS ACL expiration * @priv: Private driver interface data * @mac: MAC address of the station * Returns: 0 on success, -1 on failure */ int (*set_radius_acl_expire)(void *priv, const u8 *mac); /** * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) * @priv: Private driver interface data * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove * extra IE(s) * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL * to remove extra IE(s) * Returns: 0 on success, -1 on failure * * This is an optional function to add WPS IE in the kernel driver for * Beacon and Probe Response frames. This can be left undefined (set * to %NULL) if the driver uses the Beacon template from set_ap() * and does not process Probe Request frames. If the driver takes care * of (Re)Association frame processing, the assocresp buffer includes * WPS IE(s) that need to be added to (Re)Association Response frames * whenever a (Re)Association Request frame indicated use of WPS. * * This will also be used to add P2P IE(s) into Beacon/Probe Response * frames when operating as a GO. The driver is responsible for adding * timing related attributes (e.g., NoA) in addition to the IEs * included here by appending them after these buffers. This call is * also used to provide Probe Response IEs for P2P Listen state * operations for drivers that generate the Probe Response frames * internally. * * DEPRECATED - use set_ap() instead */ int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, const struct wpabuf *proberesp, const struct wpabuf *assocresp); /** * set_supp_port - Set IEEE 802.1X Supplicant Port status * @priv: Private driver interface data * @authorized: Whether the port is authorized * Returns: 0 on success, -1 on failure */ int (*set_supp_port)(void *priv, int authorized); /** * set_wds_sta - Bind a station into a 4-address WDS (AP only) * @priv: Private driver interface data * @addr: MAC address of the associated station * @aid: Association ID * @val: 1 = bind to 4-address WDS; 0 = unbind * @bridge_ifname: Bridge interface to use for the WDS station or %NULL * to indicate that bridge is not to be used * @ifname_wds: Buffer to return the interface name for the new WDS * station or %NULL to indicate name is not returned. * Returns: 0 on success, -1 on failure */ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, const char *bridge_ifname, char *ifname_wds); /** * send_action - Transmit an Action frame * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel * @wait: Time to wait off-channel for a response (in ms), or zero * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @data: Frame body * @data_len: data length in octets @ @no_cck: Whether CCK rates must not be used to transmit this frame * Returns: 0 on success, -1 on failure * * This command can be used to request the driver to transmit an action * frame to the specified destination. * * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will * be transmitted on the given channel and the device will wait for a * response on that channel for the given wait time. * * If the flag is not set, the wait time will be ignored. In this case, * if a remain-on-channel duration is in progress, the frame must be * transmitted on that channel; alternatively the frame may be sent on * the current operational channel (if in associated state in station * mode or while operating as an AP.) * * If @src differs from the device MAC address, use of a random * transmitter address is requested for this message exchange. */ int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, int no_cck); /** * send_action_cancel_wait - Cancel action frame TX wait * @priv: Private driver interface data * * This command cancels the wait time associated with sending an action * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is * set in the driver flags. */ void (*send_action_cancel_wait)(void *priv); /** * remain_on_channel - Remain awake on a channel * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel * @duration: Duration in milliseconds * Returns: 0 on success, -1 on failure * * This command is used to request the driver to remain awake on the * specified channel for the specified duration and report received * Action frames with EVENT_RX_MGMT events. Optionally, received * Probe Request frames may also be requested to be reported by calling * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. * * The driver may not be at the requested channel when this function * returns, i.e., the return code is only indicating whether the * request was accepted. The caller will need to wait until the * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has * completed the channel change. This may take some time due to other * need for the radio and the caller should be prepared to timing out * its wait since there are no guarantees on when this request can be * executed. */ int (*remain_on_channel)(void *priv, unsigned int freq, unsigned int duration); /** * cancel_remain_on_channel - Cancel remain-on-channel operation * @priv: Private driver interface data * * This command can be used to cancel a remain-on-channel operation * before its originally requested duration has passed. This could be * used, e.g., when remain_on_channel() is used to request extra time * to receive a response to an Action frame and the response is * received when there is still unneeded time remaining on the * remain-on-channel operation. */ int (*cancel_remain_on_channel)(void *priv); /** * probe_req_report - Request Probe Request frames to be indicated * @priv: Private driver interface data * @report: Whether to report received Probe Request frames * Returns: 0 on success, -1 on failure (or if not supported) * * This command can be used to request the driver to indicate when * Probe Request frames are received with EVENT_RX_PROBE_REQ events. * Since this operation may require extra resources, e.g., due to less * optimal hardware/firmware RX filtering, many drivers may disable * Probe Request reporting at least in station mode. This command is * used to notify the driver when the Probe Request frames need to be * reported, e.g., during remain-on-channel operations. */ int (*probe_req_report)(void *priv, int report); /** * deinit_ap - Deinitialize AP mode * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable AP mode related * configuration. If the interface was not dynamically added, * change the driver mode to station mode to allow normal station * operations like scanning to be completed. */ int (*deinit_ap)(void *priv); /** * deinit_p2p_cli - Deinitialize P2P client mode * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable P2P client mode. If the * interface was not dynamically added, change the interface type back * to station mode. */ int (*deinit_p2p_cli)(void *priv); /** * suspend - Notification on system suspend/hibernate event * @priv: Private driver interface data */ void (*suspend)(void *priv); /** * resume - Notification on system resume/thaw event * @priv: Private driver interface data */ void (*resume)(void *priv); /** * signal_monitor - Set signal monitoring parameters * @priv: Private driver interface data * @threshold: Threshold value for signal change events; 0 = disabled * @hysteresis: Minimum change in signal strength before indicating a * new event * Returns: 0 on success, -1 on failure (or if not supported) * * This function can be used to configure monitoring of signal strength * with the current AP. Whenever signal strength drops below the * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event * should be generated assuming the signal strength has changed at * least %hysteresis from the previously indicated signal change event. */ int (*signal_monitor)(void *priv, int threshold, int hysteresis); /** * get_noa - Get current Notice of Absence attribute payload * @priv: Private driver interface data * @buf: Buffer for returning NoA * @buf_len: Buffer length in octets * Returns: Number of octets used in buf, 0 to indicate no NoA is being * advertized, or -1 on failure * * This function is used to fetch the current Notice of Absence * attribute value from GO. */ int (*get_noa)(void *priv, u8 *buf, size_t buf_len); /** * set_noa - Set Notice of Absence parameters for GO (testing) * @priv: Private driver interface data * @count: Count * @start: Start time in ms from next TBTT * @duration: Duration in ms * Returns: 0 on success or -1 on failure * * This function is used to set Notice of Absence parameters for GO. It * is used only for testing. To disable NoA, all parameters are set to * 0. */ int (*set_noa)(void *priv, u8 count, int start, int duration); /** * set_p2p_powersave - Set P2P power save options * @priv: Private driver interface data * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change * @opp_ps: 0 = disable, 1 = enable, -1 = no change * @ctwindow: 0.. = change (msec), -1 = no change * Returns: 0 on success or -1 on failure */ int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps, int ctwindow); /** * ampdu - Enable/disable aggregation * @priv: Private driver interface data * @ampdu: 1/0 = enable/disable A-MPDU aggregation * Returns: 0 on success or -1 on failure */ int (*ampdu)(void *priv, int ampdu); /** * get_radio_name - Get physical radio name for the device * @priv: Private driver interface data * Returns: Radio name or %NULL if not known * * The returned data must not be modified by the caller. It is assumed * that any interface that has the same radio name as another is * sharing the same physical radio. This information can be used to * share scan results etc. information between the virtual interfaces * to speed up various operations. */ const char * (*get_radio_name)(void *priv); /** * send_tdls_mgmt - for sending TDLS management packets * @priv: private driver interface data * @dst: Destination (peer) MAC address * @action_code: TDLS action code for the mssage * @dialog_token: Dialog Token to use in the message (if needed) * @status_code: Status Code or Reason Code to use (if needed) * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield) * @initiator: Is the current end the TDLS link initiator * @buf: TDLS IEs to add to the message * @len: Length of buf in octets * Returns: 0 on success, negative (<0) on failure * * This optional function can be used to send packet to driver which is * responsible for receiving and sending all TDLS packets. */ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, int initiator, const u8 *buf, size_t len); /** * tdls_oper - Ask the driver to perform high-level TDLS operations * @priv: Private driver interface data * @oper: TDLS high-level operation. See %enum tdls_oper * @peer: Destination (peer) MAC address * Returns: 0 on success, negative (<0) on failure * * This optional function can be used to send high-level TDLS commands * to the driver. */ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); /** * wnm_oper - Notify driver of the WNM frame reception * @priv: Private driver interface data * @oper: WNM operation. See %enum wnm_oper * @peer: Destination (peer) MAC address * @buf: Buffer for the driver to fill in (for getting IE) * @buf_len: Return the len of buf * Returns: 0 on success, negative (<0) on failure */ int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len); /** * set_qos_map - Set QoS Map * @priv: Private driver interface data * @qos_map_set: QoS Map * @qos_map_set_len: Length of QoS Map */ int (*set_qos_map)(void *priv, const u8 *qos_map_set, u8 qos_map_set_len); /** * br_add_ip_neigh - Add a neigh to the bridge ip neigh table * @priv: Private driver interface data * @version: IP version of the IP address, 4 or 6 * @ipaddr: IP address for the neigh entry * @prefixlen: IP address prefix length * @addr: Corresponding MAC address * Returns: 0 on success, negative (<0) on failure */ int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr, int prefixlen, const u8 *addr); /** * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table * @priv: Private driver interface data * @version: IP version of the IP address, 4 or 6 * @ipaddr: IP address for the neigh entry * Returns: 0 on success, negative (<0) on failure */ int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr); /** * br_port_set_attr - Set a bridge port attribute * @attr: Bridge port attribute to set * @val: Value to be set * Returns: 0 on success, negative (<0) on failure */ int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr, unsigned int val); /** * br_port_set_attr - Set a bridge network parameter * @param: Bridge parameter to set * @val: Value to be set * Returns: 0 on success, negative (<0) on failure */ int (*br_set_net_param)(void *priv, enum drv_br_net_param param, unsigned int val); /** * get_wowlan - Get wake-on-wireless status * @priv: Private driver interface data */ int (*get_wowlan)(void *priv); /** * set_wowlan - Set wake-on-wireless triggers * @priv: Private driver interface data * @triggers: wowlan triggers */ int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers); /** * signal_poll - Get current connection information * @priv: Private driver interface data * @signal_info: Connection info structure */ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); /** * channel_info - Get parameters of the current operating channel * @priv: Private driver interface data * @channel_info: Channel info structure * Returns: 0 on success, negative (<0) on failure */ int (*channel_info)(void *priv, struct wpa_channel_info *channel_info); /** * set_authmode - Set authentication algorithm(s) for static WEP * @priv: Private driver interface data * @authmode: 1=Open System, 2=Shared Key, 3=both * Returns: 0 on success, -1 on failure * * This function can be used to set authentication algorithms for AP * mode when static WEP is used. If the driver uses user space MLME/SME * implementation, there is no need to implement this function. * * DEPRECATED - use set_ap() instead */ int (*set_authmode)(void *priv, int authmode); #ifdef ANDROID /** * driver_cmd - Execute driver-specific command * @priv: Private driver interface data * @cmd: Command to execute * @buf: Return buffer * @buf_len: Buffer length * Returns: 0 on success, -1 on failure */ int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len); #endif /* ANDROID */ /** * vendor_cmd - Execute vendor specific command * @priv: Private driver interface data * @vendor_id: Vendor id * @subcmd: Vendor command id * @nested_attr_flag: Specifies if vendor subcommand uses nested * attributes or not * @data: Vendor command parameters (%NULL if no parameters) * @data_len: Data length * @buf: Return buffer (%NULL to ignore reply) * Returns: 0 on success, negative (<0) on failure * * This function handles vendor specific commands that are passed to * the driver/device. The command is identified by vendor id and * command id. The nested_attr_flag specifies whether the subcommand * uses nested attributes or not. Parameters can be passed * as argument to the command in the data buffer. Reply (if any) will be * filled in the supplied return buffer. * * The exact driver behavior is driver interface and vendor specific. As * an example, this will be converted to a vendor specific cfg80211 * command in case of the nl80211 driver interface. */ int (*vendor_cmd)(void *priv, unsigned int vendor_id, unsigned int subcmd, const u8 *data, size_t data_len, enum nested_attr nested_attr_flag, struct wpabuf *buf); /** * set_rekey_info - Set rekey information * @priv: Private driver interface data * @kek: Current KEK * @kek_len: KEK length in octets * @kck: Current KCK * @kck_len: KCK length in octets * @replay_ctr: Current EAPOL-Key Replay Counter * * This optional function can be used to provide information for the * driver/firmware to process EAPOL-Key frames in Group Key Handshake * while the host (including wpa_supplicant) is sleeping. */ void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len, const u8 *kck, size_t kck_len, const u8 *replay_ctr); /** * sta_assoc - Station association indication * @priv: Private driver interface data * @own_addr: Source address and BSSID for association frame * @addr: MAC address of the station to associate * @reassoc: flag to indicate re-association * @status: association response status code * @ie: assoc response ie buffer * @len: ie buffer length * Returns: 0 on success, -1 on failure * * This function indicates the driver to send (Re)Association * Response frame to the station. */ int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len); /** * sta_auth - Station authentication indication * @priv: private driver interface data * @params: Station authentication parameters * * Returns: 0 on success, -1 on failure */ int (*sta_auth)(void *priv, struct wpa_driver_sta_auth_params *params); /** * add_tspec - Add traffic stream * @priv: Private driver interface data * @addr: MAC address of the station to associate * @tspec_ie: tspec ie buffer * @tspec_ielen: tspec ie length * Returns: 0 on success, -1 on failure * * This function adds the traffic steam for the station * and fills the medium_time in tspec_ie. */ int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen); /** * add_sta_node - Add a station node in the driver * @priv: Private driver interface data * @addr: MAC address of the station to add * @auth_alg: authentication algorithm used by the station * Returns: 0 on success, -1 on failure * * This function adds the station node in the driver, when * the station gets added by FT-over-DS. */ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); /** * sched_scan - Request the driver to initiate scheduled scan * @priv: Private driver interface data * @params: Scan parameters * Returns: 0 on success, -1 on failure * * This operation should be used for scheduled scan offload to * the hardware. Every time scan results are available, the * driver should report scan results event for wpa_supplicant * which will eventually request the results with * wpa_driver_get_scan_results2(). This operation is optional * and if not provided or if it returns -1, we fall back to * normal host-scheduled scans. */ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params); /** * stop_sched_scan - Request the driver to stop a scheduled scan * @priv: Private driver interface data * Returns: 0 on success, -1 on failure * * This should cause the scheduled scan to be stopped and * results should stop being sent. Must be supported if * sched_scan is supported. */ int (*stop_sched_scan)(void *priv); /** * poll_client - Probe (null data or such) the given station * @priv: Private driver interface data * @own_addr: MAC address of sending interface * @addr: MAC address of the station to probe * @qos: Indicates whether station is QoS station * * This function is used to verify whether an associated station is * still present. This function does not need to be implemented if the * driver provides such inactivity polling mechanism. */ void (*poll_client)(void *priv, const u8 *own_addr, const u8 *addr, int qos); /** * radio_disable - Disable/enable radio * @priv: Private driver interface data * @disabled: 1=disable 0=enable radio * Returns: 0 on success, -1 on failure * * This optional command is for testing purposes. It can be used to * disable the radio on a testbed device to simulate out-of-radio-range * conditions. */ int (*radio_disable)(void *priv, int disabled); /** * switch_channel - Announce channel switch and migrate the GO to the * given frequency * @priv: Private driver interface data * @settings: Settings for CSA period and new channel * Returns: 0 on success, -1 on failure * * This function is used to move the GO to the legacy STA channel to * avoid frequency conflict in single channel concurrency. */ int (*switch_channel)(void *priv, struct csa_settings *settings); /** * add_tx_ts - Add traffic stream * @priv: Private driver interface data * @tsid: Traffic stream ID * @addr: Receiver address * @user_prio: User priority of the traffic stream * @admitted_time: Admitted time for this TS in units of * 32 microsecond periods (per second). * Returns: 0 on success, -1 on failure */ int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio, u16 admitted_time); /** * del_tx_ts - Delete traffic stream * @priv: Private driver interface data * @tsid: Traffic stream ID * @addr: Receiver address * Returns: 0 on success, -1 on failure */ int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr); /** * Enable channel-switching with TDLS peer * @priv: Private driver interface data * @addr: MAC address of the TDLS peer * @oper_class: Operating class of the switch channel * @params: Channel specification * Returns: 0 on success, -1 on failure * * The function indicates to driver that it can start switching to a * different channel with a specified TDLS peer. The switching is * assumed on until canceled with tdls_disable_channel_switch(). */ int (*tdls_enable_channel_switch)( void *priv, const u8 *addr, u8 oper_class, const struct hostapd_freq_params *params); /** * Disable channel switching with TDLS peer * @priv: Private driver interface data * @addr: MAC address of the TDLS peer * Returns: 0 on success, -1 on failure * * This function indicates to the driver that it should stop switching * with a given TDLS peer. */ int (*tdls_disable_channel_switch)(void *priv, const u8 *addr); /** * start_dfs_cac - Listen for radar interference on the channel * @priv: Private driver interface data * @freq: Channel parameters * Returns: 0 on success, -1 on failure */ int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq); /** * stop_ap - Removes beacon from AP * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable AP mode related * configuration. Unlike deinit_ap, it does not change to station * mode. */ int (*stop_ap)(void *priv); /** * get_survey - Retrieve survey data * @priv: Private driver interface data * @freq: If set, survey data for the specified frequency is only * being requested. If not set, all survey data is requested. * Returns: 0 on success, -1 on failure * * Use this to retrieve: * * - the observed channel noise floor * - the amount of time we have spent on the channel * - the amount of time during which we have spent on the channel that * the radio has determined the medium is busy and we cannot * transmit * - the amount of time we have spent receiving data * - the amount of time we have spent transmitting data * * This data can be used for spectrum heuristics. One example is * Automatic Channel Selection (ACS). The channel survey data is * kept on a linked list on the channel data, one entry is added * for each survey. The min_nf of the channel is updated for each * survey. */ int (*get_survey)(void *priv, unsigned int freq); /** * status - Get driver interface status information * @priv: Private driver interface data * @buf: Buffer for printing the status information * @buflen: Maximum length of the buffer * Returns: Length of written status information or -1 on failure */ int (*status)(void *priv, char *buf, size_t buflen); /** * roaming - Set roaming policy for driver-based BSS selection * @priv: Private driver interface data * @allowed: Whether roaming within ESS is allowed * @bssid: Forced BSSID if roaming is disabled or %NULL if not set * Returns: Length of written status information or -1 on failure * * This optional callback can be used to update roaming policy from the * associate() command (bssid being set there indicates that the driver * should not roam before getting this roaming() call to allow roaming. * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION * capability, roaming policy is handled within wpa_supplicant and there * is no need to implement or react to this callback. */ int (*roaming)(void *priv, int allowed, const u8 *bssid); /** * disable_fils - Enable/disable FILS feature * @priv: Private driver interface data * @disable: 0-enable and 1-disable FILS feature * Returns: 0 on success, -1 on failure * * This callback can be used to configure driver and below layers to * enable/disable all FILS features. */ int (*disable_fils)(void *priv, int disable); /** * set_mac_addr - Set MAC address * @priv: Private driver interface data * @addr: MAC address to use or %NULL for setting back to permanent * Returns: 0 on success, -1 on failure */ int (*set_mac_addr)(void *priv, const u8 *addr); #ifdef CONFIG_MACSEC int (*macsec_init)(void *priv, struct macsec_init_params *params); int (*macsec_deinit)(void *priv); /** * macsec_get_capability - Inform MKA of this driver's capability * @priv: Private driver interface data * @cap: Driver's capability * Returns: 0 on success, -1 on failure */ int (*macsec_get_capability)(void *priv, enum macsec_cap *cap); /** * enable_protect_frames - Set protect frames status * @priv: Private driver interface data * @enabled: true = protect frames enabled * false = protect frames disabled * Returns: 0 on success, -1 on failure (or if not supported) */ int (*enable_protect_frames)(void *priv, bool enabled); /** * enable_encrypt - Set encryption status * @priv: Private driver interface data * @enabled: true = encrypt outgoing traffic * false = integrity-only protection on outgoing traffic * Returns: 0 on success, -1 on failure (or if not supported) */ int (*enable_encrypt)(void *priv, bool enabled); /** * set_replay_protect - Set replay protect status and window size * @priv: Private driver interface data * @enabled: true = replay protect enabled * false = replay protect disabled * @window: replay window size, valid only when replay protect enabled * Returns: 0 on success, -1 on failure (or if not supported) */ int (*set_replay_protect)(void *priv, bool enabled, u32 window); /** * set_current_cipher_suite - Set current cipher suite * @priv: Private driver interface data * @cs: EUI64 identifier * Returns: 0 on success, -1 on failure (or if not supported) */ int (*set_current_cipher_suite)(void *priv, u64 cs); /** * enable_controlled_port - Set controlled port status * @priv: Private driver interface data * @enabled: true = controlled port enabled * false = controlled port disabled * Returns: 0 on success, -1 on failure (or if not supported) */ int (*enable_controlled_port)(void *priv, bool enabled); /** * get_receive_lowest_pn - Get receive lowest pn * @priv: Private driver interface data * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa); /** * get_transmit_next_pn - Get transmit next pn * @priv: Private driver interface data * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** * set_transmit_next_pn - Set transmit next pn * @priv: Private driver interface data * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** * set_receive_lowest_pn - Set receive lowest PN * @priv: Private driver interface data * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa); /** * create_receive_sc - create secure channel for receiving * @priv: Private driver interface data * @sc: secure channel * @conf_offset: confidentiality offset (0, 30, or 50) * @validation: frame validation policy (0 = Disabled, 1 = Checked, * 2 = Strict) * Returns: 0 on success, -1 on failure (or if not supported) */ int (*create_receive_sc)(void *priv, struct receive_sc *sc, unsigned int conf_offset, int validation); /** * delete_receive_sc - delete secure connection for receiving * @priv: private driver interface data from init() * @sc: secure channel * Returns: 0 on success, -1 on failure */ int (*delete_receive_sc)(void *priv, struct receive_sc *sc); /** * create_receive_sa - create secure association for receive * @priv: private driver interface data from init() * @sa: secure association * Returns: 0 on success, -1 on failure */ int (*create_receive_sa)(void *priv, struct receive_sa *sa); /** * delete_receive_sa - Delete secure association for receive * @priv: Private driver interface data from init() * @sa: Secure association * Returns: 0 on success, -1 on failure */ int (*delete_receive_sa)(void *priv, struct receive_sa *sa); /** * enable_receive_sa - enable the SA for receive * @priv: private driver interface data from init() * @sa: secure association * Returns: 0 on success, -1 on failure */ int (*enable_receive_sa)(void *priv, struct receive_sa *sa); /** * disable_receive_sa - disable SA for receive * @priv: private driver interface data from init() * @sa: secure association * Returns: 0 on success, -1 on failure */ int (*disable_receive_sa)(void *priv, struct receive_sa *sa); /** * create_transmit_sc - create secure connection for transmit * @priv: private driver interface data from init() * @sc: secure channel * @conf_offset: confidentiality offset (0, 30, or 50) * Returns: 0 on success, -1 on failure */ int (*create_transmit_sc)(void *priv, struct transmit_sc *sc, unsigned int conf_offset); /** * delete_transmit_sc - delete secure connection for transmit * @priv: private driver interface data from init() * @sc: secure channel * Returns: 0 on success, -1 on failure */ int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc); /** * create_transmit_sa - create secure association for transmit * @priv: private driver interface data from init() * @sa: secure association * Returns: 0 on success, -1 on failure */ int (*create_transmit_sa)(void *priv, struct transmit_sa *sa); /** * delete_transmit_sa - Delete secure association for transmit * @priv: Private driver interface data from init() * @sa: Secure association * Returns: 0 on success, -1 on failure */ int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa); /** * enable_transmit_sa - enable SA for transmit * @priv: private driver interface data from init() * @sa: secure association * Returns: 0 on success, -1 on failure */ int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa); /** * disable_transmit_sa - disable SA for transmit * @priv: private driver interface data from init() * @sa: secure association * Returns: 0 on success, -1 on failure */ int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa); #endif /* CONFIG_MACSEC */ /** * init_mesh - Driver specific initialization for mesh * @priv: Private driver interface data * Returns: 0 on success, -1 on failure */ int (*init_mesh)(void *priv); /** * join_mesh - Join a mesh network * @priv: Private driver interface data * @params: Mesh configuration parameters * Returns: 0 on success, -1 on failure */ int (*join_mesh)(void *priv, struct wpa_driver_mesh_join_params *params); /** * leave_mesh - Leave a mesh network * @priv: Private driver interface data * Returns 0 on success, -1 on failure */ int (*leave_mesh)(void *priv); /** * probe_mesh_link - Inject a frame over direct mesh link to a given * peer skipping the next_hop lookup from mpath table. * @priv: Private driver interface data * @addr: Peer MAC address * @eth: Ethernet frame to be sent * @len: Ethernet frame lengtn in bytes * Returns 0 on success, -1 on failure */ int (*probe_mesh_link)(void *priv, const u8 *addr, const u8 *eth, size_t len); /** * do_acs - Automatically select channel * @priv: Private driver interface data * @params: Parameters for ACS * Returns 0 on success, -1 on failure * * This command can be used to offload ACS to the driver if the driver * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD). */ int (*do_acs)(void *priv, struct drv_acs_params *params); /** * set_band - Notify driver of band(s) selection * @priv: Private driver interface data * @band_mask: The selected band(s) bit mask (from enum set_band) * Returns 0 on success, -1 on failure */ int (*set_band)(void *priv, u32 band_mask); /** * get_pref_freq_list - Get preferred frequency list for an interface * @priv: Private driver interface data * @if_type: Interface type * @num: Number of channels * @freq_list: Preferred channel frequency list encoded in MHz values * Returns 0 on success, -1 on failure * * This command can be used to query the preferred frequency list from * the driver specific to a particular interface type. */ int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, unsigned int *num, unsigned int *freq_list); /** * set_prob_oper_freq - Indicate probable P2P operating channel * @priv: Private driver interface data * @freq: Channel frequency in MHz * Returns 0 on success, -1 on failure * * This command can be used to inform the driver of the operating * frequency that an ongoing P2P group formation is likely to come up * on. Local device is assuming P2P Client role. */ int (*set_prob_oper_freq)(void *priv, unsigned int freq); /** * abort_scan - Request the driver to abort an ongoing scan * @priv: Private driver interface data * @scan_cookie: Cookie identifying the scan request. This is used only * when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN * was used to trigger scan. Otherwise, 0 is used. * Returns 0 on success, -1 on failure */ int (*abort_scan)(void *priv, u64 scan_cookie); /** * configure_data_frame_filters - Request to configure frame filters * @priv: Private driver interface data * @filter_flags: The type of frames to filter (bitfield of * WPA_DATA_FRAME_FILTER_FLAG_*) * Returns: 0 on success or -1 on failure */ int (*configure_data_frame_filters)(void *priv, u32 filter_flags); /** * get_ext_capab - Get extended capabilities for the specified interface * @priv: Private driver interface data * @type: Interface type for which to get extended capabilities * @ext_capab: Extended capabilities fetched * @ext_capab_mask: Extended capabilities mask * @ext_capab_len: Length of the extended capabilities * Returns: 0 on success or -1 on failure */ int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type, const u8 **ext_capab, const u8 **ext_capab_mask, unsigned int *ext_capab_len); /** * p2p_lo_start - Start offloading P2P listen to device * @priv: Private driver interface data * @freq: Listening frequency (MHz) for P2P listen * @period: Length of the listen operation in milliseconds * @interval: Interval for running the listen operation in milliseconds * @count: Number of times to run the listen operation * @device_types: Device primary and secondary types * @dev_types_len: Number of bytes for device_types * @ies: P2P IE and WSC IE for Probe Response frames * @ies_len: Length of ies in bytes * Returns: 0 on success or -1 on failure */ int (*p2p_lo_start)(void *priv, unsigned int freq, unsigned int period, unsigned int interval, unsigned int count, const u8 *device_types, size_t dev_types_len, const u8 *ies, size_t ies_len); /** * p2p_lo_stop - Stop P2P listen offload * @priv: Private driver interface data * Returns: 0 on success or -1 on failure */ int (*p2p_lo_stop)(void *priv); /** * set_default_scan_ies - Set default scan IEs * @priv: Private driver interface data * @ies: Scan default IEs buffer * @ies_len: Length of IEs in bytes * Returns: 0 on success or -1 on failure * * The driver can use these by default when there are no scan IEs coming * in the subsequent scan requests. Also in case of one or more of IEs * given in set_default_scan_ies() are missing in the subsequent scan * request, the driver should merge the missing scan IEs in the scan * request from the IEs set by set_default_scan_ies() in the Probe * Request frames sent. */ int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len); /** * set_tdls_mode - Set TDLS trigger mode to the host driver * @priv: Private driver interface data * @tdls_external_control: Represents if TDLS external trigger control * mode is enabled/disabled. * * This optional callback can be used to configure the TDLS external * trigger control mode to the host driver. */ int (*set_tdls_mode)(void *priv, int tdls_external_control); /** * get_bss_transition_status - Get candidate BSS's transition status * @priv: Private driver interface data * @params: Candidate BSS list * * Get the accept or reject reason code for a list of BSS transition * candidates. */ struct wpa_bss_candidate_info * (*get_bss_transition_status)(void *priv, struct wpa_bss_trans_info *params); /** * ignore_assoc_disallow - Configure driver to ignore assoc_disallow * @priv: Private driver interface data * @ignore_disallow: 0 to not ignore, 1 to ignore * Returns: 0 on success, -1 on failure */ int (*ignore_assoc_disallow)(void *priv, int ignore_disallow); /** * set_bssid_tmp_disallow - Set disallowed BSSIDs to the driver * @priv: Private driver interface data * @num_bssid: Number of temporarily disallowed BSSIDs * @bssids: List of temporarily disallowed BSSIDs */ int (*set_bssid_tmp_disallow)(void *priv, unsigned int num_bssid, const u8 *bssid); /** * update_connect_params - Update the connection parameters * @priv: Private driver interface data * @params: Association parameters * @mask: Bit mask indicating which parameters in @params have to be * updated * Returns: 0 on success, -1 on failure * * Update the connection parameters when in connected state so that the * driver uses the updated parameters for subsequent roaming. This is * used only with drivers that implement internal BSS selection and * roaming. */ int (*update_connect_params)( void *priv, struct wpa_driver_associate_params *params, enum wpa_drv_update_connect_params_mask mask); /** * send_external_auth_status - Indicate the status of external * authentication processing to the host driver. * @priv: Private driver interface data * @params: Status of authentication processing. * Returns: 0 on success, -1 on failure */ int (*send_external_auth_status)(void *priv, struct external_auth *params); /** * set_4addr_mode - Set 4-address mode * @priv: Private driver interface data * @bridge_ifname: Bridge interface name * @val: 0 - disable 4addr mode, 1 - enable 4addr mode * Returns: 0 on success, < 0 on failure */ int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val); /** * update_dh_ie - Update DH IE * @priv: Private driver interface data * @peer_mac: Peer MAC address * @reason_code: Reacon code * @ie: DH IE * @ie_len: DH IE length in bytes * Returns: 0 on success, -1 on failure * * This callback is used to let the driver know the DH processing result * and DH IE for a pending association. */ int (*update_dh_ie)(void *priv, const u8 *peer_mac, u16 reason_code, const u8 *ie, size_t ie_len); /** * dpp_listen - Notify driver about start/stop of DPP listen * @priv: Private driver interface data * @enable: Whether listen state is enabled (or disabled) * Returns: 0 on success, -1 on failure * * This optional callback can be used to update RX frame filtering to * explicitly allow reception of broadcast Public Action frames. */ int (*dpp_listen)(void *priv, bool enable); #ifdef CONFIG_TESTING_OPTIONS int (*register_frame)(void *priv, u16 type, const u8 *match, size_t match_len, bool multicast); #endif /* CONFIG_TESTING_OPTIONS */ }; /** * enum wpa_event_type - Event type for wpa_supplicant_event() calls */ enum wpa_event_type { /** * EVENT_ASSOC - Association completed * * This event needs to be delivered when the driver completes IEEE * 802.11 association or reassociation successfully. * wpa_driver_ops::get_bssid() is expected to provide the current BSSID * after this event has been generated. In addition, optional * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide * more information about the association. If the driver interface gets * both of these events at the same time, it can also include the * assoc_info data in EVENT_ASSOC call. */ EVENT_ASSOC, /** * EVENT_DISASSOC - Association lost * * This event should be called when association is lost either due to * receiving deauthenticate or disassociate frame from the AP or when * sending either of these frames to the current AP. If the driver * supports separate deauthentication event, EVENT_DISASSOC should only * be used for disassociation and EVENT_DEAUTH for deauthentication. * In AP mode, union wpa_event_data::disassoc_info is required. */ EVENT_DISASSOC, /** * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected * * This event must be delivered when a Michael MIC error is detected by * the local driver. Additional data for event processing is * provided with union wpa_event_data::michael_mic_failure. This * information is used to request new encyption key and to initiate * TKIP countermeasures if needed. */ EVENT_MICHAEL_MIC_FAILURE, /** * EVENT_SCAN_RESULTS - Scan results available * * This event must be called whenever scan results are available to be * fetched with struct wpa_driver_ops::get_scan_results(). This event * is expected to be used some time after struct wpa_driver_ops::scan() * is called. If the driver provides an unsolicited event when the scan * has been completed, this event can be used to trigger * EVENT_SCAN_RESULTS call. If such event is not available from the * driver, the driver wrapper code is expected to use a registered * timeout to generate EVENT_SCAN_RESULTS call after the time that the * scan is expected to be completed. Optional information about * completed scan can be provided with union wpa_event_data::scan_info. */ EVENT_SCAN_RESULTS, /** * EVENT_ASSOCINFO - Report optional extra information for association * * This event can be used to report extra association information for * EVENT_ASSOC processing. This extra information includes IEs from * association frames and Beacon/Probe Response frames in union * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before * EVENT_ASSOC. Alternatively, the driver interface can include * assoc_info data in the EVENT_ASSOC call if it has all the * information available at the same point. */ EVENT_ASSOCINFO, /** * EVENT_INTERFACE_STATUS - Report interface status changes * * This optional event can be used to report changes in interface * status (interface added/removed) using union * wpa_event_data::interface_status. This can be used to trigger * wpa_supplicant to stop and re-start processing for the interface, * e.g., when a cardbus card is ejected/inserted. */ EVENT_INTERFACE_STATUS, /** * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication * * This event can be used to inform wpa_supplicant about candidates for * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible * for scan request (ap_scan=2 mode), this event is required for * pre-authentication. If wpa_supplicant is performing scan request * (ap_scan=1), this event is optional since scan results can be used * to add pre-authentication candidates. union * wpa_event_data::pmkid_candidate is used to report the BSSID of the * candidate and priority of the candidate, e.g., based on the signal * strength, in order to try to pre-authenticate first with candidates * that are most likely targets for re-association. * * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates * on the candidate list. In addition, it can be called for the current * AP and APs that have existing PMKSA cache entries. wpa_supplicant * will automatically skip pre-authentication in cases where a valid * PMKSA exists. When more than one candidate exists, this event should * be generated once for each candidate. * * Driver will be notified about successful pre-authentication with * struct wpa_driver_ops::add_pmkid() calls. */ EVENT_PMKID_CANDIDATE, /** * EVENT_TDLS - Request TDLS operation * * This event can be used to request a TDLS operation to be performed. */ EVENT_TDLS, /** * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs * * The driver is expected to report the received FT IEs from * FT authentication sequence from the AP. The FT IEs are included in * the extra information in union wpa_event_data::ft_ies. */ EVENT_FT_RESPONSE, /** * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS * * The driver can use this event to inform wpa_supplicant about a STA * in an IBSS with which protected frames could be exchanged. This * event starts RSN authentication with the other STA to authenticate * the STA and set up encryption keys with it. */ EVENT_IBSS_RSN_START, /** * EVENT_AUTH - Authentication result * * This event should be called when authentication attempt has been * completed. This is only used if the driver supports separate * authentication step (struct wpa_driver_ops::authenticate). * Information about authentication result is included in * union wpa_event_data::auth. */ EVENT_AUTH, /** * EVENT_DEAUTH - Authentication lost * * This event should be called when authentication is lost either due * to receiving deauthenticate frame from the AP or when sending that * frame to the current AP. * In AP mode, union wpa_event_data::deauth_info is required. */ EVENT_DEAUTH, /** * EVENT_ASSOC_REJECT - Association rejected * * This event should be called when (re)association attempt has been * rejected by the AP. Information about the association response is * included in union wpa_event_data::assoc_reject. */ EVENT_ASSOC_REJECT, /** * EVENT_AUTH_TIMED_OUT - Authentication timed out */ EVENT_AUTH_TIMED_OUT, /** * EVENT_ASSOC_TIMED_OUT - Association timed out */ EVENT_ASSOC_TIMED_OUT, /** * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS */ EVENT_WPS_BUTTON_PUSHED, /** * EVENT_TX_STATUS - Report TX status */ EVENT_TX_STATUS, /** * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA */ EVENT_RX_FROM_UNKNOWN, /** * EVENT_RX_MGMT - Report RX of a management frame */ EVENT_RX_MGMT, /** * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started * * This event is used to indicate when the driver has started the * requested remain-on-channel duration. Information about the * operation is included in union wpa_event_data::remain_on_channel. */ EVENT_REMAIN_ON_CHANNEL, /** * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out * * This event is used to indicate when the driver has completed * remain-on-channel duration, i.e., may noot be available on the * requested channel anymore. Information about the * operation is included in union wpa_event_data::remain_on_channel. */ EVENT_CANCEL_REMAIN_ON_CHANNEL, /** * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame * * This event is used to indicate when a Probe Request frame has been * received. Information about the received frame is included in * union wpa_event_data::rx_probe_req. The driver is required to report * these events only after successfully completed probe_req_report() * commands to request the events (i.e., report parameter is non-zero) * in station mode. In AP mode, Probe Request frames should always be * reported. */ EVENT_RX_PROBE_REQ, /** * EVENT_NEW_STA - New wired device noticed * * This event is used to indicate that a new device has been detected * in a network that does not use association-like functionality (i.e., * mainly wired Ethernet). This can be used to start EAPOL * authenticator when receiving a frame from a device. The address of * the device is included in union wpa_event_data::new_sta. */ EVENT_NEW_STA, /** * EVENT_EAPOL_RX - Report received EAPOL frame * * When in AP mode with hostapd, this event is required to be used to * deliver the receive EAPOL frames from the driver. */ EVENT_EAPOL_RX, /** * EVENT_SIGNAL_CHANGE - Indicate change in signal strength * * This event is used to indicate changes in the signal strength * observed in frames received from the current AP if signal strength * monitoring has been enabled with signal_monitor(). */ EVENT_SIGNAL_CHANGE, /** * EVENT_INTERFACE_ENABLED - Notify that interface was enabled * * This event is used to indicate that the interface was enabled after * having been previously disabled, e.g., due to rfkill. */ EVENT_INTERFACE_ENABLED, /** * EVENT_INTERFACE_DISABLED - Notify that interface was disabled * * This event is used to indicate that the interface was disabled, * e.g., due to rfkill. */ EVENT_INTERFACE_DISABLED, /** * EVENT_CHANNEL_LIST_CHANGED - Channel list changed * * This event is used to indicate that the channel list has changed, * e.g., because of a regulatory domain change triggered by scan * results including an AP advertising a country code. */ EVENT_CHANNEL_LIST_CHANGED, /** * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable * * This event is used to indicate that the driver cannot maintain this * interface in its operation mode anymore. The most likely use for * this is to indicate that AP mode operation is not available due to * operating channel would need to be changed to a DFS channel when * the driver does not support radar detection and another virtual * interfaces caused the operating channel to change. Other similar * resource conflicts could also trigger this for station mode * interfaces. This event can be propagated when channel switching * fails. */ EVENT_INTERFACE_UNAVAILABLE, /** * EVENT_BEST_CHANNEL * * Driver generates this event whenever it detects a better channel * (e.g., based on RSSI or channel use). This information can be used * to improve channel selection for a new AP/P2P group. */ EVENT_BEST_CHANNEL, /** * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received * * This event should be called when a Deauthentication frame is dropped * due to it not being protected (MFP/IEEE 802.11w). * union wpa_event_data::unprot_deauth is required to provide more * details of the frame. */ EVENT_UNPROT_DEAUTH, /** * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received * * This event should be called when a Disassociation frame is dropped * due to it not being protected (MFP/IEEE 802.11w). * union wpa_event_data::unprot_disassoc is required to provide more * details of the frame. */ EVENT_UNPROT_DISASSOC, /** * EVENT_STATION_LOW_ACK * * Driver generates this event whenever it detected that a particular * station was lost. Detection can be through massive transmission * failures for example. */ EVENT_STATION_LOW_ACK, /** * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore */ EVENT_IBSS_PEER_LOST, /** * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey * * This event carries the new replay counter to notify wpa_supplicant * of the current EAPOL-Key Replay Counter in case the driver/firmware * completed Group Key Handshake while the host (including * wpa_supplicant was sleeping). */ EVENT_DRIVER_GTK_REKEY, /** * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped */ EVENT_SCHED_SCAN_STOPPED, /** * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll * * This event indicates that the station responded to the poll * initiated with @poll_client. */ EVENT_DRIVER_CLIENT_POLL_OK, /** * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status */ EVENT_EAPOL_TX_STATUS, /** * EVENT_CH_SWITCH - AP or GO decided to switch channels * * Described in wpa_event_data.ch_switch * */ EVENT_CH_SWITCH, /** * EVENT_CH_SWITCH_STARTED - AP or GO started to switch channels * * This is a pre-switch event indicating the shortly following switch * of operating channels. * * Described in wpa_event_data.ch_switch */ EVENT_CH_SWITCH_STARTED, /** * EVENT_WNM - Request WNM operation * * This event can be used to request a WNM operation to be performed. */ EVENT_WNM, /** * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode * * This event indicates that the driver reported a connection failure * with the specified client (for example, max client reached, etc.) in * AP mode. */ EVENT_CONNECT_FAILED_REASON, /** * EVENT_DFS_RADAR_DETECTED - Notify of radar detection * * A radar has been detected on the supplied frequency, hostapd should * react accordingly (e.g., change channel). */ EVENT_DFS_RADAR_DETECTED, /** * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed * * After a successful CAC, the channel can be marked clear and used. */ EVENT_DFS_CAC_FINISHED, /** * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted * * The CAC was not successful, and the channel remains in the previous * state. This may happen due to a radar being detected or other * external influences. */ EVENT_DFS_CAC_ABORTED, /** * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over * * The channel which was previously unavailable is now available again. */ EVENT_DFS_NOP_FINISHED, /** * EVENT_SURVEY - Received survey data * * This event gets triggered when a driver query is issued for survey * data and the requested data becomes available. The returned data is * stored in struct survey_results. The results provide at most one * survey entry for each frequency and at minimum will provide one * survey entry for one frequency. The survey data can be os_malloc()'d * and then os_free()'d, so the event callback must only copy data. */ EVENT_SURVEY, /** * EVENT_SCAN_STARTED - Scan started * * This indicates that driver has started a scan operation either based * on a request from wpa_supplicant/hostapd or from another application. * EVENT_SCAN_RESULTS is used to indicate when the scan has been * completed (either successfully or by getting cancelled). */ EVENT_SCAN_STARTED, /** * EVENT_AVOID_FREQUENCIES - Received avoid frequency range * * This event indicates a set of frequency ranges that should be avoided * to reduce issues due to interference or internal co-existence * information in the driver. */ EVENT_AVOID_FREQUENCIES, /** * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification */ EVENT_NEW_PEER_CANDIDATE, /** * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS * * Indicates a pair of primary and secondary channels chosen by ACS * in device. */ EVENT_ACS_CHANNEL_SELECTED, /** * EVENT_DFS_CAC_STARTED - Notify that channel availability check has * been started. * * This event indicates that channel availability check has been started * on a DFS frequency by a driver that supports DFS Offload. */ EVENT_DFS_CAC_STARTED, /** * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped */ EVENT_P2P_LO_STOP, /** * EVENT_BEACON_LOSS - Beacon loss detected * * This event indicates that no Beacon frames has been received from * the current AP. This may indicate that the AP is not anymore in * range. */ EVENT_BEACON_LOSS, /** * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check * done previously (Pre-CAC) on the channel has expired. This would * normally be on a non-ETSI DFS regulatory domain. DFS state of the * channel will be moved from available to usable. A new CAC has to be * performed before start operating on this channel. */ EVENT_DFS_PRE_CAC_EXPIRED, /** * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers * that do not define separate commands for authentication and * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11 * authentication to wpa_supplicant. This event carries all the * necessary information from the host driver for the authentication to * happen. */ EVENT_EXTERNAL_AUTH, /** * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized * * This event should be indicated when the driver completes the 4-way * handshake. This event should be preceded by an EVENT_ASSOC that * indicates the completion of IEEE 802.11 association. */ EVENT_PORT_AUTHORIZED, /** * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode * change event. */ EVENT_STATION_OPMODE_CHANGED, /** * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed * * This event is emitted when the MAC changes while the interface is * enabled. When an interface was disabled and becomes enabled, it * must be always assumed that the MAC possibly changed. */ EVENT_INTERFACE_MAC_CHANGED, /** * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status * * This event is emitted when an interface is added/removed for WDS STA. */ EVENT_WDS_STA_INTERFACE_STATUS, /** * EVENT_UPDATE_DH - Notification of updated DH information */ EVENT_UPDATE_DH, /** * EVENT_UNPROT_BEACON - Unprotected Beacon frame received * * This event should be called when a Beacon frame is dropped due to it * not being protected correctly. union wpa_event_data::unprot_beacon * is required to provide more details of the frame. */ EVENT_UNPROT_BEACON, }; /** * struct freq_survey - Channel survey info * * @ifidx: Interface index in which this survey was observed * @freq: Center of frequency of the surveyed channel * @nf: Channel noise floor in dBm * @channel_time: Amount of time in ms the radio spent on the channel * @channel_time_busy: Amount of time in ms the radio detected some signal * that indicated to the radio the channel was not clear * @channel_time_rx: Amount of time the radio spent receiving data * @channel_time_tx: Amount of time the radio spent transmitting data * @filled: bitmask indicating which fields have been reported, see * SURVEY_HAS_* defines. * @list: Internal list pointers */ struct freq_survey { u32 ifidx; unsigned int freq; s8 nf; u64 channel_time; u64 channel_time_busy; u64 channel_time_rx; u64 channel_time_tx; unsigned int filled; struct dl_list list; }; #define SURVEY_HAS_NF BIT(0) #define SURVEY_HAS_CHAN_TIME BIT(1) #define SURVEY_HAS_CHAN_TIME_BUSY BIT(2) #define SURVEY_HAS_CHAN_TIME_RX BIT(3) #define SURVEY_HAS_CHAN_TIME_TX BIT(4) /** * enum sta_connect_fail_reason_codes - STA connect failure reason code values * @STA_CONNECT_FAIL_REASON_UNSPECIFIED: No reason code specified for * connection failure. * @STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received * for unicast Probe Request frame. * @STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request. * @STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for * auth request. * @STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not * received from AP. * @STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send * Association Request frame. * @STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for * Association Request frame. * @STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response * frame is not received from AP. */ enum sta_connect_fail_reason_codes { STA_CONNECT_FAIL_REASON_UNSPECIFIED = 0, STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1, STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2, STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3, STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4, STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5, STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6, STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7, }; /** * union wpa_event_data - Additional data for wpa_supplicant_event() calls */ union wpa_event_data { /** * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events * * This structure is optional for EVENT_ASSOC calls and required for * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the * driver interface does not need to generate separate EVENT_ASSOCINFO * calls. */ struct assoc_info { /** * reassoc - Flag to indicate association or reassociation */ int reassoc; /** * req_ies - (Re)Association Request IEs * * If the driver generates WPA/RSN IE, this event data must be * returned for WPA handshake to have needed information. If * wpa_supplicant-generated WPA/RSN IE is used, this * information event is optional. * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *req_ies; /** * req_ies_len - Length of req_ies in bytes */ size_t req_ies_len; /** * resp_ies - (Re)Association Response IEs * * Optional association data from the driver. This data is not * required WPA, but may be useful for some protocols and as * such, should be reported if this is available to the driver * interface. * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes */ size_t resp_ies_len; /** * resp_frame - (Re)Association Response frame */ const u8 *resp_frame; /** * resp_frame_len - (Re)Association Response frame length */ size_t resp_frame_len; /** * beacon_ies - Beacon or Probe Response IEs * * Optional Beacon/ProbeResp data: IEs included in Beacon or * Probe Response frames from the current AP (i.e., the one * that the client just associated with). This information is * used to update WPA/RSN IE for the AP. If this field is not * set, the results from previous scan will be used. If no * data for the new AP is found, scan results will be requested * again (without scan request). At this point, the driver is * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is * used). * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *beacon_ies; /** * beacon_ies_len - Length of beacon_ies */ size_t beacon_ies_len; /** * freq - Frequency of the operational channel in MHz */ unsigned int freq; /** * wmm_params - WMM parameters used in this association. */ struct wmm_params wmm_params; /** * addr - Station address (for AP mode) */ const u8 *addr; /** * The following is the key management offload information * @authorized * @key_replay_ctr * @key_replay_ctr_len * @ptk_kck * @ptk_kek_len * @ptk_kek * @ptk_kek_len */ /** * authorized - Status of key management offload, * 1 = successful */ int authorized; /** * key_replay_ctr - Key replay counter value last used * in a valid EAPOL-Key frame */ const u8 *key_replay_ctr; /** * key_replay_ctr_len - The length of key_replay_ctr */ size_t key_replay_ctr_len; /** * ptk_kck - The derived PTK KCK */ const u8 *ptk_kck; /** * ptk_kek_len - The length of ptk_kck */ size_t ptk_kck_len; /** * ptk_kek - The derived PTK KEK * This is used in key management offload and also in FILS SK * offload. */ const u8 *ptk_kek; /** * ptk_kek_len - The length of ptk_kek */ size_t ptk_kek_len; /** * subnet_status - The subnet status: * 0 = unknown, 1 = unchanged, 2 = changed */ u8 subnet_status; /** * The following information is used in FILS SK offload * @fils_erp_next_seq_num * @fils_pmk * @fils_pmk_len * @fils_pmkid */ /** * fils_erp_next_seq_num - The next sequence number to use in * FILS ERP messages */ u16 fils_erp_next_seq_num; /** * fils_pmk - A new PMK if generated in case of FILS * authentication */ const u8 *fils_pmk; /** * fils_pmk_len - Length of fils_pmk */ size_t fils_pmk_len; /** * fils_pmkid - PMKID used or generated in FILS authentication */ const u8 *fils_pmkid; } assoc_info; /** * struct disassoc_info - Data for EVENT_DISASSOC events */ struct disassoc_info { /** * addr - Station address (for AP mode) */ const u8 *addr; /** * reason_code - Reason Code (host byte order) used in * Deauthentication frame */ u16 reason_code; /** * ie - Optional IE(s) in Disassociation frame */ const u8 *ie; /** * ie_len - Length of ie buffer in octets */ size_t ie_len; /** * locally_generated - Whether the frame was locally generated */ int locally_generated; } disassoc_info; /** * struct deauth_info - Data for EVENT_DEAUTH events */ struct deauth_info { /** * addr - Station address (for AP mode) */ const u8 *addr; /** * reason_code - Reason Code (host byte order) used in * Deauthentication frame */ u16 reason_code; /** * ie - Optional IE(s) in Deauthentication frame */ const u8 *ie; /** * ie_len - Length of ie buffer in octets */ size_t ie_len; /** * locally_generated - Whether the frame was locally generated */ int locally_generated; } deauth_info; /** * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE */ struct michael_mic_failure { int unicast; const u8 *src; } michael_mic_failure; /** * struct interface_status - Data for EVENT_INTERFACE_STATUS */ struct interface_status { unsigned int ifindex; char ifname[100]; enum { EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED } ievent; } interface_status; /** * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE */ struct pmkid_candidate { /** BSSID of the PMKID candidate */ u8 bssid[ETH_ALEN]; /** Smaller the index, higher the priority */ int index; /** Whether RSN IE includes pre-authenticate flag */ int preauth; } pmkid_candidate; /** * struct tdls - Data for EVENT_TDLS */ struct tdls { u8 peer[ETH_ALEN]; enum { TDLS_REQUEST_SETUP, TDLS_REQUEST_TEARDOWN, TDLS_REQUEST_DISCOVER, } oper; u16 reason_code; /* for teardown */ } tdls; /** * struct wnm - Data for EVENT_WNM */ struct wnm { u8 addr[ETH_ALEN]; enum { WNM_OPER_SLEEP, } oper; enum { WNM_SLEEP_ENTER, WNM_SLEEP_EXIT } sleep_action; int sleep_intval; u16 reason_code; u8 *buf; u16 buf_len; } wnm; /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * * During FT (IEEE 802.11r) authentication sequence, the driver is * expected to use this event to report received FT IEs (MDIE, FTIE, * RSN IE, TIE, possible resource request) to the supplicant. The FT * IEs for the next message will be delivered through the * struct wpa_driver_ops::update_ft_ies() callback. */ struct ft_ies { const u8 *ies; size_t ies_len; int ft_action; u8 target_ap[ETH_ALEN]; /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ const u8 *ric_ies; /** Length of ric_ies buffer in octets */ size_t ric_ies_len; } ft_ies; /** * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START */ struct ibss_rsn_start { u8 peer[ETH_ALEN]; } ibss_rsn_start; /** * struct auth_info - Data for EVENT_AUTH events */ struct auth_info { u8 peer[ETH_ALEN]; u8 bssid[ETH_ALEN]; u16 auth_type; u16 auth_transaction; u16 status_code; const u8 *ies; size_t ies_len; } auth; /** * struct assoc_reject - Data for EVENT_ASSOC_REJECT events */ struct assoc_reject { /** * bssid - BSSID of the AP that rejected association */ const u8 *bssid; /** * resp_ies - (Re)Association Response IEs * * Optional association data from the driver. This data is not * required WPA, but may be useful for some protocols and as * such, should be reported if this is available to the driver * interface. * * This should start with the first IE (fixed fields before IEs * are not included). */ const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes */ size_t resp_ies_len; /** * status_code - Status Code from (Re)association Response */ u16 status_code; /** * timed_out - Whether failure is due to timeout (etc.) rather * than explicit rejection response from the AP. */ int timed_out; /** * timeout_reason - Reason for the timeout */ const char *timeout_reason; /** * fils_erp_next_seq_num - The next sequence number to use in * FILS ERP messages */ u16 fils_erp_next_seq_num; /** * reason_code - Connection failure reason code from the driver */ enum sta_connect_fail_reason_codes reason_code; } assoc_reject; struct timeout_event { u8 addr[ETH_ALEN]; } timeout_event; /** * struct tx_status - Data for EVENT_TX_STATUS events */ struct tx_status { u16 type; u16 stype; const u8 *dst; const u8 *data; size_t data_len; int ack; } tx_status; /** * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events */ struct rx_from_unknown { const u8 *bssid; const u8 *addr; int wds; } rx_from_unknown; /** * struct rx_mgmt - Data for EVENT_RX_MGMT events */ struct rx_mgmt { const u8 *frame; size_t frame_len; u32 datarate; /** * drv_priv - Pointer to store driver private BSS information * * If not set to NULL, this is used for comparison with * hostapd_data->drv_priv to determine which BSS should process * the frame. */ void *drv_priv; /** * freq - Frequency (in MHz) on which the frame was received */ int freq; /** * ssi_signal - Signal strength in dBm (or 0 if not available) */ int ssi_signal; } rx_mgmt; /** * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events * * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events. */ struct remain_on_channel { /** * freq - Channel frequency in MHz */ unsigned int freq; /** * duration - Duration to remain on the channel in milliseconds */ unsigned int duration; } remain_on_channel; /** * struct scan_info - Optional data for EVENT_SCAN_RESULTS events * @aborted: Whether the scan was aborted * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned) * @num_freqs: Number of entries in freqs array * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard * SSID) * @num_ssids: Number of entries in ssids array * @external_scan: Whether the scan info is for an external scan * @nl_scan_event: 1 if the source of this scan event is a normal scan, * 0 if the source of the scan event is a vendor scan * @scan_start_tsf: Time when the scan started in terms of TSF of the * BSS that the interface that requested the scan is connected to * (if available). * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf * is set. */ struct scan_info { int aborted; const int *freqs; size_t num_freqs; struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; size_t num_ssids; int external_scan; int nl_scan_event; u64 scan_start_tsf; u8 scan_start_tsf_bssid[ETH_ALEN]; } scan_info; /** * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events */ struct rx_probe_req { /** * sa - Source address of the received Probe Request frame */ const u8 *sa; /** * da - Destination address of the received Probe Request frame * or %NULL if not available */ const u8 *da; /** * bssid - BSSID of the received Probe Request frame or %NULL * if not available */ const u8 *bssid; /** * ie - IEs from the Probe Request body */ const u8 *ie; /** * ie_len - Length of ie buffer in octets */ size_t ie_len; /** * signal - signal strength in dBm (or 0 if not available) */ int ssi_signal; } rx_probe_req; /** * struct new_sta - Data for EVENT_NEW_STA events */ struct new_sta { const u8 *addr; } new_sta; /** * struct eapol_rx - Data for EVENT_EAPOL_RX events */ struct eapol_rx { const u8 *src; const u8 *data; size_t data_len; } eapol_rx; /** * signal_change - Data for EVENT_SIGNAL_CHANGE events */ struct wpa_signal_info signal_change; /** * struct best_channel - Data for EVENT_BEST_CHANNEL events * @freq_24: Best 2.4 GHz band channel frequency in MHz * @freq_5: Best 5 GHz band channel frequency in MHz * @freq_overall: Best channel frequency in MHz * * 0 can be used to indicate no preference in either band. */ struct best_channel { int freq_24; int freq_5; int freq_overall; } best_chan; struct unprot_deauth { const u8 *sa; const u8 *da; u16 reason_code; } unprot_deauth; struct unprot_disassoc { const u8 *sa; const u8 *da; u16 reason_code; } unprot_disassoc; /** * struct low_ack - Data for EVENT_STATION_LOW_ACK events * @addr: station address * @num_packets: Number of packets lost (consecutive packets not * acknowledged) */ struct low_ack { u8 addr[ETH_ALEN]; u32 num_packets; } low_ack; /** * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST */ struct ibss_peer_lost { u8 peer[ETH_ALEN]; } ibss_peer_lost; /** * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY */ struct driver_gtk_rekey { const u8 *bssid; const u8 *replay_ctr; } driver_gtk_rekey; /** * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events * @addr: station address */ struct client_poll { u8 addr[ETH_ALEN]; } client_poll; /** * struct eapol_tx_status * @dst: Original destination * @data: Data starting with IEEE 802.1X header (!) * @data_len: Length of data * @ack: Indicates ack or lost frame * * This corresponds to hapd_send_eapol if the frame sent * there isn't just reported as EVENT_TX_STATUS. */ struct eapol_tx_status { const u8 *dst; const u8 *data; int data_len; int ack; } eapol_tx_status; /** * struct ch_switch * @freq: Frequency of new channel in MHz * @ht_enabled: Whether this is an HT channel * @ch_offset: Secondary channel offset * @ch_width: Channel width * @cf1: Center frequency 1 * @cf2: Center frequency 2 */ struct ch_switch { int freq; int ht_enabled; int ch_offset; enum chan_width ch_width; int cf1; int cf2; } ch_switch; /** * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON * @addr: Remote client address * @code: Reason code for connection failure */ struct connect_failed_reason { u8 addr[ETH_ALEN]; enum { MAX_CLIENT_REACHED, BLOCKED_CLIENT } code; } connect_failed_reason; /** * struct dfs_event - Data for radar detected events * @freq: Frequency of the channel in MHz */ struct dfs_event { int freq; int ht_enabled; int chan_offset; enum chan_width chan_width; int cf1; int cf2; } dfs_event; /** * survey_results - Survey result data for EVENT_SURVEY * @freq_filter: Requested frequency survey filter, 0 if request * was for all survey data * @survey_list: Linked list of survey data (struct freq_survey) */ struct survey_results { unsigned int freq_filter; struct dl_list survey_list; /* struct freq_survey */ } survey_results; /** * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED * @initiator: Initiator of the regulatory change * @type: Regulatory change type * @alpha2: Country code (or "" if not available) */ struct channel_list_changed { enum reg_change_initiator initiator; enum reg_type type; char alpha2[3]; } channel_list_changed; /** * freq_range - List of frequency ranges * * This is used as the data with EVENT_AVOID_FREQUENCIES. */ struct wpa_freq_range_list freq_range; /** * struct mesh_peer * * @peer: Peer address * @ies: Beacon IEs * @ie_len: Length of @ies * * Notification of new candidate mesh peer. */ struct mesh_peer { const u8 *peer; const u8 *ies; size_t ie_len; } mesh_peer; /** * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED * @pri_freq: Selected primary frequency * @sec_freq: Selected secondary frequency * @edmg_channel: Selected EDMG channel * @vht_seg0_center_ch: VHT mode Segment0 center channel * The value is the index of the channel center frequency for * 20 MHz, 40 MHz, and 80 MHz channels. The value is the center * frequency index of the primary 80 MHz segment for 160 MHz and * 80+80 MHz channels. * @vht_seg1_center_ch: VHT mode Segment1 center channel * The value is zero for 20 MHz, 40 MHz, and 80 MHz channels. The * value is the index of the channel center frequency for 160 MHz * channels and the center frequency index of the secondary 80 MHz * segment for 80+80 MHz channels. * @ch_width: Selected Channel width by driver. Driver may choose to * change hostapd configured ACS channel width due driver internal * channel restrictions. * hw_mode: Selected band (used with hw_mode=any) */ struct acs_selected_channels { unsigned int pri_freq; unsigned int sec_freq; u8 edmg_channel; u8 vht_seg0_center_ch; u8 vht_seg1_center_ch; u16 ch_width; enum hostapd_hw_mode hw_mode; } acs_selected_channels; /** * struct p2p_lo_stop - Reason code for P2P Listen offload stop event * @reason_code: Reason for stopping offload * P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as * scheduled. * P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to * be stopped. * P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload * parameters. * P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not * supported by device. */ struct p2p_lo_stop { enum { P2P_LO_STOPPED_REASON_COMPLETE = 0, P2P_LO_STOPPED_REASON_RECV_STOP_CMD, P2P_LO_STOPPED_REASON_INVALID_PARAM, P2P_LO_STOPPED_REASON_NOT_SUPPORTED, } reason_code; } p2p_lo_stop; /* For EVENT_EXTERNAL_AUTH */ struct external_auth external_auth; /** * struct sta_opmode - Station's operation mode change event * @addr: The station MAC address * @smps_mode: SMPS mode of the station * @chan_width: Channel width of the station * @rx_nss: RX_NSS of the station * * This is used as data with EVENT_STATION_OPMODE_CHANGED. */ struct sta_opmode { const u8 *addr; enum smps_mode smps_mode; enum chan_width chan_width; u8 rx_nss; } sta_opmode; /** * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS. */ struct wds_sta_interface { const u8 *sta_addr; const char *ifname; enum { INTERFACE_ADDED, INTERFACE_REMOVED } istatus; } wds_sta_interface; /** * struct update_dh - Data for EVENT_UPDATE_DH */ struct update_dh { const u8 *peer; const u8 *ie; size_t ie_len; } update_dh; /** * struct unprot_beacon - Data for EVENT_UNPROT_BEACON */ struct unprot_beacon { const u8 *sa; } unprot_beacon; }; /** * wpa_supplicant_event - Report a driver event for wpa_supplicant * @ctx: Context pointer (wpa_s); this is the ctx variable registered * with struct wpa_driver_ops::init() * @event: event type (defined above) * @data: possible extra data for the event * * Driver wrapper code should call this function whenever an event is received * from the driver. */ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); /** * wpa_supplicant_event_global - Report a driver event for wpa_supplicant * @ctx: Context pointer (wpa_s); this is the ctx variable registered * with struct wpa_driver_ops::init() * @event: event type (defined above) * @data: possible extra data for the event * * Same as wpa_supplicant_event(), but we search for the interface in * wpa_global. */ void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, union wpa_event_data *data); /* * The following inline functions are provided for convenience to simplify * event indication for some of the common events. */ static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, size_t ielen, int reassoc) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.assoc_info.reassoc = reassoc; event.assoc_info.req_ies = ie; event.assoc_info.req_ies_len = ielen; event.assoc_info.addr = addr; wpa_supplicant_event(ctx, EVENT_ASSOC, &event); } static inline void drv_event_disassoc(void *ctx, const u8 *addr) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.disassoc_info.addr = addr; wpa_supplicant_event(ctx, EVENT_DISASSOC, &event); } static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, size_t data_len) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.eapol_rx.src = src; event.eapol_rx.data = data; event.eapol_rx.data_len = data_len; wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); } /* driver_common.c */ void wpa_scan_results_free(struct wpa_scan_results *res); /* Convert wpa_event_type to a string for logging */ const char * event_to_string(enum wpa_event_type event); /* Convert chan_width to a string for logging and control interfaces */ const char * channel_width_to_string(enum chan_width width); int channel_width_to_int(enum chan_width width); int ht_supported(const struct hostapd_hw_modes *mode); int vht_supported(const struct hostapd_hw_modes *mode); struct wowlan_triggers * wpa_get_wowlan_triggers(const char *wowlan_triggers, const struct wpa_driver_capa *capa); /* Convert driver flag to string */ const char * driver_flag_to_string(u64 flag); const char * driver_flag2_to_string(u64 flag2); /* NULL terminated array of linked in driver wrappers */ extern const struct wpa_driver_ops *const wpa_drivers[]; /* Available drivers */ #ifdef CONFIG_DRIVER_WEXT extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ #endif /* CONFIG_DRIVER_WEXT */ #ifdef CONFIG_DRIVER_NL80211 /* driver_nl80211.c */ extern const struct wpa_driver_ops wpa_driver_nl80211_ops; #endif /* CONFIG_DRIVER_NL80211 */ #ifdef CONFIG_DRIVER_HOSTAP extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ #ifdef CONFIG_DRIVER_BSD extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ #ifdef CONFIG_DRIVER_OPENBSD /* driver_openbsd.c */ extern const struct wpa_driver_ops wpa_driver_openbsd_ops; #endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ #endif /* CONFIG_DRIVER_NDIS */ #ifdef CONFIG_DRIVER_WIRED extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ #endif /* CONFIG_DRIVER_WIRED */ #ifdef CONFIG_DRIVER_MACSEC_QCA /* driver_macsec_qca.c */ extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops; #endif /* CONFIG_DRIVER_MACSEC_QCA */ #ifdef CONFIG_DRIVER_MACSEC_LINUX /* driver_macsec_linux.c */ extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops; #endif /* CONFIG_DRIVER_MACSEC_LINUX */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern const struct wpa_driver_ops wpa_driver_roboswitch_ops; #endif /* CONFIG_DRIVER_ROBOSWITCH */ #ifdef CONFIG_DRIVER_ATHEROS /* driver_atheros.c */ extern const struct wpa_driver_ops wpa_driver_atheros_ops; #endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_NONE extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ #endif /* CONFIG_DRIVER_NONE */ #endif /* DRIVER_H */ diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index d19586f06ead..0f0a01d0180b 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1,3203 +1,3203 @@ /* * Driver interaction with Linux nl80211/cfg80211 - Event processing * Copyright (c) 2002-2017, Jouni Malinen * Copyright (c) 2007, Johannes Berg * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include #include "utils/common.h" #include "utils/eloop.h" #include "common/qca-vendor.h" #include "common/qca-vendor-attr.h" #include "common/brcm_vendor.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "driver_nl80211.h" static void nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len, struct nlattr *ack, struct nlattr *cookie); static const char * nl80211_command_to_string(enum nl80211_commands cmd) { #define C2S(x) case x: return #x; switch (cmd) { C2S(NL80211_CMD_UNSPEC) C2S(NL80211_CMD_GET_WIPHY) C2S(NL80211_CMD_SET_WIPHY) C2S(NL80211_CMD_NEW_WIPHY) C2S(NL80211_CMD_DEL_WIPHY) C2S(NL80211_CMD_GET_INTERFACE) C2S(NL80211_CMD_SET_INTERFACE) C2S(NL80211_CMD_NEW_INTERFACE) C2S(NL80211_CMD_DEL_INTERFACE) C2S(NL80211_CMD_GET_KEY) C2S(NL80211_CMD_SET_KEY) C2S(NL80211_CMD_NEW_KEY) C2S(NL80211_CMD_DEL_KEY) C2S(NL80211_CMD_GET_BEACON) C2S(NL80211_CMD_SET_BEACON) C2S(NL80211_CMD_START_AP) C2S(NL80211_CMD_STOP_AP) C2S(NL80211_CMD_GET_STATION) C2S(NL80211_CMD_SET_STATION) C2S(NL80211_CMD_NEW_STATION) C2S(NL80211_CMD_DEL_STATION) C2S(NL80211_CMD_GET_MPATH) C2S(NL80211_CMD_SET_MPATH) C2S(NL80211_CMD_NEW_MPATH) C2S(NL80211_CMD_DEL_MPATH) C2S(NL80211_CMD_SET_BSS) C2S(NL80211_CMD_SET_REG) C2S(NL80211_CMD_REQ_SET_REG) C2S(NL80211_CMD_GET_MESH_CONFIG) C2S(NL80211_CMD_SET_MESH_CONFIG) C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) C2S(NL80211_CMD_GET_REG) C2S(NL80211_CMD_GET_SCAN) C2S(NL80211_CMD_TRIGGER_SCAN) C2S(NL80211_CMD_NEW_SCAN_RESULTS) C2S(NL80211_CMD_SCAN_ABORTED) C2S(NL80211_CMD_REG_CHANGE) C2S(NL80211_CMD_AUTHENTICATE) C2S(NL80211_CMD_ASSOCIATE) C2S(NL80211_CMD_DEAUTHENTICATE) C2S(NL80211_CMD_DISASSOCIATE) C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) C2S(NL80211_CMD_REG_BEACON_HINT) C2S(NL80211_CMD_JOIN_IBSS) C2S(NL80211_CMD_LEAVE_IBSS) C2S(NL80211_CMD_TESTMODE) C2S(NL80211_CMD_CONNECT) C2S(NL80211_CMD_ROAM) C2S(NL80211_CMD_DISCONNECT) C2S(NL80211_CMD_SET_WIPHY_NETNS) C2S(NL80211_CMD_GET_SURVEY) C2S(NL80211_CMD_NEW_SURVEY_RESULTS) C2S(NL80211_CMD_SET_PMKSA) C2S(NL80211_CMD_DEL_PMKSA) C2S(NL80211_CMD_FLUSH_PMKSA) C2S(NL80211_CMD_REMAIN_ON_CHANNEL) C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) C2S(NL80211_CMD_SET_TX_BITRATE_MASK) C2S(NL80211_CMD_REGISTER_FRAME) C2S(NL80211_CMD_FRAME) C2S(NL80211_CMD_FRAME_TX_STATUS) C2S(NL80211_CMD_SET_POWER_SAVE) C2S(NL80211_CMD_GET_POWER_SAVE) C2S(NL80211_CMD_SET_CQM) C2S(NL80211_CMD_NOTIFY_CQM) C2S(NL80211_CMD_SET_CHANNEL) C2S(NL80211_CMD_SET_WDS_PEER) C2S(NL80211_CMD_FRAME_WAIT_CANCEL) C2S(NL80211_CMD_JOIN_MESH) C2S(NL80211_CMD_LEAVE_MESH) C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) C2S(NL80211_CMD_UNPROT_DISASSOCIATE) C2S(NL80211_CMD_NEW_PEER_CANDIDATE) C2S(NL80211_CMD_GET_WOWLAN) C2S(NL80211_CMD_SET_WOWLAN) C2S(NL80211_CMD_START_SCHED_SCAN) C2S(NL80211_CMD_STOP_SCHED_SCAN) C2S(NL80211_CMD_SCHED_SCAN_RESULTS) C2S(NL80211_CMD_SCHED_SCAN_STOPPED) C2S(NL80211_CMD_SET_REKEY_OFFLOAD) C2S(NL80211_CMD_PMKSA_CANDIDATE) C2S(NL80211_CMD_TDLS_OPER) C2S(NL80211_CMD_TDLS_MGMT) C2S(NL80211_CMD_UNEXPECTED_FRAME) C2S(NL80211_CMD_PROBE_CLIENT) C2S(NL80211_CMD_REGISTER_BEACONS) C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) C2S(NL80211_CMD_SET_NOACK_MAP) C2S(NL80211_CMD_CH_SWITCH_NOTIFY) C2S(NL80211_CMD_START_P2P_DEVICE) C2S(NL80211_CMD_STOP_P2P_DEVICE) C2S(NL80211_CMD_CONN_FAILED) C2S(NL80211_CMD_SET_MCAST_RATE) C2S(NL80211_CMD_SET_MAC_ACL) C2S(NL80211_CMD_RADAR_DETECT) C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) C2S(NL80211_CMD_UPDATE_FT_IES) C2S(NL80211_CMD_FT_EVENT) C2S(NL80211_CMD_CRIT_PROTOCOL_START) C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) C2S(NL80211_CMD_GET_COALESCE) C2S(NL80211_CMD_SET_COALESCE) C2S(NL80211_CMD_CHANNEL_SWITCH) C2S(NL80211_CMD_VENDOR) C2S(NL80211_CMD_SET_QOS_MAP) C2S(NL80211_CMD_ADD_TX_TS) C2S(NL80211_CMD_DEL_TX_TS) C2S(NL80211_CMD_GET_MPP) C2S(NL80211_CMD_JOIN_OCB) C2S(NL80211_CMD_LEAVE_OCB) C2S(NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) C2S(NL80211_CMD_TDLS_CHANNEL_SWITCH) C2S(NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH) C2S(NL80211_CMD_WIPHY_REG_CHANGE) C2S(NL80211_CMD_ABORT_SCAN) C2S(NL80211_CMD_START_NAN) C2S(NL80211_CMD_STOP_NAN) C2S(NL80211_CMD_ADD_NAN_FUNCTION) C2S(NL80211_CMD_DEL_NAN_FUNCTION) C2S(NL80211_CMD_CHANGE_NAN_CONFIG) C2S(NL80211_CMD_NAN_MATCH) C2S(NL80211_CMD_SET_MULTICAST_TO_UNICAST) C2S(NL80211_CMD_UPDATE_CONNECT_PARAMS) C2S(NL80211_CMD_SET_PMK) C2S(NL80211_CMD_DEL_PMK) C2S(NL80211_CMD_PORT_AUTHORIZED) C2S(NL80211_CMD_RELOAD_REGDB) C2S(NL80211_CMD_EXTERNAL_AUTH) C2S(NL80211_CMD_STA_OPMODE_CHANGED) C2S(NL80211_CMD_CONTROL_PORT_FRAME) C2S(NL80211_CMD_GET_FTM_RESPONDER_STATS) C2S(NL80211_CMD_PEER_MEASUREMENT_START) C2S(NL80211_CMD_PEER_MEASUREMENT_RESULT) C2S(NL80211_CMD_PEER_MEASUREMENT_COMPLETE) C2S(NL80211_CMD_NOTIFY_RADAR) C2S(NL80211_CMD_UPDATE_OWE_INFO) C2S(NL80211_CMD_PROBE_MESH_LINK) C2S(NL80211_CMD_SET_TID_CONFIG) C2S(NL80211_CMD_UNPROT_BEACON) C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS) C2S(NL80211_CMD_SET_SAR_SPECS) C2S(__NL80211_CMD_AFTER_LAST) } #undef C2S return "NL80211_CMD_UNKNOWN"; } static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && drv->force_connect_cmd) { /* * Avoid reporting two association events that would confuse * the core code. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore auth event when using driver SME"); return; } wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.auth)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " "frame"); return; } os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); event.auth.auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); if (len > 24 + sizeof(mgmt->u.auth)) { event.auth.ies = mgmt->u.auth.variable; event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); } wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); } static void nl80211_parse_wmm_params(struct nlattr *wmm_attr, struct wmm_params *wmm_params) { struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1]; static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = { [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, }; if (!wmm_attr || nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr, wme_policy) || !wmm_info[NL80211_STA_WME_UAPSD_QUEUES]) return; wmm_params->uapsd_queues = nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]); wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO; } static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len, struct nlattr *wmm, struct nlattr *req_ie) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 status; int ssid_len; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && drv->force_connect_cmd) { /* * Avoid reporting two association events that would confuse * the core code. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore assoc event when using driver SME"); return; } wpa_printf(MSG_DEBUG, "nl80211: Associate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.assoc_resp)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " "frame"); return; } status = le_to_host16(mgmt->u.assoc_resp.status_code); if (status != WLAN_STATUS_SUCCESS) { os_memset(&event, 0, sizeof(event)); event.assoc_reject.bssid = mgmt->bssid; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_reject.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; event.assoc_reject.resp_ies_len = len - 24 - sizeof(mgmt->u.assoc_resp); } event.assoc_reject.status_code = status; wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } drv->associated = 1; os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); os_memset(&event, 0, sizeof(event)); event.assoc_info.resp_frame = frame; event.assoc_info.resp_frame_len = len; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; event.assoc_info.resp_ies_len = len - 24 - sizeof(mgmt->u.assoc_resp); } if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); event.assoc_info.req_ies_len = nla_len(req_ie); } /* When this association was initiated outside of wpa_supplicant, * drv->ssid needs to be set here to satisfy later checking. */ ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid); if (ssid_len > 0) { drv->ssid_len = ssid_len; wpa_printf(MSG_DEBUG, "nl80211: Set drv->ssid based on scan res info to '%s'", wpa_ssid_txt(drv->ssid, drv->ssid_len)); } event.assoc_info.freq = drv->assoc_freq; drv->first_bss->freq = drv->assoc_freq; nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } #ifdef CONFIG_DRIVER_NL80211_QCA static int qca_drv_connect_fail_reason_code_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct nlattr *tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); u32 *reason_code = arg; *reason_code = 0; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_VENDOR_DATA]) { wpa_printf(MSG_ERROR, "%s: Vendor data not found", __func__); return NL_SKIP; } nla_parse(tb_sta_info, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX, nla_data(tb[NL80211_ATTR_VENDOR_DATA]), nla_len(tb[NL80211_ATTR_VENDOR_DATA]), NULL); if (!tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE]) { wpa_printf(MSG_INFO, "%s: Vendor attr not found", __func__); return NL_SKIP; } *reason_code = nla_get_u32(tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE]); return NL_SKIP; } static enum qca_sta_connect_fail_reason_codes drv_get_connect_fail_reason_code(struct wpa_driver_nl80211_data *drv) { enum qca_sta_connect_fail_reason_codes reason_code; struct nl_msg *msg; int ret; msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); if (!msg || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO)) { nlmsg_free(msg); return 0; } ret = send_and_recv_msgs(drv, msg, qca_drv_connect_fail_reason_code_handler, &reason_code, NULL, NULL); if (ret) wpa_printf(MSG_DEBUG, "nl80211: Get connect fail reason_code failed: ret=%d (%s)", ret, strerror(-ret)); return reason_code; } static enum sta_connect_fail_reason_codes convert_connect_fail_reason_codes(enum qca_sta_connect_fail_reason_codes reason_code) { switch (reason_code) { case QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: return STA_CONNECT_FAIL_REASON_NO_BSS_FOUND; case QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: return STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL; case QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: return STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED; case QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: return STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED; case QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: return STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL; case QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: return STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED; case QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: return STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED; default: return STA_CONNECT_FAIL_REASON_UNSPECIFIED; } } #endif /* CONFIG_DRIVER_NL80211_QCA */ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *status, struct nlattr *addr, struct nlattr *req_ie, struct nlattr *resp_ie, struct nlattr *timed_out, struct nlattr *timeout_reason, struct nlattr *authorized, struct nlattr *key_replay_ctr, struct nlattr *ptk_kck, struct nlattr *ptk_kek, struct nlattr *subnet_status, struct nlattr *fils_erp_next_seq_num, struct nlattr *fils_pmk, struct nlattr *fils_pmkid) { union wpa_event_data event; const u8 *ssid = NULL; u16 status_code; int ssid_len; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* * Avoid reporting two association events that would confuse * the core code. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " "when using userspace SME", cmd); return; } drv->connect_reassoc = 0; status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS; if (cmd == NL80211_CMD_CONNECT) { wpa_printf(MSG_DEBUG, "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)", status_code, drv->ignore_next_local_disconnect); } else if (cmd == NL80211_CMD_ROAM) { wpa_printf(MSG_DEBUG, "nl80211: Roam event"); } os_memset(&event, 0, sizeof(event)); if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) { if (addr) event.assoc_reject.bssid = nla_data(addr); if (drv->ignore_next_local_disconnect) { drv->ignore_next_local_disconnect = 0; if (!event.assoc_reject.bssid || (os_memcmp(event.assoc_reject.bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0)) { /* * Ignore the event that came without a BSSID or * for the old connection since this is likely * not relevant to the new Connect command. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore connection failure event triggered during reassociation"); return; } } if (resp_ie) { event.assoc_reject.resp_ies = nla_data(resp_ie); event.assoc_reject.resp_ies_len = nla_len(resp_ie); } event.assoc_reject.status_code = status_code; event.assoc_reject.timed_out = timed_out != NULL; if (timed_out && timeout_reason) { enum nl80211_timeout_reason reason; reason = nla_get_u32(timeout_reason); switch (reason) { case NL80211_TIMEOUT_SCAN: event.assoc_reject.timeout_reason = "scan"; break; case NL80211_TIMEOUT_AUTH: event.assoc_reject.timeout_reason = "auth"; break; case NL80211_TIMEOUT_ASSOC: event.assoc_reject.timeout_reason = "assoc"; break; default: break; } } if (fils_erp_next_seq_num) event.assoc_reject.fils_erp_next_seq_num = nla_get_u16(fils_erp_next_seq_num); #ifdef CONFIG_DRIVER_NL80211_QCA if (drv->get_sta_info_vendor_cmd_avail) { enum qca_sta_connect_fail_reason_codes reason_code; reason_code = drv_get_connect_fail_reason_code(drv); event.assoc_reject.reason_code = convert_connect_fail_reason_codes(reason_code); } #endif /* CONFIG_DRIVER_NL80211_QCA */ wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } drv->associated = 1; if (addr) { os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); } if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); event.assoc_info.req_ies_len = nla_len(req_ie); if (cmd == NL80211_CMD_ROAM) { ssid = get_ie(event.assoc_info.req_ies, event.assoc_info.req_ies_len, WLAN_EID_SSID); if (ssid && ssid[1] > 0 && ssid[1] <= 32) { drv->ssid_len = ssid[1]; os_memcpy(drv->ssid, ssid + 2, ssid[1]); wpa_printf(MSG_DEBUG, "nl80211: Set drv->ssid based on req_ie to '%s'", wpa_ssid_txt(drv->ssid, drv->ssid_len)); } } } if (resp_ie) { event.assoc_info.resp_ies = nla_data(resp_ie); event.assoc_info.resp_ies_len = nla_len(resp_ie); } event.assoc_info.freq = nl80211_get_assoc_freq(drv); drv->first_bss->freq = drv->assoc_freq; if ((!ssid || ssid[1] == 0 || ssid[1] > 32) && (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) { /* When this connection was initiated outside of wpa_supplicant, * drv->ssid needs to be set here to satisfy later checking. */ drv->ssid_len = ssid_len; wpa_printf(MSG_DEBUG, "nl80211: Set drv->ssid based on scan res info to '%s'", wpa_ssid_txt(drv->ssid, drv->ssid_len)); } if (authorized && nla_get_u8(authorized)) { event.assoc_info.authorized = 1; wpa_printf(MSG_DEBUG, "nl80211: connection authorized"); } if (key_replay_ctr) { event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr); event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr); } if (ptk_kck) { event.assoc_info.ptk_kck = nla_data(ptk_kck); event.assoc_info.ptk_kck_len = nla_len(ptk_kck); } if (ptk_kek) { event.assoc_info.ptk_kek = nla_data(ptk_kek); event.assoc_info.ptk_kek_len = nla_len(ptk_kek); } if (subnet_status) { /* * At least for now, this is only available from * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that * attribute has the same values 0, 1, 2 as are used in the * variable here, so no mapping between different values are * needed. */ event.assoc_info.subnet_status = nla_get_u8(subnet_status); } if (fils_erp_next_seq_num) event.assoc_info.fils_erp_next_seq_num = nla_get_u16(fils_erp_next_seq_num); if (fils_pmk) { event.assoc_info.fils_pmk = nla_data(fils_pmk); event.assoc_info.fils_pmk_len = nla_len(fils_pmk); } if (fils_pmkid) event.assoc_info.fils_pmkid = nla_data(fils_pmkid); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); /* Avoid a race condition by stopping to ignore any following * disconnection events now that the driver has indicated it is * connected since that connection could have been triggered by a roam * operation that happened in parallel with the disconnection request. */ drv->ignore_next_local_disconnect = 0; } static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, struct nlattr *reason, struct nlattr *addr, struct nlattr *by_ap) { union wpa_event_data data; unsigned int locally_generated = by_ap == NULL; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* * Avoid reporting two disassociation events that could * confuse the core code. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " "event when using userspace SME"); return; } if (drv->ignore_next_local_disconnect) { drv->ignore_next_local_disconnect = 0; if (locally_generated) { wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " "event triggered during reassociation"); return; } wpa_printf(MSG_WARNING, "nl80211: Was expecting local " "disconnect but got another disconnect " "event first"); } wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); nl80211_mark_disconnected(drv); os_memset(&data, 0, sizeof(data)); if (reason) data.deauth_info.reason_code = nla_get_u16(reason); data.deauth_info.locally_generated = by_ap == NULL; wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); } static int calculate_chan_offset(int width, int freq, int cf1, int cf2) { int freq1 = 0; switch (convert2width(width)) { case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: return 0; case CHAN_WIDTH_40: freq1 = cf1 - 10; break; case CHAN_WIDTH_80: freq1 = cf1 - 30; break; case CHAN_WIDTH_160: freq1 = cf1 - 70; break; case CHAN_WIDTH_80P80: freq1 = cf1 - 30; break; case CHAN_WIDTH_UNKNOWN: case CHAN_WIDTH_2160: case CHAN_WIDTH_4320: case CHAN_WIDTH_6480: case CHAN_WIDTH_8640: /* FIXME: implement this */ return 0; } return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; } static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, struct nlattr *ifindex, struct nlattr *freq, struct nlattr *type, struct nlattr *bw, struct nlattr *cf1, struct nlattr *cf2, int finished) { struct i802_bss *bss; union wpa_event_data data; int ht_enabled = 1; int chan_offset = 0; int ifidx; wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event", finished ? "" : " started"); if (!freq) return; ifidx = nla_get_u32(ifindex); bss = get_bss_ifindex(drv, ifidx); if (bss == NULL) { wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", ifidx); return; } if (type) { enum nl80211_channel_type ch_type = nla_get_u32(type); wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type); switch (ch_type) { case NL80211_CHAN_NO_HT: ht_enabled = 0; break; case NL80211_CHAN_HT20: break; case NL80211_CHAN_HT40PLUS: chan_offset = 1; break; case NL80211_CHAN_HT40MINUS: chan_offset = -1; break; } } else if (bw && cf1) { /* This can happen for example with VHT80 ch switch */ chan_offset = calculate_chan_offset(nla_get_u32(bw), nla_get_u32(freq), nla_get_u32(cf1), cf2 ? nla_get_u32(cf2) : 0); wpa_printf(MSG_DEBUG, "nl80211: Calculated channel offset: %d", chan_offset); } else { wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); } os_memset(&data, 0, sizeof(data)); data.ch_switch.freq = nla_get_u32(freq); data.ch_switch.ht_enabled = ht_enabled; data.ch_switch.ch_offset = chan_offset; if (bw) data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); if (cf1) data.ch_switch.cf1 = nla_get_u32(cf1); if (cf2) data.ch_switch.cf2 = nla_get_u32(cf2); if (finished) bss->freq = data.ch_switch.freq; drv->assoc_freq = data.ch_switch.freq; wpa_supplicant_event(bss->ctx, finished ? EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data); } static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *addr) { union wpa_event_data event; enum wpa_event_type ev; if (nla_len(addr) != ETH_ALEN) return; wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, cmd, MAC2STR((u8 *) nla_data(addr))); if (cmd == NL80211_CMD_AUTHENTICATE) ev = EVENT_AUTH_TIMED_OUT; else if (cmd == NL80211_CMD_ASSOCIATE) ev = EVENT_ASSOC_TIMED_OUT; else return; os_memset(&event, 0, sizeof(event)); os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); wpa_supplicant_event(drv->ctx, ev, &event); } static void mlme_event_mgmt(struct i802_bss *bss, struct nlattr *freq, struct nlattr *sig, const u8 *frame, size_t len) { struct wpa_driver_nl80211_data *drv = bss->drv; const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 fc, stype; int ssi_signal = 0; int rx_freq = 0; wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24) { wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); return; } fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); if (sig) ssi_signal = (s32) nla_get_u32(sig); os_memset(&event, 0, sizeof(event)); if (freq) { event.rx_mgmt.freq = nla_get_u32(freq); rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; } wpa_printf(MSG_DEBUG, "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u", MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), rx_freq, ssi_signal, fc, le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), (unsigned int) len); event.rx_mgmt.frame = frame; event.rx_mgmt.frame_len = len; event.rx_mgmt.ssi_signal = ssi_signal; event.rx_mgmt.drv_priv = bss; wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); } static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, struct nlattr *cookie, const u8 *frame, size_t len, struct nlattr *ack) { union wpa_event_data event; const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame; u16 fc = le_to_host16(hdr->frame_control); u64 cookie_val = 0; if (cookie) cookie_val = nla_get_u64(cookie); wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event A1=" MACSTR " %sstype=%d cookie=0x%llx%s ack=%d", MAC2STR(hdr->addr1), WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ? "not-mgmt " : "", WLAN_FC_GET_STYPE(fc), (long long unsigned int) cookie_val, cookie ? "" : "(N/A)", ack != NULL); if (cookie_val && cookie_val == drv->eapol_tx_cookie && len >= ETH_HLEN && WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) { wpa_printf(MSG_DEBUG, "nl80211: Work around misdelivered control port TX status for EAPOL"); nl80211_control_port_frame_tx_status(drv, frame, len, ack, cookie); return; } if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) return; if (!is_ap_interface(drv->nlmode) && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { if (!cookie) return; wpa_printf(MSG_DEBUG, "nl80211: Frame TX status: cookie=0x%llx%s (ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_frame_cookie ? " (match)" : " (unknown)", ack != NULL); if (cookie_val != drv->send_frame_cookie) return; } else if (!is_ap_interface(drv->nlmode) && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { wpa_printf(MSG_DEBUG, "nl80211: Authentication frame TX status: ack=%d", !!ack); } os_memset(&event, 0, sizeof(event)); event.tx_status.type = WLAN_FC_GET_TYPE(fc); event.tx_status.stype = WLAN_FC_GET_STYPE(fc); event.tx_status.dst = hdr->addr1; event.tx_status.data = frame; event.tx_status.data_len = len; event.tx_status.ack = ack != NULL; wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); } static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, enum wpa_event_type type, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; const u8 *bssid = NULL; u16 reason_code = 0; if (type == EVENT_DEAUTH) wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); else wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len >= 24) { bssid = mgmt->bssid; if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && !drv->associated && os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { /* * Avoid issues with some roaming cases where * disconnection event for the old AP may show up after * we have started connection with the new AP. * In case of locally generated event clear * ignore_next_local_deauth as well, to avoid next local * deauth event be wrongly ignored. */ if (!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN)) { wpa_printf(MSG_DEBUG, "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag"); drv->ignore_next_local_deauth = 0; } else { wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, MAC2STR(bssid), MAC2STR(drv->auth_attempt_bssid)); } return; } if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && drv->connect_reassoc && drv->associated && os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 && os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) { /* * Avoid issues with some roaming cases where * disconnection event for the old AP may show up after * we have started connection with the new AP. */ wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already connecting with " MACSTR, MAC2STR(bssid), MAC2STR(drv->auth_attempt_bssid)); return; } if (drv->associated != 0 && os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { /* * We have presumably received this deauth as a * response to a clear_state_mismatch() outgoing * deauth. Don't let it take us offline! */ wpa_printf(MSG_DEBUG, "nl80211: Deauth received " "from Unknown BSSID " MACSTR " -- ignoring", MAC2STR(bssid)); return; } } nl80211_mark_disconnected(drv); os_memset(&event, 0, sizeof(event)); /* Note: Same offset for Reason Code in both frame subtypes */ if (len >= 24 + sizeof(mgmt->u.deauth)) reason_code = le_to_host16(mgmt->u.deauth.reason_code); if (type == EVENT_DISASSOC) { event.disassoc_info.locally_generated = !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); event.disassoc_info.addr = bssid; event.disassoc_info.reason_code = reason_code; if (frame + len > mgmt->u.disassoc.variable) { event.disassoc_info.ie = mgmt->u.disassoc.variable; event.disassoc_info.ie_len = frame + len - mgmt->u.disassoc.variable; } } else { event.deauth_info.locally_generated = !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); if (drv->ignore_deauth_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth"); drv->ignore_deauth_event = 0; if (event.deauth_info.locally_generated) drv->ignore_next_local_deauth = 0; return; } if (drv->ignore_next_local_deauth) { drv->ignore_next_local_deauth = 0; if (event.deauth_info.locally_generated) { wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request"); return; } wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first"); } event.deauth_info.addr = bssid; event.deauth_info.reason_code = reason_code; if (frame + len > mgmt->u.deauth.variable) { event.deauth_info.ie = mgmt->u.deauth.variable; event.deauth_info.ie_len = frame + len - mgmt->u.deauth.variable; } } wpa_supplicant_event(drv->ctx, type, &event); } static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, enum wpa_event_type type, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 reason_code = 0; if (type == EVENT_UNPROT_DEAUTH) wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); else wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); if (len < 24) return; mgmt = (const struct ieee80211_mgmt *) frame; os_memset(&event, 0, sizeof(event)); /* Note: Same offset for Reason Code in both frame subtypes */ if (len >= 24 + sizeof(mgmt->u.deauth)) reason_code = le_to_host16(mgmt->u.deauth.reason_code); if (type == EVENT_UNPROT_DISASSOC) { event.unprot_disassoc.sa = mgmt->sa; event.unprot_disassoc.da = mgmt->da; event.unprot_disassoc.reason_code = reason_code; } else { event.unprot_deauth.sa = mgmt->sa; event.unprot_deauth.da = mgmt->da; event.unprot_deauth.reason_code = reason_code; } wpa_supplicant_event(drv->ctx, type, &event); } static void mlme_event_unprot_beacon(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; if (len < 24) return; mgmt = (const struct ieee80211_mgmt *) frame; os_memset(&event, 0, sizeof(event)); event.unprot_beacon.sa = mgmt->sa; wpa_supplicant_event(drv->ctx, EVENT_UNPROT_BEACON, &event); } static void mlme_event(struct i802_bss *bss, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, struct nlattr *cookie, struct nlattr *sig, struct nlattr *wmm, struct nlattr *req_ie) { struct wpa_driver_nl80211_data *drv = bss->drv; const u8 *data; size_t len; if (timed_out && addr) { mlme_timeout_event(drv, cmd, addr); return; } if (frame == NULL) { wpa_printf(MSG_DEBUG, "nl80211: MLME event %d (%s) without frame data", cmd, nl80211_command_to_string(cmd)); return; } data = nla_data(frame); len = nla_len(frame); if (len < 4 + 2 * ETH_ALEN) { wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR ") - too short", cmd, nl80211_command_to_string(cmd), bss->ifname, MAC2STR(bss->addr)); return; } wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR ") A1=" MACSTR " A2=" MACSTR, cmd, nl80211_command_to_string(cmd), bss->ifname, MAC2STR(bss->addr), MAC2STR(data + 4), MAC2STR(data + 4 + ETH_ALEN)); if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && (is_zero_ether_addr(bss->rand_addr) || os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) && os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " "for foreign address", bss->ifname); return; } wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", nla_data(frame), nla_len(frame)); switch (cmd) { case NL80211_CMD_AUTHENTICATE: mlme_event_auth(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_ASSOCIATE: mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm, req_ie); break; case NL80211_CMD_DEAUTHENTICATE: mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DISASSOCIATE: mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME: mlme_event_mgmt(bss, freq, sig, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME_TX_STATUS: mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), nla_len(frame), ack); break; case NL80211_CMD_UNPROT_DEAUTHENTICATE: mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_UNPROT_DISASSOCIATE: mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_UNPROT_BEACON: mlme_event_unprot_beacon(drv, nla_data(frame), nla_len(frame)); break; default: break; } } static void mlme_event_michael_mic_failure(struct i802_bss *bss, struct nlattr *tb[]) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); os_memset(&data, 0, sizeof(data)); if (tb[NL80211_ATTR_MAC]) { wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", nla_data(tb[NL80211_ATTR_MAC]), nla_len(tb[NL80211_ATTR_MAC])); data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); } if (tb[NL80211_ATTR_KEY_SEQ]) { wpa_hexdump(MSG_DEBUG, "nl80211: TSC", nla_data(tb[NL80211_ATTR_KEY_SEQ]), nla_len(tb[NL80211_ATTR_KEY_SEQ])); } if (tb[NL80211_ATTR_KEY_TYPE]) { enum nl80211_key_type key_type = nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); if (key_type == NL80211_KEYTYPE_PAIRWISE) data.michael_mic_failure.unicast = 1; } else data.michael_mic_failure.unicast = 1; if (tb[NL80211_ATTR_KEY_IDX]) { u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); } wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { unsigned int freq; union wpa_event_data event; if (tb[NL80211_ATTR_MAC] == NULL) { wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " "event"); return; } os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); drv->associated = 1; wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", MAC2STR(drv->bssid)); freq = nl80211_get_assoc_freq(drv); if (freq) { wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz", freq); drv->first_bss->freq = freq; } os_memset(&event, 0, sizeof(event)); event.assoc_info.freq = freq; wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, int cancel_event, struct nlattr *tb[]) { unsigned int freq, chan_type, duration; union wpa_event_data data; u64 cookie; if (tb[NL80211_ATTR_WIPHY_FREQ]) freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); else freq = 0; if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); else chan_type = 0; if (tb[NL80211_ATTR_DURATION]) duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); else duration = 0; if (tb[NL80211_ATTR_COOKIE]) cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); else cookie = 0; wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", cancel_event, freq, chan_type, duration, (long long unsigned int) cookie, cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); if (cookie != drv->remain_on_chan_cookie) return; /* not for us */ if (cancel_event) drv->pending_remain_on_chan = 0; os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = freq; data.remain_on_channel.duration = duration; wpa_supplicant_event(drv->ctx, cancel_event ? EVENT_CANCEL_REMAIN_ON_CHANNEL : EVENT_REMAIN_ON_CHANNEL, &data); } static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { union wpa_event_data data; os_memset(&data, 0, sizeof(data)); if (tb[NL80211_ATTR_IE]) { data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); } if (tb[NL80211_ATTR_IE_RIC]) { data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); } if (tb[NL80211_ATTR_MAC]) os_memcpy(data.ft_ies.target_ap, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, MAC2STR(data.ft_ies.target_ap)); wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data); } static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv, struct i802_bss *bss, struct nlattr *tb[]) { union wpa_event_data data; if (!is_ap_interface(drv->nlmode)) return; if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE]) return; os_memset(&data, 0, sizeof(data)); data.update_dh.peer = nla_data(tb[NL80211_ATTR_MAC]); data.update_dh.ie = nla_data(tb[NL80211_ATTR_IE]); data.update_dh.ie_len = nla_len(tb[NL80211_ATTR_IE]); wpa_printf(MSG_DEBUG, "nl80211: DH event - peer " MACSTR, MAC2STR(data.update_dh.peer)); wpa_supplicant_event(bss->ctx, EVENT_UPDATE_DH, &data); } static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, struct nlattr *tb[], int external_scan) { union wpa_event_data event; struct nlattr *nl; int rem; struct scan_info *info; -#define MAX_REPORT_FREQS 50 +#define MAX_REPORT_FREQS 100 int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; if (!external_scan && drv->scan_for_auth) { drv->scan_for_auth = 0; wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " "cfg80211 BSS entry"); wpa_driver_nl80211_authenticate_retry(drv); return; } os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; info->external_scan = external_scan; info->nl_scan_event = 1; if (tb[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { struct wpa_driver_scan_ssid *s = &info->ssids[info->num_ssids]; s->ssid = nla_data(nl); s->ssid_len = nla_len(nl); wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", wpa_ssid_txt(s->ssid, s->ssid_len)); info->num_ssids++; if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) break; } } if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { - char msg[300], *pos, *end; + char msg[500], *pos, *end; int res; pos = msg; end = pos + sizeof(msg); *pos = '\0'; nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) { freqs[num_freqs] = nla_get_u32(nl); res = os_snprintf(pos, end - pos, " %d", freqs[num_freqs]); if (!os_snprintf_error(end - pos, res)) pos += res; num_freqs++; if (num_freqs == MAX_REPORT_FREQS - 1) break; } info->freqs = freqs; info->num_freqs = num_freqs; wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", msg); } if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] && tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) { info->scan_start_tsf = nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]); os_memcpy(info->scan_start_tsf_bssid, nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]), ETH_ALEN); } wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG }, }; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; union wpa_event_data ed; int res; if (tb[NL80211_ATTR_CQM] == NULL || nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], cqm_policy)) { wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); return; } os_memset(&ed, 0, sizeof(ed)); if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { if (!tb[NL80211_ATTR_MAC]) return; os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); ed.low_ack.num_packets = nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]); wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR " (num_packets %u)", MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets); wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); return; } if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) { wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event"); wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL); return; } if (cqm[NL80211_ATTR_CQM_TXE_RATE] && cqm[NL80211_ATTR_CQM_TXE_PKTS] && cqm[NL80211_ATTR_CQM_TXE_INTVL] && cqm[NL80211_ATTR_MAC]) { wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR " (rate: %u pkts: %u interval: %u)", MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])), nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]), nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]), nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL])); return; } if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) { wpa_printf(MSG_DEBUG, "nl80211: Not a CQM RSSI threshold event"); return; } event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI high"); ed.signal_change.above_threshold = 1; } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI low"); ed.signal_change.above_threshold = 0; } else { wpa_printf(MSG_DEBUG, "nl80211: Unknown CQM RSSI threshold event: %d", event); return; } /* * nl80211_get_link_signal() and nl80211_get_link_noise() set default * values in case querying the driver fails. */ res = nl80211_get_link_signal(drv, &ed.signal_change); if (res == 0) { wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", ed.signal_change.current_signal, ed.signal_change.current_txrate); } else { wpa_printf(MSG_DEBUG, "nl80211: Querying the driver for signal info failed"); } res = nl80211_get_link_noise(drv, &ed.signal_change); if (res == 0) { wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", ed.signal_change.current_noise); } else { wpa_printf(MSG_DEBUG, "nl80211: Querying the driver for noise info failed"); } wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); } static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { const u8 *addr; union wpa_event_data data; if (drv->nlmode != NL80211_IFTYPE_MESH_POINT || !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE]) return; addr = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR, MAC2STR(addr)); os_memset(&data, 0, sizeof(data)); data.mesh_peer.peer = addr; data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]); data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]); wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data); } static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, struct i802_bss *bss, struct nlattr **tb) { u8 *addr; union wpa_event_data data; if (tb[NL80211_ATTR_MAC] == NULL) return; addr = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { u8 *ies = NULL; size_t ies_len = 0; if (tb[NL80211_ATTR_IE]) { ies = nla_data(tb[NL80211_ATTR_IE]); ies_len = nla_len(tb[NL80211_ATTR_IE]); } wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); drv_event_assoc(bss->ctx, addr, ies, ies_len, 0); return; } if (drv->nlmode != NL80211_IFTYPE_ADHOC) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data); } static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, struct i802_bss *bss, struct nlattr **tb) { u8 *addr; union wpa_event_data data; if (tb[NL80211_ATTR_MAC] == NULL) return; addr = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, MAC2STR(addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { drv_event_disassoc(bss->ctx, addr); return; } if (drv->nlmode != NL80211_IFTYPE_ADHOC) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); wpa_supplicant_event(bss->ctx, EVENT_IBSS_PEER_LOST, &data); } static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { [NL80211_REKEY_DATA_KEK] = { .minlen = NL80211_KEK_LEN, .maxlen = NL80211_KEK_LEN, }, [NL80211_REKEY_DATA_KCK] = { .minlen = NL80211_KCK_LEN, .maxlen = NL80211_KCK_LEN, }, [NL80211_REKEY_DATA_REPLAY_CTR] = { .minlen = NL80211_REPLAY_CTR_LEN, .maxlen = NL80211_REPLAY_CTR_LEN, }, }; union wpa_event_data data; if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_REKEY_DATA] || nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, tb[NL80211_ATTR_REKEY_DATA], rekey_policy) || !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) return; os_memset(&data, 0, sizeof(data)); data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, MAC2STR(data.driver_gtk_rekey.bssid)); data.driver_gtk_rekey.replay_ctr = nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); } static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, [NL80211_PMKSA_CANDIDATE_BSSID] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN, }, [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, }; union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] || nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) || !cand[NL80211_PMKSA_CANDIDATE_INDEX] || !cand[NL80211_PMKSA_CANDIDATE_BSSID]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.pmkid_candidate.bssid, nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); data.pmkid_candidate.index = nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); data.pmkid_candidate.preauth = cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); } static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; const u8 *addr; u64 cookie = 0; addr = nla_data(tb[NL80211_ATTR_MAC]); if (!addr) return; if (tb[NL80211_ATTR_COOKIE]) cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR " ack=%d cookie=%llu)", MAC2STR(addr), tb[NL80211_ATTR_ACK] != NULL, (long long unsigned int) cookie); if (!tb[NL80211_ATTR_ACK]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.client_poll.addr, addr, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); } static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { case NL80211_TDLS_SETUP: wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " MACSTR, MAC2STR(data.tdls.peer)); data.tdls.oper = TDLS_REQUEST_SETUP; break; case NL80211_TDLS_TEARDOWN: wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " MACSTR, MAC2STR(data.tdls.peer)); data.tdls.oper = TDLS_REQUEST_TEARDOWN; break; case NL80211_TDLS_DISCOVERY_REQ: wpa_printf(MSG_DEBUG, "nl80211: TDLS discovery request for peer " MACSTR, MAC2STR(data.tdls.peer)); data.tdls.oper = TDLS_REQUEST_DISCOVER; break; default: wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " "event"); return; } if (tb[NL80211_ATTR_REASON_CODE]) { data.tdls.reason_code = nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); } wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); } static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); } static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; u32 reason; wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) return; os_memset(&data, 0, sizeof(data)); os_memcpy(data.connect_failed_reason.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); switch (reason) { case NL80211_CONN_FAIL_MAX_CLIENTS: wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); data.connect_failed_reason.code = MAX_CLIENT_REACHED; break; case NL80211_CONN_FAIL_BLOCKED_CLIENT: wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR " tried to connect", MAC2STR(data.connect_failed_reason.addr)); data.connect_failed_reason.code = BLOCKED_CLIENT; break; default: wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " "%u", reason); return; } wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); } static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; enum nl80211_radar_event event_type; if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) return; os_memset(&data, 0, sizeof(data)); data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); /* Check HT params */ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { data.dfs_event.ht_enabled = 1; data.dfs_event.chan_offset = 0; switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { case NL80211_CHAN_NO_HT: data.dfs_event.ht_enabled = 0; break; case NL80211_CHAN_HT20: break; case NL80211_CHAN_HT40PLUS: data.dfs_event.chan_offset = 1; break; case NL80211_CHAN_HT40MINUS: data.dfs_event.chan_offset = -1; break; } } /* Get VHT params */ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) data.dfs_event.chan_width = convert2width(nla_get_u32( tb[NL80211_ATTR_CHANNEL_WIDTH])); if (tb[NL80211_ATTR_CENTER_FREQ1]) data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); if (tb[NL80211_ATTR_CENTER_FREQ2]) data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", data.dfs_event.freq, data.dfs_event.ht_enabled, data.dfs_event.chan_offset, data.dfs_event.chan_width, data.dfs_event.cf1, data.dfs_event.cf2); switch (event_type) { case NL80211_RADAR_DETECTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); break; case NL80211_RADAR_CAC_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); break; case NL80211_RADAR_CAC_ABORTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); break; case NL80211_RADAR_NOP_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); break; case NL80211_RADAR_PRE_CAC_EXPIRED: wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED, &data); break; case NL80211_RADAR_CAC_STARTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); break; default: wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " "received", event_type); break; } } static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, int wds) { struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data event; if (!tb[NL80211_ATTR_MAC]) return; os_memset(&event, 0, sizeof(event)); event.rx_from_unknown.bssid = bss->addr; event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); event.rx_from_unknown.wds = wds; wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); } #ifdef CONFIG_DRIVER_NL80211_QCA static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { u32 i, count; union wpa_event_data event; struct wpa_freq_range *range = NULL; const struct qca_avoid_freq_list *freq_range; freq_range = (const struct qca_avoid_freq_list *) data; if (len < sizeof(freq_range->count)) return; count = freq_range->count; if (len < sizeof(freq_range->count) + count * sizeof(struct qca_avoid_freq_range)) { wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", (unsigned int) len); return; } if (count > 0) { range = os_calloc(count, sizeof(struct wpa_freq_range)); if (range == NULL) return; } os_memset(&event, 0, sizeof(event)); for (i = 0; i < count; i++) { unsigned int idx = event.freq_range.num; range[idx].min = freq_range->range[i].start_freq; range[idx].max = freq_range->range[i].end_freq; wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", range[idx].min, range[idx].max); if (range[idx].min > range[idx].max) { wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); continue; } event.freq_range.num++; } event.freq_range.range = range; wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); os_free(range); } static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode) { switch (hw_mode) { case QCA_ACS_MODE_IEEE80211B: return HOSTAPD_MODE_IEEE80211B; case QCA_ACS_MODE_IEEE80211G: return HOSTAPD_MODE_IEEE80211G; case QCA_ACS_MODE_IEEE80211A: return HOSTAPD_MODE_IEEE80211A; case QCA_ACS_MODE_IEEE80211AD: return HOSTAPD_MODE_IEEE80211AD; case QCA_ACS_MODE_IEEE80211ANY: return HOSTAPD_MODE_IEEE80211ANY; default: return NUM_HOSTAPD_MODES; } } static unsigned int chan_to_freq(struct wpa_driver_nl80211_data *drv, u8 chan, enum hostapd_hw_mode hw_mode) { if (hw_mode == NUM_HOSTAPD_MODES) { /* For drivers that do not report ACS_HW_MODE */ u16 num_modes, flags; struct hostapd_hw_modes *modes; u8 dfs_domain; int i; modes = nl80211_get_hw_feature_data(drv->first_bss, &num_modes, &flags, &dfs_domain); if (!modes) { wpa_printf(MSG_DEBUG, "nl80211: Fetching hardware mode failed"); goto try_2_4_or_5; } if (num_modes == 1) hw_mode = modes[0].mode; for (i = 0; i < num_modes; i++) { os_free(modes[i].channels); os_free(modes[i].rates); } os_free(modes); } if (hw_mode == HOSTAPD_MODE_IEEE80211AD) { if (chan >= 1 && chan <= 6) return 56160 + (2160 * chan); return 0; } try_2_4_or_5: if (chan >= 1 && chan <= 13) return 2407 + 5 * chan; if (chan == 14) return 2484; if (chan >= 36 && chan <= 177) return 5000 + 5 * chan; return 0; } static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; union wpa_event_data event; u8 chan; wpa_printf(MSG_DEBUG, "nl80211: ACS channel selection vendor event received"); if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, (struct nlattr *) data, len, NULL) || (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY] && !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]) || (!tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY] && !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])) return; os_memset(&event, 0, sizeof(event)); event.acs_selected_channels.hw_mode = NUM_HOSTAPD_MODES; if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode); if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES || event.acs_selected_channels.hw_mode == HOSTAPD_MODE_IEEE80211ANY) { wpa_printf(MSG_DEBUG, "nl80211: Invalid hw_mode %d in ACS selection event", hw_mode); return; } } if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]) { event.acs_selected_channels.pri_freq = nla_get_u32( tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]); } else { chan = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]); event.acs_selected_channels.pri_freq = chan_to_freq(drv, chan, event.acs_selected_channels.hw_mode); } if (tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]) { event.acs_selected_channels.sec_freq = nla_get_u32( tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]); } else { chan = nla_get_u8( tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]); event.acs_selected_channels.sec_freq = chan_to_freq(drv, chan, event.acs_selected_channels.hw_mode); } if (tb[QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL]) event.acs_selected_channels.edmg_channel = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL]); if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) event.acs_selected_channels.vht_seg0_center_ch = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]); if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]) event.acs_selected_channels.vht_seg1_center_ch = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]); if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) event.acs_selected_channels.ch_width = nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]); wpa_printf(MSG_INFO, "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d", event.acs_selected_channels.pri_freq, event.acs_selected_channels.sec_freq, event.acs_selected_channels.ch_width, event.acs_selected_channels.vht_seg0_center_ch, event.acs_selected_channels.vht_seg1_center_ch, event.acs_selected_channels.hw_mode, event.acs_selected_channels.edmg_channel); /* Ignore ACS channel list check for backwards compatibility */ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); } static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1]; u8 *bssid; wpa_printf(MSG_DEBUG, "nl80211: Key management roam+auth vendor event received"); if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX, (struct nlattr *) data, len, NULL) || !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] || nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN || !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] || !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] || !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED]) return; bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]); wpa_printf(MSG_DEBUG, " * roam BSSID " MACSTR, MAC2STR(bssid)); mlme_event_connect(drv, NL80211_CMD_ROAM, NULL, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], NULL, NULL, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]); } static void qca_nl80211_key_mgmt_auth_handler(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { if (!drv->roam_indication_done) { wpa_printf(MSG_DEBUG, "nl80211: Pending roam indication, delay processing roam+auth vendor event"); os_get_reltime(&drv->pending_roam_ind_time); os_free(drv->pending_roam_data); drv->pending_roam_data = os_memdup(data, len); if (!drv->pending_roam_data) return; drv->pending_roam_data_len = len; return; } drv->roam_indication_done = false; qca_nl80211_key_mgmt_auth(drv, data, len); } static void qca_nl80211_dfs_offload_radar_event( struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length) { union wpa_event_data data; struct nlattr *tb[NL80211_ATTR_MAX + 1]; wpa_printf(MSG_DEBUG, "nl80211: DFS offload radar vendor event received"); if (nla_parse(tb, NL80211_ATTR_MAX, (struct nlattr *) msg, length, NULL)) return; if (!tb[NL80211_ATTR_WIPHY_FREQ]) { wpa_printf(MSG_INFO, "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event"); return; } os_memset(&data, 0, sizeof(data)); data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz", data.dfs_event.freq); /* Check HT params */ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { data.dfs_event.ht_enabled = 1; data.dfs_event.chan_offset = 0; switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { case NL80211_CHAN_NO_HT: data.dfs_event.ht_enabled = 0; break; case NL80211_CHAN_HT20: break; case NL80211_CHAN_HT40PLUS: data.dfs_event.chan_offset = 1; break; case NL80211_CHAN_HT40MINUS: data.dfs_event.chan_offset = -1; break; } } /* Get VHT params */ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) data.dfs_event.chan_width = convert2width(nla_get_u32( tb[NL80211_ATTR_CHANNEL_WIDTH])); if (tb[NL80211_ATTR_CENTER_FREQ1]) data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); if (tb[NL80211_ATTR_CENTER_FREQ2]) data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, " "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", data.dfs_event.freq, data.dfs_event.ht_enabled, data.dfs_event.chan_offset, data.dfs_event.chan_width, data.dfs_event.cf1, data.dfs_event.cf2); switch (subcmd) { case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); break; default: wpa_printf(MSG_DEBUG, "nl80211: Unknown DFS offload radar event %d received", subcmd); break; } } static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, u8 *data, size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; u64 cookie = 0; union wpa_event_data event; struct scan_info *info; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, (struct nlattr *) data, len, NULL) || !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) return; cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); if (cookie != drv->vendor_scan_cookie) { /* External scan trigger event, ignore */ return; } /* Cookie match, own scan */ os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->external_scan = 0; info->nl_scan_event = 0; drv->scan_state = SCAN_STARTED; wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event); } static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, struct nlattr *tb[], int external_scan) { union wpa_event_data event; struct nlattr *nl; int rem; struct scan_info *info; int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; info->external_scan = external_scan; if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) { struct wpa_driver_scan_ssid *s = &info->ssids[info->num_ssids]; s->ssid = nla_data(nl); s->ssid_len = nla_len(nl); wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", wpa_ssid_txt(s->ssid, s->ssid_len)); info->num_ssids++; if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) break; } } if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { - char msg[300], *pos, *end; + char msg[500], *pos, *end; int res; pos = msg; end = pos + sizeof(msg); *pos = '\0'; nla_for_each_nested(nl, tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], rem) { freqs[num_freqs] = nla_get_u32(nl); res = os_snprintf(pos, end - pos, " %d", freqs[num_freqs]); if (!os_snprintf_error(end - pos, res)) pos += res; num_freqs++; if (num_freqs == MAX_REPORT_FREQS - 1) break; } info->freqs = freqs; info->num_freqs = num_freqs; wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", msg); } wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, u8 *data, size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; u64 cookie = 0; enum scan_status status; int external_scan; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, (struct nlattr *) data, len, NULL) || !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] || !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) return; status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]); if (status >= VENDOR_SCAN_STATUS_MAX) return; /* invalid status */ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); if (cookie != drv->vendor_scan_cookie) { /* Event from an external scan, get scan results */ external_scan = 1; } else { external_scan = 0; if (status == VENDOR_SCAN_STATUS_NEW_RESULTS) drv->scan_state = SCAN_COMPLETED; else drv->scan_state = SCAN_ABORTED; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); drv->vendor_scan_cookie = 0; drv->last_scan_cmd = 0; } send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb, external_scan); } static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv, u8 *data, size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1]; union wpa_event_data event; wpa_printf(MSG_DEBUG, "nl80211: P2P listen offload stop vendor event received"); if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX, (struct nlattr *) data, len, NULL) || !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]) return; os_memset(&event, 0, sizeof(event)); event.p2p_lo_stop.reason_code = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]); wpa_printf(MSG_DEBUG, "nl80211: P2P Listen offload stop reason: %d", event.p2p_lo_stop.reason_code); wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event); } #endif /* CONFIG_DRIVER_NL80211_QCA */ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *data, size_t len) { switch (subcmd) { case QCA_NL80211_VENDOR_SUBCMD_TEST: wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len); break; #ifdef CONFIG_DRIVER_NL80211_QCA case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: qca_nl80211_avoid_freq(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: qca_nl80211_key_mgmt_auth_handler(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: qca_nl80211_acs_select_ch(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: qca_nl80211_scan_trigger_event(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE: qca_nl80211_scan_done_event(drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: qca_nl80211_p2p_lo_stop_event(drv, data, len); break; #endif /* CONFIG_DRIVER_NL80211_QCA */ default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported QCA vendor event %u", subcmd); break; } } #ifdef CONFIG_DRIVER_NL80211_BRCM static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { struct nlattr *tb[BRCM_VENDOR_ATTR_ACS_LAST + 1]; union wpa_event_data event; wpa_printf(MSG_DEBUG, "nl80211: BRCM ACS channel selection vendor event received"); if (nla_parse(tb, BRCM_VENDOR_ATTR_ACS_LAST, (struct nlattr *) data, len, NULL) || !tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ] || !tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]) return; os_memset(&event, 0, sizeof(event)); if (tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]) event.acs_selected_channels.pri_freq = nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]); if (tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]) event.acs_selected_channels.sec_freq = nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]); if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) event.acs_selected_channels.vht_seg0_center_ch = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]); if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) event.acs_selected_channels.vht_seg1_center_ch = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]); if (tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]) event.acs_selected_channels.ch_width = nla_get_u16(tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]); if (tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]) { event.acs_selected_channels.hw_mode = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]); if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES || event.acs_selected_channels.hw_mode == HOSTAPD_MODE_IEEE80211ANY) { wpa_printf(MSG_DEBUG, "nl80211: Invalid hw_mode %d in ACS selection event", event.acs_selected_channels.hw_mode); return; } } wpa_printf(MSG_DEBUG, "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d", event.acs_selected_channels.pri_freq, event.acs_selected_channels.sec_freq, event.acs_selected_channels.ch_width, event.acs_selected_channels.vht_seg0_center_ch, event.acs_selected_channels.vht_seg1_center_ch, event.acs_selected_channels.hw_mode); wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); } static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *data, size_t len) { wpa_printf(MSG_DEBUG, "nl80211: Got BRCM vendor event %u", subcmd); switch (subcmd) { case BRCM_VENDOR_EVENT_PRIV_STR: case BRCM_VENDOR_EVENT_HANGED: /* Dump the event on to the console */ wpa_msg(NULL, MSG_INFO, "%s", data); break; case BRCM_VENDOR_EVENT_ACS: brcm_nl80211_acs_select_ch(drv, data, len); break; default: wpa_printf(MSG_DEBUG, "%s: Ignore unsupported BRCM vendor event %u", __func__, subcmd); break; } } #endif /* CONFIG_DRIVER_NL80211_BRCM */ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { u32 vendor_id, subcmd, wiphy = 0; int wiphy_idx; u8 *data = NULL; size_t len = 0; if (!tb[NL80211_ATTR_VENDOR_ID] || !tb[NL80211_ATTR_VENDOR_SUBCMD]) return; vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); if (tb[NL80211_ATTR_WIPHY]) wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", wiphy, vendor_id, subcmd); if (tb[NL80211_ATTR_VENDOR_DATA]) { data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); } wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", wiphy, wiphy_idx); return; } #ifdef ANDROID #ifdef ANDROID_LIB_EVENT wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data, len); #endif /* ANDROID_LIB_EVENT */ #endif /* ANDROID */ switch (vendor_id) { case OUI_QCA: nl80211_vendor_event_qca(drv, subcmd, data, len); break; #ifdef CONFIG_DRIVER_NL80211_BRCM case OUI_BRCM: nl80211_vendor_event_brcm(drv, subcmd, data, len); break; #endif /* CONFIG_DRIVER_NL80211_BRCM */ default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); break; } } static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { union wpa_event_data data; enum nl80211_reg_initiator init; wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) return; os_memset(&data, 0, sizeof(data)); init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]); wpa_printf(MSG_DEBUG, " * initiator=%d", init); switch (init) { case NL80211_REGDOM_SET_BY_CORE: data.channel_list_changed.initiator = REGDOM_SET_BY_CORE; break; case NL80211_REGDOM_SET_BY_USER: data.channel_list_changed.initiator = REGDOM_SET_BY_USER; break; case NL80211_REGDOM_SET_BY_DRIVER: data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER; break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE; break; } if (tb[NL80211_ATTR_REG_TYPE]) { enum nl80211_reg_type type; type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); wpa_printf(MSG_DEBUG, " * type=%d", type); switch (type) { case NL80211_REGDOM_TYPE_COUNTRY: data.channel_list_changed.type = REGDOM_TYPE_COUNTRY; break; case NL80211_REGDOM_TYPE_WORLD: data.channel_list_changed.type = REGDOM_TYPE_WORLD; break; case NL80211_REGDOM_TYPE_CUSTOM_WORLD: data.channel_list_changed.type = REGDOM_TYPE_CUSTOM_WORLD; break; case NL80211_REGDOM_TYPE_INTERSECTION: data.channel_list_changed.type = REGDOM_TYPE_INTERSECTION; break; } } if (tb[NL80211_ATTR_REG_ALPHA2]) { os_strlcpy(data.channel_list_changed.alpha2, nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]), sizeof(data.channel_list_changed.alpha2)); wpa_printf(MSG_DEBUG, " * alpha2=%s", data.channel_list_changed.alpha2); } wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); } static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq) { static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, }; struct nlattr *tb[NL80211_FREQUENCY_ATTR_MAX + 1]; u32 freq = 0, max_tx_power = 0; nla_parse(tb, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), freq_policy); if (tb[NL80211_FREQUENCY_ATTR_FREQ]) freq = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]); if (tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) max_tx_power = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]); wpa_printf(MSG_DEBUG, "nl80211: Channel (%s): freq=%u max_tx_power=%u%s%s%s", title, freq, max_tx_power, tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "", tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "", tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : ""); } static void nl80211_reg_beacon_hint_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { union wpa_event_data data; wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); os_memset(&data, 0, sizeof(data)); data.channel_list_changed.initiator = REGDOM_BEACON_HINT; if (tb[NL80211_ATTR_FREQ_BEFORE]) nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]); if (tb[NL80211_ATTR_FREQ_AFTER]) nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]); wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); } static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data event; enum nl80211_external_auth_action act; if (!tb[NL80211_ATTR_AKM_SUITES] || !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] || !tb[NL80211_ATTR_BSSID] || !tb[NL80211_ATTR_SSID]) return; os_memset(&event, 0, sizeof(event)); act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]); switch (act) { case NL80211_EXTERNAL_AUTH_START: event.external_auth.action = EXT_AUTH_START; break; case NL80211_EXTERNAL_AUTH_ABORT: event.external_auth.action = EXT_AUTH_ABORT; break; default: return; } event.external_auth.key_mgmt_suite = nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]); event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]); if (event.external_auth.ssid_len > SSID_MAX_LEN) return; event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]); event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]); wpa_printf(MSG_DEBUG, "nl80211: External auth action: %u, AKM: 0x%x", event.external_auth.action, event.external_auth.key_mgmt_suite); wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event); } static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { const u8 *addr; if (!tb[NL80211_ATTR_MAC] || nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) { wpa_printf(MSG_DEBUG, "nl80211: Ignore port authorized event without BSSID"); return; } addr = nla_data(tb[NL80211_ATTR_MAC]); if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore port authorized event for " MACSTR " (not the currently connected BSSID " MACSTR ")", MAC2STR(addr), MAC2STR(drv->bssid)); return; } wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL); } static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data ed; u8 smps_mode, max_bw; if (!tb[NL80211_ATTR_MAC] || (!tb[NL80211_ATTR_CHANNEL_WIDTH] && !tb[NL80211_ATTR_SMPS_MODE] && !tb[NL80211_ATTR_NSS])) return; ed.sta_opmode.smps_mode = SMPS_INVALID; ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; ed.sta_opmode.rx_nss = 0xff; ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]); if (tb[NL80211_ATTR_SMPS_MODE]) { smps_mode = nla_get_u8(tb[NL80211_ATTR_SMPS_MODE]); switch (smps_mode) { case NL80211_SMPS_OFF: ed.sta_opmode.smps_mode = SMPS_OFF; break; case NL80211_SMPS_STATIC: ed.sta_opmode.smps_mode = SMPS_STATIC; break; case NL80211_SMPS_DYNAMIC: ed.sta_opmode.smps_mode = SMPS_DYNAMIC; break; default: ed.sta_opmode.smps_mode = SMPS_INVALID; break; } } if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]); switch (max_bw) { case NL80211_CHAN_WIDTH_20_NOHT: ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT; break; case NL80211_CHAN_WIDTH_20: ed.sta_opmode.chan_width = CHAN_WIDTH_20; break; case NL80211_CHAN_WIDTH_40: ed.sta_opmode.chan_width = CHAN_WIDTH_40; break; case NL80211_CHAN_WIDTH_80: ed.sta_opmode.chan_width = CHAN_WIDTH_80; break; case NL80211_CHAN_WIDTH_80P80: ed.sta_opmode.chan_width = CHAN_WIDTH_80P80; break; case NL80211_CHAN_WIDTH_160: ed.sta_opmode.chan_width = CHAN_WIDTH_160; break; default: ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; break; } } if (tb[NL80211_ATTR_NSS]) ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]); wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed); } static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { u8 *src_addr; u16 ethertype; if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_FRAME] || !tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) return; src_addr = nla_data(tb[NL80211_ATTR_MAC]); ethertype = nla_get_u16(tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); switch (ethertype) { case ETH_P_RSN_PREAUTH: wpa_printf(MSG_INFO, "nl80211: Got pre-auth frame from " MACSTR " over control port unexpectedly", MAC2STR(src_addr)); break; case ETH_P_PAE: drv_event_eapol_rx(drv->ctx, src_addr, nla_data(tb[NL80211_ATTR_FRAME]), nla_len(tb[NL80211_ATTR_FRAME])); break; default: wpa_printf(MSG_INFO, "nl80211: Unxpected ethertype 0x%04x from " MACSTR " over control port", ethertype, MAC2STR(src_addr)); break; } } static void nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len, struct nlattr *ack, struct nlattr *cookie) { union wpa_event_data event; if (!cookie || len < ETH_HLEN) return; wpa_printf(MSG_DEBUG, "nl80211: Control port TX status (ack=%d), cookie=%llu", ack != NULL, (long long unsigned int) nla_get_u64(cookie)); os_memset(&event, 0, sizeof(event)); event.eapol_tx_status.dst = frame; event.eapol_tx_status.data = frame + ETH_HLEN; event.eapol_tx_status.data_len = len - ETH_HLEN; event.eapol_tx_status.ack = ack != NULL; wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); } static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { struct wpa_driver_nl80211_data *drv = bss->drv; int external_scan_event = 0; struct nlattr *frame = tb[NL80211_ATTR_FRAME]; wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", cmd, nl80211_command_to_string(cmd), bss->ifname); #ifdef CONFIG_DRIVER_NL80211_QCA if (cmd == NL80211_CMD_ROAM && (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { if (drv->pending_roam_data) { struct os_reltime now, age; os_get_reltime(&now); os_reltime_sub(&now, &drv->pending_roam_ind_time, &age); if (age.sec == 0 && age.usec < 100000) { wpa_printf(MSG_DEBUG, "nl80211: Process pending roam+auth vendor event"); qca_nl80211_key_mgmt_auth( drv, drv->pending_roam_data, drv->pending_roam_data_len); } os_free(drv->pending_roam_data); drv->pending_roam_data = NULL; return; } /* * Device will use roam+auth vendor event to indicate * roaming, so ignore the regular roam event. */ drv->roam_indication_done = true; wpa_printf(MSG_DEBUG, "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth", cmd); return; } #endif /* CONFIG_DRIVER_NL80211_QCA */ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && (cmd == NL80211_CMD_NEW_SCAN_RESULTS || cmd == NL80211_CMD_SCAN_ABORTED)) nl80211_restore_ap_mode(bss); switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); drv->scan_state = SCAN_STARTED; if (drv->scan_for_auth) { /* * Cannot indicate EVENT_SCAN_STARTED here since we skip * EVENT_SCAN_RESULTS in scan_for_auth case and the * upper layer implementation could get confused about * scanning state. */ wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth"); break; } wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); break; case NL80211_CMD_START_SCHED_SCAN: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); drv->scan_state = SCHED_SCAN_STARTED; break; case NL80211_CMD_SCHED_SCAN_STOPPED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); drv->scan_state = SCHED_SCAN_STOPPED; wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); break; case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); drv->last_scan_cmd = 0; } else { external_scan_event = 1; } send_scan_event(drv, 0, tb, external_scan_event); break; case NL80211_CMD_SCHED_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; send_scan_event(drv, 0, tb, 0); break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_ABORTED; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { /* * Need to indicate that scan results are available in * order not to make wpa_supplicant stop its scanning. */ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); drv->last_scan_cmd = 0; } else { external_scan_event = 1; } send_scan_event(drv, 1, tb, external_scan_event); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: case NL80211_CMD_FRAME_TX_STATUS: case NL80211_CMD_UNPROT_DEAUTHENTICATE: case NL80211_CMD_UNPROT_DISASSOCIATE: mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], tb[NL80211_ATTR_STA_WME], tb[NL80211_ATTR_REQ_IE]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: mlme_event_connect(drv, cmd, tb[NL80211_ATTR_STATUS_CODE], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_TIMEOUT_REASON], NULL, NULL, NULL, tb[NL80211_ATTR_FILS_KEK], NULL, tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM], tb[NL80211_ATTR_PMK], tb[NL80211_ATTR_PMKID]); break; case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: mlme_event_ch_switch(drv, tb[NL80211_ATTR_IFINDEX], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], tb[NL80211_ATTR_CHANNEL_WIDTH], tb[NL80211_ATTR_CENTER_FREQ1], tb[NL80211_ATTR_CENTER_FREQ2], 0); break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, tb[NL80211_ATTR_IFINDEX], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], tb[NL80211_ATTR_CHANNEL_WIDTH], tb[NL80211_ATTR_CENTER_FREQ1], tb[NL80211_ATTR_CENTER_FREQ2], 1); break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_DISCONNECTED_BY_AP]); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: mlme_event_michael_mic_failure(bss, tb); break; case NL80211_CMD_JOIN_IBSS: mlme_event_join_ibss(drv, tb); break; case NL80211_CMD_REMAIN_ON_CHANNEL: mlme_event_remain_on_channel(drv, 0, tb); break; case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: mlme_event_remain_on_channel(drv, 1, tb); break; case NL80211_CMD_NOTIFY_CQM: nl80211_cqm_event(drv, tb); break; case NL80211_CMD_REG_CHANGE: case NL80211_CMD_WIPHY_REG_CHANGE: nl80211_reg_change_event(drv, tb); break; case NL80211_CMD_REG_BEACON_HINT: nl80211_reg_beacon_hint_event(drv, tb); break; case NL80211_CMD_NEW_STATION: nl80211_new_station_event(drv, bss, tb); break; case NL80211_CMD_DEL_STATION: nl80211_del_station_event(drv, bss, tb); break; case NL80211_CMD_SET_REKEY_OFFLOAD: nl80211_rekey_offload_event(drv, tb); break; case NL80211_CMD_PMKSA_CANDIDATE: nl80211_pmksa_candidate_event(drv, tb); break; case NL80211_CMD_PROBE_CLIENT: nl80211_client_probe_event(drv, tb); break; case NL80211_CMD_TDLS_OPER: nl80211_tdls_oper_event(drv, tb); break; case NL80211_CMD_CONN_FAILED: nl80211_connect_failed_event(drv, tb); break; case NL80211_CMD_FT_EVENT: mlme_event_ft_event(drv, tb); break; case NL80211_CMD_RADAR_DETECT: nl80211_radar_event(drv, tb); break; case NL80211_CMD_STOP_AP: nl80211_stop_ap(drv, tb); break; case NL80211_CMD_VENDOR: nl80211_vendor_event(drv, tb); break; case NL80211_CMD_NEW_PEER_CANDIDATE: nl80211_new_peer_candidate(drv, tb); break; case NL80211_CMD_PORT_AUTHORIZED: nl80211_port_authorized(drv, tb); break; case NL80211_CMD_STA_OPMODE_CHANGED: nl80211_sta_opmode_change_event(drv, tb); break; case NL80211_CMD_UPDATE_OWE_INFO: mlme_event_dh_event(drv, bss, tb); break; case NL80211_CMD_UNPROT_BEACON: if (frame) mlme_event_unprot_beacon(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: if (!frame) break; nl80211_control_port_frame_tx_status(drv, nla_data(frame), nla_len(frame), tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE]); break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); break; } } int process_global_event(struct nl_msg *msg, void *arg) { struct nl80211_global *global = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct wpa_driver_nl80211_data *drv, *tmp; int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1; struct i802_bss *bss; u64 wdev_id = 0; int wdev_id_set = 0; int wiphy_idx_set = 0; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); else if (tb[NL80211_ATTR_WDEV]) { wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); wdev_id_set = 1; } else if (tb[NL80211_ATTR_WIPHY]) { wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); wiphy_idx_set = 1; } dl_list_for_each_safe(drv, tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { for (bss = drv->first_bss; bss; bss = bss->next) { if (wiphy_idx_set) wiphy_idx = nl80211_get_wiphy_index(bss); if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) || ifidx == bss->ifindex || (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) || (wdev_id_set && bss->wdev_id_set && wdev_id == bss->wdev_id)) { do_process_drv_event(bss, gnlh->cmd, tb); return NL_SKIP; } } wpa_printf(MSG_DEBUG, "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx)", gnlh->cmd, nl80211_command_to_string(gnlh->cmd), ifidx, (long long unsigned int) wdev_id); } return NL_SKIP; } int process_bss_event(struct nl_msg *msg, void *arg) { struct i802_bss *bss = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", gnlh->cmd, nl80211_command_to_string(gnlh->cmd), bss->ifname); switch (gnlh->cmd) { case NL80211_CMD_FRAME: case NL80211_CMD_FRAME_TX_STATUS: mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], tb[NL80211_ATTR_STA_WME], NULL); break; case NL80211_CMD_UNEXPECTED_FRAME: nl80211_spurious_frame(bss, tb, 0); break; case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: nl80211_spurious_frame(bss, tb, 1); break; case NL80211_CMD_EXTERNAL_AUTH: nl80211_external_auth(bss->drv, tb); break; case NL80211_CMD_CONTROL_PORT_FRAME: nl80211_control_port_frame(bss->drv, tb); break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); break; } return NL_SKIP; } diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index fc70cf1962db..a162deb9ef6b 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -1,508 +1,510 @@ /* * EAP-WSC server for Wi-Fi Protected Setup * Copyright (c) 2007-2008, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "eap_i.h" #include "eap_common/eap_wsc_common.h" #include "p2p/p2p.h" #include "wps/wps.h" struct eap_wsc_data { enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; enum wsc_op_code in_op_code, out_op_code; size_t out_used; size_t fragment_size; struct wps_data *wps; int ext_reg_timeout; }; #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_wsc_state_txt(int state) { switch (state) { case START: return "START"; case MESG: return "MESG"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; case DONE: return "DONE"; case FAIL: return "FAIL"; default: return "?"; } } #endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_wsc_state(struct eap_wsc_data *data, int state) { wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", eap_wsc_state_txt(data->state), eap_wsc_state_txt(state)); data->state = state; } static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx) { struct eap_sm *sm = eloop_ctx; struct eap_wsc_data *data = timeout_ctx; if (sm->method_pending != METHOD_PENDING_WAIT) return; wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External " "Registrar"); data->ext_reg_timeout = 1; eap_sm_pending_cb(sm); } static void * eap_wsc_init(struct eap_sm *sm) { struct eap_wsc_data *data; int registrar; struct wps_config cfg; if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN && os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) registrar = 0; /* Supplicant is Registrar */ else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN && os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) registrar = 1; /* Supplicant is Enrollee */ else { wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", sm->identity, sm->identity_len); return NULL; } data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = registrar ? START : MESG; data->registrar = registrar; os_memset(&cfg, 0, sizeof(cfg)); cfg.wps = sm->cfg->wps; cfg.registrar = registrar; if (registrar) { if (!sm->cfg->wps || !sm->cfg->wps->registrar) { wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not " "initialized"); os_free(data); return NULL; } } else { if (sm->user == NULL || sm->user->password == NULL) { /* * In theory, this should not really be needed, but * Windows 7 uses Registrar mode to probe AP's WPS * capabilities before trying to use Enrollee and fails * if the AP does not allow that probing to happen.. */ wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) " "configured for Enrollee functionality - " "allow for probing capabilities (M1)"); } else { cfg.pin = sm->user->password; cfg.pin_len = sm->user->password_len; } } cfg.assoc_wps_ie = sm->assoc_wps_ie; cfg.peer_addr = sm->peer_addr; #ifdef CONFIG_P2P if (sm->assoc_p2p_ie) { - wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P " - "client"); - cfg.use_psk_key = 1; + if (!sm->cfg->wps->use_passphrase) { + wpa_printf(MSG_DEBUG, + "EAP-WSC: Prefer PSK format for non-6 GHz P2P client"); + cfg.use_psk_key = 1; + } cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie); } #endif /* CONFIG_P2P */ cfg.pbc_in_m1 = sm->cfg->pbc_in_m1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); return NULL; } data->fragment_size = sm->cfg->fragment_size > 0 ? sm->cfg->fragment_size : WSC_FRAGMENT_SIZE; return data; } static void eap_wsc_reset(struct eap_sm *sm, void *priv) { struct eap_wsc_data *data = priv; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); wps_deinit(data->wps); os_free(data); } static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm, struct eap_wsc_data *data, u8 id) { struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " "request"); return NULL; } wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start"); wpabuf_put_u8(req, WSC_Start); /* Op-Code */ wpabuf_put_u8(req, 0); /* Flags */ return req; } static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) { struct wpabuf *req; u8 flags; size_t send_len, plen; flags = 0; send_len = wpabuf_len(data->out_buf) - data->out_used; if (2 + send_len > data->fragment_size) { send_len = data->fragment_size - 2; flags |= WSC_FLAGS_MF; if (data->out_used == 0) { flags |= WSC_FLAGS_LF; send_len -= 2; } } plen = 2 + send_len; if (flags & WSC_FLAGS_LF) plen += 2; req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " "request"); return NULL; } wpabuf_put_u8(req, data->out_op_code); /* Op-Code */ wpabuf_put_u8(req, flags); /* Flags */ if (flags & WSC_FLAGS_LF) wpabuf_put_be16(req, wpabuf_len(data->out_buf)); wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, send_len); data->out_used += send_len; if (data->out_used == wpabuf_len(data->out_buf)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; eap_wsc_state(data, MESG); } else { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); eap_wsc_state(data, WAIT_FRAG_ACK); } return req; } static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_wsc_data *data = priv; switch (data->state) { case START: return eap_wsc_build_start(sm, data, id); case MESG: if (data->out_buf == NULL) { data->out_buf = wps_get_msg(data->wps, &data->out_op_code); if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to " "receive message from WPS"); return NULL; } data->out_used = 0; } /* fall through */ case WAIT_FRAG_ACK: return eap_wsc_build_msg(data, id); case FRAG_ACK: return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST); default: wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in " "buildReq", data->state); return NULL; } } static bool eap_wsc_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { const u8 *pos; size_t len; pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, respData, &len); if (pos == NULL || len < 2) { wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame"); return true; } return false; } static int eap_wsc_process_cont(struct eap_wsc_data *data, const u8 *buf, size_t len, u8 op_code) { /* Process continuation of a pending message */ if (op_code != data->in_op_code) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " "fragment (expected %d)", op_code, data->in_op_code); eap_wsc_state(data, FAIL); return -1; } if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); eap_wsc_state(data, FAIL); return -1; } wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu " "bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); return 0; } static int eap_wsc_process_fragment(struct eap_wsc_data *data, u8 flags, u8 op_code, u16 message_length, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length " "field in a fragmented packet"); return -1; } if (data->in_buf == NULL) { /* First fragment of the message */ data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " "message"); return -1; } data->in_op_code = op_code; wpabuf_put_data(data->in_buf, buf, len); wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in " "first fragment, waiting for %lu bytes more", (unsigned long) len, (unsigned long) wpabuf_tailroom(data->in_buf)); } return 0; } static void eap_wsc_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_wsc_data *data = priv; const u8 *start, *pos, *end; size_t len; u8 op_code, flags; u16 message_length = 0; enum wps_process_res res; struct wpabuf tmpbuf; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); if (data->ext_reg_timeout) { eap_wsc_state(data, FAIL); return; } pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, respData, &len); if (pos == NULL || len < 2) return; /* Should not happen; message already verified */ start = pos; end = start + len; op_code = *pos++; flags = *pos++; if (flags & WSC_FLAGS_LF) { if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); return; } message_length = WPA_GET_BE16(pos); pos += 2; if (message_length < end - pos || message_length > 50000) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); return; } } wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " "Flags 0x%x Message Length %d", op_code, flags, message_length); if (data->state == WAIT_FRAG_ACK) { if (op_code != WSC_FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " "in WAIT_FRAG_ACK state", op_code); eap_wsc_state(data, FAIL); return; } wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); eap_wsc_state(data, MESG); return; } if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && op_code != WSC_Done) { wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", op_code); eap_wsc_state(data, FAIL); return; } if (data->in_buf && eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { eap_wsc_state(data, FAIL); return; } if (flags & WSC_FLAGS_MF) { if (eap_wsc_process_fragment(data, flags, op_code, message_length, pos, end - pos) < 0) eap_wsc_state(data, FAIL); else eap_wsc_state(data, FRAG_ACK); return; } if (data->in_buf == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&tmpbuf, pos, end - pos); data->in_buf = &tmpbuf; } res = wps_process_msg(data->wps, op_code, data->in_buf); switch (res) { case WPS_DONE: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " "successfully - report EAP failure"); eap_wsc_state(data, FAIL); break; case WPS_CONTINUE: eap_wsc_state(data, MESG); break; case WPS_FAILURE: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); eap_wsc_state(data, FAIL); break; case WPS_PENDING: eap_wsc_state(data, MESG); sm->method_pending = METHOD_PENDING_WAIT; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout, sm, data); break; } if (data->in_buf != &tmpbuf) wpabuf_free(data->in_buf); data->in_buf = NULL; } static bool eap_wsc_isDone(struct eap_sm *sm, void *priv) { struct eap_wsc_data *data = priv; return data->state == FAIL; } static bool eap_wsc_isSuccess(struct eap_sm *sm, void *priv) { /* EAP-WSC will always result in EAP-Failure */ return false; } static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) { /* Recommended retransmit times: retransmit timeout 5 seconds, * per-message timeout 15 seconds, i.e., 3 tries. */ sm->MaxRetrans = 2; /* total 3 attempts */ return 5; } int eap_server_wsc_register(void) { struct eap_method *eap; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, "WSC"); if (eap == NULL) return -1; eap->init = eap_wsc_init; eap->reset = eap_wsc_reset; eap->buildReq = eap_wsc_buildReq; eap->check = eap_wsc_check; eap->process = eap_wsc_process; eap->isDone = eap_wsc_isDone; eap->isSuccess = eap_wsc_isSuccess; eap->getTimeout = eap_wsc_getTimeout; return eap_server_method_register(eap); } diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 1aa98f1a877a..9ac505735cbb 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1,5577 +1,5651 @@ /* * Wi-Fi Direct - P2P module * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx); static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev); static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq); static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len); static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx); static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); /* * p2p_scan recovery timeout * * Many drivers are using 30 second timeout on scan results. Allow a bit larger * timeout for this to avoid hitting P2P timeout unnecessarily. */ #define P2P_SCAN_TIMEOUT 35 /** * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer * entries will be removed */ #ifndef P2P_PEER_EXPIRATION_AGE #define P2P_PEER_EXPIRATION_AGE 60 #endif /* P2P_PEER_EXPIRATION_AGE */ void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; struct os_reltime now; size_t i; os_get_reltime(&now); dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) continue; if (dev == p2p->go_neg_peer) { /* * GO Negotiation is in progress with the peer, so * don't expire the peer entry until GO Negotiation * fails or times out. */ continue; } if (p2p->cfg->go_connected && p2p->cfg->go_connected(p2p->cfg->cb_ctx, dev->info.p2p_device_addr)) { /* * We are connected as a client to a group in which the * peer is the GO, so do not expire the peer entry. */ os_get_reltime(&dev->last_seen); continue; } for (i = 0; i < p2p->num_groups; i++) { if (p2p_group_is_client_connected( p2p->groups[i], dev->info.p2p_device_addr)) break; } if (i < p2p->num_groups) { /* * The peer is connected as a client in a group where * we are the GO, so do not expire the peer entry. */ os_get_reltime(&dev->last_seen); continue; } p2p_dbg(p2p, "Expiring old peer entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); dl_list_del(&dev->list); p2p_device_free(p2p, dev); } } static const char * p2p_state_txt(int state) { switch (state) { case P2P_IDLE: return "IDLE"; case P2P_SEARCH: return "SEARCH"; case P2P_CONNECT: return "CONNECT"; case P2P_CONNECT_LISTEN: return "CONNECT_LISTEN"; case P2P_GO_NEG: return "GO_NEG"; case P2P_LISTEN_ONLY: return "LISTEN_ONLY"; case P2P_WAIT_PEER_CONNECT: return "WAIT_PEER_CONNECT"; case P2P_WAIT_PEER_IDLE: return "WAIT_PEER_IDLE"; case P2P_SD_DURING_FIND: return "SD_DURING_FIND"; case P2P_PROVISIONING: return "PROVISIONING"; case P2P_PD_DURING_FIND: return "PD_DURING_FIND"; case P2P_INVITE: return "INVITE"; case P2P_INVITE_LISTEN: return "INVITE_LISTEN"; default: return "?"; } } const char * p2p_get_state_txt(struct p2p_data *p2p) { return p2p_state_txt(p2p->state); } struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p) { return p2p ? p2p->p2ps_adv_list : NULL; } void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr) { if (p2p && intended_addr) os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN); } u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; if (!addr || !p2p) return 0; dev = p2p_get_device(p2p, addr); if (dev) return dev->wps_prov_info; else return 0; } void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; if (!addr || !p2p) return; dev = p2p_get_device(p2p, addr); if (dev) dev->wps_prov_info = 0; } void p2p_set_state(struct p2p_data *p2p, int new_state) { p2p_dbg(p2p, "State %s -> %s", p2p_state_txt(p2p->state), p2p_state_txt(new_state)); p2p->state = new_state; if (new_state == P2P_IDLE && p2p->pending_channel) { p2p_dbg(p2p, "Apply change in listen channel"); p2p->cfg->reg_class = p2p->pending_reg_class; p2p->cfg->channel = p2p->pending_channel; p2p->pending_reg_class = 0; p2p->pending_channel = 0; } } void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) { p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec", p2p_state_txt(p2p->state), sec, usec); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); } void p2p_clear_timeout(struct p2p_data *p2p) { p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state)); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); } void p2p_go_neg_failed(struct p2p_data *p2p, int status) { struct p2p_go_neg_results res; struct p2p_device *peer = p2p->go_neg_peer; if (!peer) return; eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); if (p2p->state != P2P_SEARCH) { /* * Clear timeouts related to GO Negotiation if no new p2p_find * has been started. */ p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); } peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; wpabuf_free(peer->go_neg_conf); peer->go_neg_conf = NULL; p2p->go_neg_peer = NULL; os_memset(&res, 0, sizeof(res)); res.status = status; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) { unsigned int r, tu; int freq; struct wpabuf *ies; p2p_dbg(p2p, "Starting short listen state (state=%s)", p2p_state_txt(p2p->state)); if (p2p->pending_listen_freq) { /* We have a pending p2p_listen request */ p2p_dbg(p2p, "p2p_listen command pending already"); return; } freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); return; } if (os_get_random((u8 *) &r, sizeof(r)) < 0) r = 0; tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + p2p->min_disc_int) * 100; if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) tu = p2p->max_disc_tu; if (!dev_disc && tu < 100) tu = 100; /* Need to wait in non-device discovery use cases */ if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen) tu = p2p->cfg->max_listen * 1000 / 1024; if (tu == 0) { p2p_dbg(p2p, "Skip listen state since duration was 0 TU"); p2p_set_timeout(p2p, 0, 0); return; } ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; p2p->pending_listen_freq = freq; p2p->pending_listen_sec = 0; p2p->pending_listen_usec = 1024 * tu; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, ies) < 0) { p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; } wpabuf_free(ies); } int p2p_listen(struct p2p_data *p2p, unsigned int timeout) { int freq; struct wpabuf *ies; p2p_dbg(p2p, "Going to listen(only) state"); if (p2p->pending_listen_freq) { /* We have a pending p2p_listen request */ p2p_dbg(p2p, "p2p_listen command pending already"); return -1; } freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); return -1; } p2p->pending_listen_sec = timeout / 1000; p2p->pending_listen_usec = (timeout % 1000) * 1000; if (p2p->p2p_scan_running) { if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen"); return 0; } p2p_dbg(p2p, "p2p_scan running - delay start of listen state"); p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; return 0; } ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return -1; p2p->pending_listen_freq = freq; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; wpabuf_free(ies); return -1; } wpabuf_free(ies); p2p_set_state(p2p, P2P_LISTEN_ONLY); return 0; } static void p2p_device_clear_reported(struct p2p_data *p2p) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { dev->flags &= ~P2P_DEV_REPORTED; dev->sd_reqs = 0; } } /** * p2p_get_device - Fetch a peer entry * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer * Returns: Pointer to the device entry or %NULL if not found */ struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0) return dev; } return NULL; } /** * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address * @p2p: P2P module context from p2p_init() * @addr: P2P Interface Address of the peer * Returns: Pointer to the device entry or %NULL if not found */ struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0) return dev; } return NULL; } /** * p2p_create_device - Create a peer entry * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer * Returns: Pointer to the device entry or %NULL on failure * * If there is already an entry for the peer, it will be returned instead of * creating a new one. */ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev, *oldest = NULL; size_t count = 0; dev = p2p_get_device(p2p, addr); if (dev) return dev; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { count++; if (oldest == NULL || os_reltime_before(&dev->last_seen, &oldest->last_seen)) oldest = dev; } if (count + 1 > p2p->cfg->max_peers && oldest) { p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer " MACSTR, MAC2STR(oldest->info.p2p_device_addr)); dl_list_del(&oldest->list); p2p_device_free(p2p, oldest); } dev = os_zalloc(sizeof(*dev)); if (dev == NULL) return NULL; dl_list_add(&p2p->devices, &dev->list); os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN); return dev; } static void p2p_copy_client_info(struct p2p_device *dev, struct p2p_client_info *cli) { p2p_copy_filter_devname(dev->info.device_name, sizeof(dev->info.device_name), cli->dev_name, cli->dev_name_len); dev->info.dev_capab = cli->dev_capab; dev->info.config_methods = cli->config_methods; os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types; if (dev->info.wps_sec_dev_type_list_len > WPS_SEC_DEV_TYPE_MAX_LEN) dev->info.wps_sec_dev_type_list_len = WPS_SEC_DEV_TYPE_MAX_LEN; os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types, dev->info.wps_sec_dev_type_list_len); } static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, const u8 *go_interface_addr, int freq, const u8 *gi, size_t gi_len, struct os_reltime *rx_time) { struct p2p_group_info info; size_t c; struct p2p_device *dev; if (gi == NULL) return 0; if (p2p_group_info_parse(gi, gi_len, &info) < 0) return -1; /* * Clear old data for this group; if the devices are still in the * group, the information will be restored in the loop following this. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(dev->member_in_go_iface, go_interface_addr, ETH_ALEN) == 0) { os_memset(dev->member_in_go_iface, 0, ETH_ALEN); os_memset(dev->member_in_go_dev, 0, ETH_ALEN); } } for (c = 0; c < info.num_clients; c++) { struct p2p_client_info *cli = &info.client[c]; if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr, ETH_ALEN) == 0) continue; /* ignore our own entry */ dev = p2p_get_device(p2p, cli->p2p_device_addr); if (dev) { if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY | P2P_DEV_PROBE_REQ_ONLY)) { /* * Update information since we have not * received this directly from the client. */ p2p_copy_client_info(dev, cli); } else { /* * Need to update P2P Client Discoverability * flag since it is valid only in P2P Group * Info attribute. */ dev->info.dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.dev_capab |= cli->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; } } else { dev = p2p_create_device(p2p, cli->p2p_device_addr); if (dev == NULL) continue; dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; p2p_copy_client_info(dev, cli); dev->oper_freq = freq; p2p->cfg->dev_found(p2p->cfg->cb_ctx, dev->info.p2p_device_addr, &dev->info, 1); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; } os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT; } return 0; } static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev, int probe_req, const struct p2p_message *msg) { os_memcpy(dev->info.device_name, msg->device_name, sizeof(dev->info.device_name)); if (msg->manufacturer && msg->manufacturer_len < sizeof(dev->info.manufacturer)) { os_memset(dev->info.manufacturer, 0, sizeof(dev->info.manufacturer)); os_memcpy(dev->info.manufacturer, msg->manufacturer, msg->manufacturer_len); } if (msg->model_name && msg->model_name_len < sizeof(dev->info.model_name)) { os_memset(dev->info.model_name, 0, sizeof(dev->info.model_name)); os_memcpy(dev->info.model_name, msg->model_name, msg->model_name_len); } if (msg->model_number && msg->model_number_len < sizeof(dev->info.model_number)) { os_memset(dev->info.model_number, 0, sizeof(dev->info.model_number)); os_memcpy(dev->info.model_number, msg->model_number, msg->model_number_len); } if (msg->serial_number && msg->serial_number_len < sizeof(dev->info.serial_number)) { os_memset(dev->info.serial_number, 0, sizeof(dev->info.serial_number)); os_memcpy(dev->info.serial_number, msg->serial_number, msg->serial_number_len); } if (msg->pri_dev_type) os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type, sizeof(dev->info.pri_dev_type)); else if (msg->wps_pri_dev_type) os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type, sizeof(dev->info.pri_dev_type)); if (msg->wps_sec_dev_type_list) { os_memcpy(dev->info.wps_sec_dev_type_list, msg->wps_sec_dev_type_list, msg->wps_sec_dev_type_list_len); dev->info.wps_sec_dev_type_list_len = msg->wps_sec_dev_type_list_len; } if (msg->capability) { /* * P2P Client Discoverability bit is reserved in all frames * that use this function, so do not change its value here. */ dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.dev_capab |= msg->capability[0] & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.group_capab = msg->capability[1]; } if (msg->ext_listen_timing) { dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing); dev->ext_listen_interval = WPA_GET_LE16(msg->ext_listen_timing + 2); } if (!probe_req) { u16 new_config_methods; new_config_methods = msg->config_methods ? msg->config_methods : msg->wps_config_methods; if (new_config_methods && dev->info.config_methods != new_config_methods) { p2p_dbg(p2p, "Update peer " MACSTR " config_methods 0x%x -> 0x%x", MAC2STR(dev->info.p2p_device_addr), dev->info.config_methods, new_config_methods); dev->info.config_methods = new_config_methods; } } } static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, size_t ies_len) { const u8 *pos, *end; u8 id, len; wpabuf_free(dev->info.vendor_elems); dev->info.vendor_elems = NULL; end = ies + ies_len; for (pos = ies; end - pos > 1; pos += len) { id = *pos++; len = *pos++; if (len > end - pos) break; if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3) continue; if (len >= 4) { u32 type = WPA_GET_BE32(pos); if (type == WPA_IE_VENDOR_TYPE || type == WMM_IE_VENDOR_TYPE || type == WPS_IE_VENDOR_TYPE || type == P2P_IE_VENDOR_TYPE || type == WFD_IE_VENDOR_TYPE) continue; } /* Unknown vendor element - make raw IE data available */ if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0) break; wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len); if (wpabuf_size(dev->info.vendor_elems) > 2000) break; } } static int p2p_compare_wfd_info(struct p2p_device *dev, const struct p2p_message *msg) { if (dev->info.wfd_subelems && msg->wfd_subelems) { if (dev->info.wfd_subelems->used != msg->wfd_subelems->used) return 1; return os_memcmp(dev->info.wfd_subelems->buf, msg->wfd_subelems->buf, dev->info.wfd_subelems->used); } if (dev->info.wfd_subelems || msg->wfd_subelems) return 1; return 0; } /** * p2p_add_device - Add peer entries based on scan results or P2P frames * @p2p: P2P module context from p2p_init() * @addr: Source address of Beacon or Probe Response frame (may be either * P2P Device Address or P2P Interface Address) * @level: Signal level (signal strength of the received frame from the peer) * @freq: Frequency on which the Beacon or Probe Response frame was received * @rx_time: Time when the result was received * @ies: IEs from the Beacon or Probe Response frame * @ies_len: Length of ies buffer in octets * @scan_res: Whether this was based on scan results * Returns: 0 on success, -1 on failure * * If the scan result is for a GO, the clients in the group will also be added * to the peer table. This function can also be used with some other frames * like Provision Discovery Request that contains P2P Capability and P2P Device * Info attributes. */ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res) { struct p2p_device *dev; struct p2p_message msg; const u8 *p2p_dev_addr; int wfd_changed; int dev_name_changed; int i; struct os_reltime time_now; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ies, ies_len, &msg)) { p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); p2p_parse_free(&msg); return -1; } if (msg.p2p_device_addr) p2p_dev_addr = msg.p2p_device_addr; else if (msg.device_id) p2p_dev_addr = msg.device_id; else { p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (!is_zero_ether_addr(p2p->peer_filter) && os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { p2p_dbg(p2p, "Do not add peer filter for " MACSTR " due to peer filter", MAC2STR(p2p_dev_addr)); p2p_parse_free(&msg); return 0; } dev = p2p_create_device(p2p, p2p_dev_addr); if (dev == NULL) { p2p_parse_free(&msg); return -1; } if (rx_time == NULL) { os_get_reltime(&time_now); rx_time = &time_now; } /* * Update the device entry only if the new peer * entry is newer than the one previously stored, or if * the device was previously seen as a P2P Client in a group * and the new entry isn't older than a threshold. */ if (dev->last_seen.sec > 0 && os_reltime_before(rx_time, &dev->last_seen) && (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) || os_reltime_expired(&dev->last_seen, rx_time, P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) { p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)", (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) dev->last_seen.sec, (unsigned int) dev->last_seen.usec, dev->flags); p2p_parse_free(&msg); return -1; } os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY | P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) os_memcpy(dev->interface_addr, addr, ETH_ALEN); if (msg.ssid && msg.ssid[1] <= sizeof(dev->oper_ssid) && (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0)) { os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]); dev->oper_ssid_len = msg.ssid[1]; } wpabuf_free(dev->info.p2ps_instance); dev->info.p2ps_instance = NULL; if (msg.adv_service_instance && msg.adv_service_instance_len) dev->info.p2ps_instance = wpabuf_alloc_copy( msg.adv_service_instance, msg.adv_service_instance_len); if (freq >= 2412 && freq <= 2484 && msg.ds_params && *msg.ds_params >= 1 && *msg.ds_params <= 14) { int ds_freq; if (*msg.ds_params == 14) ds_freq = 2484; else ds_freq = 2407 + *msg.ds_params * 5; if (freq != ds_freq) { p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz", freq, ds_freq); freq = ds_freq; } } if (dev->listen_freq && dev->listen_freq != freq && scan_res) { p2p_dbg(p2p, "Update Listen frequency based on scan results (" MACSTR " %d -> %d MHz (DS param %d)", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq, msg.ds_params ? *msg.ds_params : -1); } if (scan_res) { dev->listen_freq = freq; if (msg.group_info) dev->oper_freq = freq; } dev->info.level = level; dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name, WPS_DEV_NAME_MAX_LEN) != 0; p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); dev->info.wps_vendor_ext[i] = NULL; } for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (msg.wps_vendor_ext[i] == NULL) break; dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy( msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]); if (dev->info.wps_vendor_ext[i] == NULL) break; } wfd_changed = p2p_compare_wfd_info(dev, &msg); if (wfd_changed) { wpabuf_free(dev->info.wfd_subelems); if (msg.wfd_subelems) dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); else dev->info.wfd_subelems = NULL; } if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info, msg.group_info_len, rx_time); } p2p_parse_free(&msg); p2p_update_peer_vendor_elems(dev, ies, ies_len); if (dev->flags & P2P_DEV_REPORTED && !wfd_changed && !dev_name_changed && (!msg.adv_service_instance || (dev->flags & P2P_DEV_P2PS_REPORTED))) return 0; p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", freq, (unsigned int) rx_time->sec, (unsigned int) rx_time->usec); if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "Do not report rejected device"); return 0; } if (dev->info.config_methods == 0 && (freq == 2412 || freq == 2437 || freq == 2462)) { /* * If we have only seen a Beacon frame from a GO, we do not yet * know what WPS config methods it supports. Since some * applications use config_methods value from P2P-DEVICE-FOUND * events, postpone reporting this peer until we've fully * discovered its capabilities. * * At least for now, do this only if the peer was detected on * one of the social channels since that peer can be easily be * found again and there are no limitations of having to use * passive scan on this channels, so this can be done through * Probe Response frame that includes the config_methods * information. */ p2p_dbg(p2p, "Do not report peer " MACSTR " with unknown config methods", MAC2STR(addr)); return 0; } p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; if (msg.adv_service_instance) dev->flags |= P2P_DEV_P2PS_REPORTED; return 0; } static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) { int i; if (p2p->go_neg_peer == dev) { /* * If GO Negotiation is in progress, report that it has failed. */ p2p_go_neg_failed(p2p, -1); } if (p2p->invite_peer == dev) p2p->invite_peer = NULL; if (p2p->sd_peer == dev) p2p->sd_peer = NULL; if (p2p->pending_client_disc_go == dev) p2p->pending_client_disc_go = NULL; /* dev_lost() device, but only if it was previously dev_found() */ if (dev->flags & P2P_DEV_REPORTED_ONCE) p2p->cfg->dev_lost(p2p->cfg->cb_ctx, dev->info.p2p_device_addr); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); dev->info.wps_vendor_ext[i] = NULL; } wpabuf_free(dev->info.wfd_subelems); wpabuf_free(dev->info.vendor_elems); wpabuf_free(dev->go_neg_conf); wpabuf_free(dev->info.p2ps_instance); os_free(dev); } static int p2p_get_next_prog_freq(struct p2p_data *p2p) { struct p2p_channels *c; struct p2p_reg_class *cla; size_t cl, ch; int found = 0; u8 reg_class; u8 channel; int freq; c = &p2p->cfg->channels; for (cl = 0; cl < c->reg_classes; cl++) { cla = &c->reg_class[cl]; if (cla->reg_class != p2p->last_prog_scan_class) continue; for (ch = 0; ch < cla->channels; ch++) { if (cla->channel[ch] == p2p->last_prog_scan_chan) { found = 1; break; } } if (found) break; } if (!found) { /* Start from beginning */ reg_class = c->reg_class[0].reg_class; channel = c->reg_class[0].channel[0]; } else { /* Pick the next channel */ ch++; if (ch == cla->channels) { cl++; if (cl == c->reg_classes) cl = 0; ch = 0; } reg_class = c->reg_class[cl].reg_class; channel = c->reg_class[cl].channel[ch]; } freq = p2p_channel_to_freq(reg_class, channel); p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz", reg_class, channel, freq); p2p->last_prog_scan_class = reg_class; p2p->last_prog_scan_chan = channel; if (freq == 2412 || freq == 2437 || freq == 2462) return 0; /* No need to add social channels */ return freq; } static void p2p_search(struct p2p_data *p2p) { int freq = 0; enum p2p_scan_type type; u16 pw_id = DEV_PW_DEFAULT; int res; if (p2p->drv_in_listen) { p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing"); return; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); if (p2p->find_pending_full && (p2p->find_type == P2P_FIND_PROGRESSIVE || p2p->find_type == P2P_FIND_START_WITH_FULL)) { type = P2P_SCAN_FULL; p2p_dbg(p2p, "Starting search (pending full scan)"); p2p->find_pending_full = 0; } else if ((p2p->find_type == P2P_FIND_PROGRESSIVE && (freq = p2p_get_next_prog_freq(p2p)) > 0) || (p2p->find_type == P2P_FIND_START_WITH_FULL && (freq = p2p->find_specified_freq) > 0)) { type = P2P_SCAN_SOCIAL_PLUS_ONE; p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { type = P2P_SCAN_SOCIAL; p2p_dbg(p2p, "Starting search"); } res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id, pw_id); + p2p->find_dev_id, pw_id, p2p->include_6ghz); if (res < 0) { p2p_dbg(p2p, "Scan request schedule failed"); p2p_continue_find(p2p); } } static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; p2p_dbg(p2p, "Find timeout -> stop"); p2p_stop_find(p2p); } void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status) { if (status != 0) { p2p_dbg(p2p, "Scan request failed"); /* Do continue find even for the first p2p_find_scan */ p2p_continue_find(p2p); } else { p2p_dbg(p2p, "Running p2p_scan"); p2p->p2p_scan_running = 1; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, p2p, NULL); } } static int p2p_run_after_scan(struct p2p_data *p2p) { struct p2p_device *dev; enum p2p_after_scan op; op = p2p->start_after_scan; p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; switch (op) { case P2P_AFTER_SCAN_NOTHING: break; case P2P_AFTER_SCAN_LISTEN: p2p_dbg(p2p, "Start previously requested Listen state"); p2p_listen(p2p, p2p->pending_listen_sec * 1000 + p2p->pending_listen_usec / 1000); return 1; case P2P_AFTER_SCAN_CONNECT: p2p_dbg(p2p, "Start previously requested connect with " MACSTR, MAC2STR(p2p->after_scan_peer)); dev = p2p_get_device(p2p, p2p->after_scan_peer); if (dev == NULL) { p2p_dbg(p2p, "Peer not known anymore"); break; } p2p_connect_send(p2p, dev); return 1; } return 0; } static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; int running; p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running); running = p2p->p2p_scan_running; /* Make sure we recover from missed scan results callback */ p2p->p2p_scan_running = 0; if (running) p2p_run_after_scan(p2p); } static void p2p_free_req_dev_types(struct p2p_data *p2p) { p2p->num_req_dev_types = 0; os_free(p2p->req_dev_types); p2p->req_dev_types = NULL; } static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) { u8 buf[SHA256_MAC_LEN]; char str_buf[256]; const u8 *adv_array; size_t i, adv_len; if (!str || !hash) return 0; if (!str[0]) { os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN); return 1; } adv_array = (u8 *) str_buf; adv_len = os_strlen(str); if (adv_len >= sizeof(str_buf)) return 0; for (i = 0; i < adv_len; i++) { if (str[i] >= 'A' && str[i] <= 'Z') str_buf[i] = str[i] - 'A' + 'a'; else str_buf[i] = str[i]; } if (sha256_vector(1, &adv_array, &adv_len, buf)) return 0; os_memcpy(hash, buf, P2PS_HASH_LEN); return 1; } int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, unsigned int search_delay, - u8 seek_count, const char **seek, int freq) + u8 seek_count, const char **seek, int freq, bool include_6ghz) { int res; struct os_reltime start; p2p_dbg(p2p, "Starting find (type=%d)", type); if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan is already running"); } p2p_free_req_dev_types(p2p); if (req_dev_types && num_req_dev_types) { p2p->req_dev_types = os_memdup(req_dev_types, num_req_dev_types * WPS_DEV_TYPE_LEN); if (p2p->req_dev_types == NULL) return -1; p2p->num_req_dev_types = num_req_dev_types; } if (dev_id) { os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN); p2p->find_dev_id = p2p->find_dev_id_buf; } else p2p->find_dev_id = NULL; - + p2p->include_6ghz = p2p_wfd_enabled(p2p) && include_6ghz; if (seek_count == 0 || !seek) { /* Not an ASP search */ p2p->p2ps_seek = 0; } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) { /* * An empty seek string means no hash values, but still an ASP * search. */ p2p_dbg(p2p, "ASP search"); p2p->p2ps_seek_count = 0; p2p->p2ps_seek = 1; } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { u8 buf[P2PS_HASH_LEN]; int i, count = 0; for (i = 0; i < seek_count; i++) { if (!p2ps_gen_hash(p2p, seek[i], buf)) continue; p2p_dbg(p2p, "Seek service %s hash " MACSTR, seek[i], MAC2STR(buf)); os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN], buf, P2PS_HASH_LEN); count++; } p2p->p2ps_seek_count = count; p2p->p2ps_seek = 1; } else { p2p->p2ps_seek_count = 0; p2p->p2ps_seek = 1; } /* Special case to perform wildcard search */ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { p2p->p2ps_seek_count = 1; os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash, P2PS_HASH_LEN); } p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); if (p2p->pending_listen_freq) { p2p_dbg(p2p, "Clear pending_listen_freq for p2p_find"); p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->find_pending_full = 0; p2p->find_type = type; if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480) p2p->find_specified_freq = freq; else p2p->find_specified_freq = 0; p2p_device_clear_reported(p2p); os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_set_state(p2p, P2P_SEARCH); p2p->search_delay = search_delay; p2p->in_search_delay = 0; eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p->last_p2p_find_timeout = timeout; if (timeout) eloop_register_timeout(timeout, 0, p2p_find_timeout, p2p, NULL); os_get_reltime(&start); switch (type) { case P2P_FIND_START_WITH_FULL: if (freq > 0) { /* * Start with the specified channel and then move to * scans for social channels and this specific channel. */ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SPECIFIC, freq, p2p->num_req_dev_types, p2p->req_dev_types, dev_id, - DEV_PW_DEFAULT); + DEV_PW_DEFAULT, + p2p->include_6ghz); break; } /* fall through */ case P2P_FIND_PROGRESSIVE: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, p2p->num_req_dev_types, p2p->req_dev_types, dev_id, - DEV_PW_DEFAULT); + DEV_PW_DEFAULT, p2p->include_6ghz); break; case P2P_FIND_ONLY_SOCIAL: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0, p2p->num_req_dev_types, p2p->req_dev_types, dev_id, - DEV_PW_DEFAULT); + DEV_PW_DEFAULT, p2p->include_6ghz); break; default: return -1; } if (!res) p2p->find_start = start; if (res != 0 && p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ if (type == P2P_FIND_PROGRESSIVE || (type == P2P_FIND_START_WITH_FULL && freq == 0)) p2p->find_pending_full = 1; res = 0; /* do not report failure */ } else if (res != 0) { p2p_dbg(p2p, "Failed to start p2p_scan"); p2p_set_state(p2p, P2P_IDLE); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); } return res; } void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) { p2p_dbg(p2p, "Stopping find"); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p_clear_timeout(p2p); if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND) p2p->cfg->find_stopped(p2p->cfg->cb_ctx); p2p->p2ps_seek_count = 0; p2p_set_state(p2p, P2P_IDLE); p2p_free_req_dev_types(p2p); p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; if (p2p->go_neg_peer) p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer = NULL; p2p->sd_peer = NULL; p2p->invite_peer = NULL; p2p_stop_listen_for_freq(p2p, freq); p2p->send_action_in_progress = 0; } void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) { if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response"); return; } if (p2p->in_listen) { p2p->in_listen = 0; p2p_clear_timeout(p2p); } if (p2p->drv_in_listen) { /* * The driver may not deliver callback to p2p_listen_end() * when the operation gets canceled, so clear the internal * variable that is tracking driver state. */ p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); p2p->drv_in_listen = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); } void p2p_stop_listen(struct p2p_data *p2p) { if (p2p->state != P2P_LISTEN_ONLY) { p2p_dbg(p2p, "Skip stop_listen since not in listen_only state."); return; } p2p_stop_listen_for_freq(p2p, 0); p2p_set_state(p2p, P2P_IDLE); } void p2p_stop_find(struct p2p_data *p2p) { p2p->pending_listen_freq = 0; p2p_stop_find_for_freq(p2p, 0); } static int p2p_prepare_channel_pref(struct p2p_data *p2p, unsigned int force_freq, unsigned int pref_freq, int go) { u8 op_class, op_channel; unsigned int freq = force_freq ? force_freq : pref_freq; p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", force_freq, pref_freq, go); if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); return -1; } if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) && (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, op_channel))) { p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", freq, op_class, op_channel); return -1; } p2p->op_reg_class = op_class; p2p->op_channel = op_channel; if (force_freq) { p2p->channels.reg_classes = 1; p2p->channels.reg_class[0].channels = 1; p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; p2p->channels.reg_class[0].channel[0] = p2p->op_channel; } else { - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); + p2p_copy_channels(&p2p->channels, &p2p->cfg->channels, + p2p->allow_6ghz); } return 0; } static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; const int op_classes_edmg[] = { 181, 182, 183, 0 }; + const int op_classes_6ghz[] = { 131, 0 }; p2p_dbg(p2p, "Prepare channel best"); if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && p2p_supported_freq(p2p, p2p->best_freq_overall) && p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel) == 0) { p2p_dbg(p2p, "Select best overall channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && p2p_supported_freq(p2p, p2p->best_freq_5) && p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel) == 0) { p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && p2p_supported_freq(p2p, p2p->best_freq_24) && p2p_freq_to_channel(p2p->best_freq_24, &op_class, &op_channel) == 0) { p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (p2p->cfg->num_pref_chan > 0 && p2p_channels_includes(&p2p->cfg->channels, p2p->cfg->pref_chan[0].op_class, p2p->cfg->pref_chan[0].chan)) { p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference"); p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class; p2p->op_channel = p2p->cfg->pref_chan[0].chan; } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_edmg, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible EDMG channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); + } else if (p2p->allow_6ghz && + (p2p_channel_select(&p2p->cfg->channels, op_classes_6ghz, + &p2p->op_reg_class, &p2p->op_channel) == + 0)) { + p2p_dbg(p2p, "Select possible 6 GHz channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else if (p2p_channels_includes(&p2p->cfg->channels, p2p->cfg->op_reg_class, p2p->cfg->op_channel)) { p2p_dbg(p2p, "Select pre-configured channel as operating channel preference"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; } else if (p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, &p2p->op_channel, NULL, NULL) == 0) { p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference", p2p->op_reg_class, p2p->op_channel); } else { /* Select any random available channel from the first available * operating class */ p2p_channel_select(&p2p->cfg->channels, NULL, &p2p->op_reg_class, &p2p->op_channel); p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference", p2p->op_channel, p2p->op_reg_class); } - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); + p2p_copy_channels(&p2p->channels, &p2p->cfg->channels, p2p->allow_6ghz); } /** * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD * @p2p: P2P module context from p2p_init() * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced * @pref_freq: Preferred frequency in MHz or 0 if no preference * @go: Whether the local end will be forced to be GO * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO * Negotiation prior to having received peer information or for P2PS PD * signalling. The selected channel may be further optimized in * p2p_reselect_channel() once the peer information is available. */ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go) { p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d", force_freq, pref_freq, go); if (force_freq || pref_freq) { if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) < 0) return -1; } else { p2p_prepare_channel_best(p2p); } p2p_channels_dump(p2p, "prepared channels", &p2p->channels); if (go) p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); else if (!force_freq) p2p_channels_union_inplace(&p2p->channels, &p2p->cfg->cli_channels); p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels); p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", p2p->op_reg_class, p2p->op_channel, force_freq ? " (forced)" : ""); if (force_freq) dev->flags |= P2P_DEV_FORCE_FREQ; else dev->flags &= ~P2P_DEV_FORCE_FREQ; return 0; } static void p2p_set_dev_persistent(struct p2p_device *dev, int persistent_group) { switch (persistent_group) { case 0: dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP | P2P_DEV_PREFER_PERSISTENT_RECONN); break; case 1: dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP; dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN; break; case 2: dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP | P2P_DEV_PREFER_PERSISTENT_RECONN; break; } } int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR " wps_method=%d persistent_group=%d pd_before_go_neg=%d " - "oob_pw_id=%u", + "oob_pw_id=%u allow_6ghz=%d", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group, pd_before_go_neg, oob_pw_id); + wps_method, persistent_group, pd_before_go_neg, oob_pw_id, + p2p->allow_6ghz); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == 15) < 0) return -1; if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer_addr)); return -1; } if (dev->oper_freq <= 0) { p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " with incomplete information", MAC2STR(peer_addr)); return -1; } /* * First, try to connect directly. If the peer does not * acknowledge frames, assume it is sleeping and use device * discoverability via the GO at that point. */ } p2p->ssid_set = 0; if (force_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", force_ssid, force_ssid_len); os_memcpy(p2p->ssid, force_ssid, force_ssid_len); p2p->ssid_len = force_ssid_len; p2p->ssid_set = 1; } dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; if (pd_before_go_neg) dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; else { dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; /* * Assign dialog token and tie breaker here to use the same * values in each retry within the same GO Negotiation exchange. */ dev->dialog_token++; if (dev->dialog_token == 0) dev->dialog_token = 1; dev->tie_breaker = p2p->next_tie_breaker; p2p->next_tie_breaker = !p2p->next_tie_breaker; } dev->connect_reqs = 0; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; p2p_set_dev_persistent(dev, persistent_group); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); dev->wps_method = wps_method; dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan running - delay connect send"); p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); return 0; } return p2p_connect_send(p2p, dev); } int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d oob_pw_id=%u", + " wps_method=%d persistent_group=%d oob_pw_id=%u allow_6ghz=%d", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group, oob_pw_id); + wps_method, persistent_group, oob_pw_id, p2p->allow_6ghz); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL) { p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == 15) < 0) return -1; p2p->ssid_set = 0; if (force_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", force_ssid, force_ssid_len); os_memcpy(p2p->ssid, force_ssid, force_ssid_len); p2p->ssid_len = force_ssid_len; p2p->ssid_set = 1; } dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; p2p_set_dev_persistent(dev, persistent_group); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); dev->wps_method = wps_method; dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; return 0; } void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg) { os_get_reltime(&dev->last_seen); p2p_copy_wps_info(p2p, dev, 0, msg); if (msg->listen_channel) { int freq; freq = p2p_channel_to_freq(msg->listen_channel[3], msg->listen_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer Listen channel: " "country=%c%c(0x%02x) reg_class=%u channel=%u", msg->listen_channel[0], msg->listen_channel[1], msg->listen_channel[2], msg->listen_channel[3], msg->listen_channel[4]); } else { p2p_dbg(p2p, "Update peer " MACSTR " Listen channel: %u -> %u MHz", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq); dev->listen_freq = freq; } } if (msg->wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); } if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request"); } else { p2p_dbg(p2p, "Created device entry based on GO Neg Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " "listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, dev->listen_freq); } dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "Do not report rejected device"); return; } p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; } void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) { os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2); os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2], p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len); *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len; } int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) { if (p2p->ssid_set) { os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len); params->ssid_len = p2p->ssid_len; } else { p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); } p2p->ssid_set = 0; p2p_random(params->passphrase, p2p->cfg->passphrase_len); return 0; } void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) { struct p2p_go_neg_results res; int go = peer->go_state == LOCAL_GO; struct p2p_channels intersection; p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); os_memset(&res, 0, sizeof(res)); res.role_go = go; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); res.wps_method = peer->wps_method; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) res.persistent_group = 2; else res.persistent_group = 1; } if (go) { /* Setup AP mode for WPS provisioning */ res.freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; p2p_random(res.passphrase, p2p->cfg->passphrase_len); } else { res.freq = peer->oper_freq; if (p2p->ssid_len) { os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; } } p2p_channels_dump(p2p, "own channels", &p2p->channels); p2p_channels_dump(p2p, "peer channels", &peer->channels); p2p_channels_intersect(&p2p->channels, &peer->channels, &intersection); if (go) { p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); p2p_channels_dump(p2p, "intersection after no-GO removal", &intersection); } p2p_channels_to_freqs(&intersection, res.freq_list, P2P_MAX_CHANNELS); res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; wpabuf_free(peer->go_neg_conf); peer->go_neg_conf = NULL; p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_GO_NEG_REQ: p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_RESP: p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_CONF: p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); break; case P2P_INVITATION_REQ: p2p_process_invitation_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_INVITATION_RESP: p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_PROV_DISC_RESP: p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1); break; case P2P_DEV_DISC_REQ: p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_DEV_DISC_RESP: p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); break; default: p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d", data[0]); break; } } static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *bssid, const u8 *data, size_t len, int freq) { if (len < 1) return; switch (data[0]) { case WLAN_PA_VENDOR_SPECIFIC: data++; len--; if (len < 4) return; if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) return; data += 4; len -= 4; p2p_rx_p2p_action(p2p, sa, data, len, freq); break; case WLAN_PA_GAS_INITIAL_REQ: p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_INITIAL_RESP: p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_COMEBACK_REQ: p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq); break; case WLAN_PA_GAS_COMEBACK_RESP: p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq); break; } } void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq) { if (category == WLAN_ACTION_PUBLIC) { p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq); return; } if (category != WLAN_ACTION_VENDOR_SPECIFIC) return; if (len < 4) return; if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) return; data += 4; len -= 4; /* P2P action frame */ p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_NOA: p2p_dbg(p2p, "Received P2P Action - Notice of Absence"); /* TODO */ break; case P2P_PRESENCE_REQ: p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq); break; case P2P_PRESENCE_RESP: p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1); break; case P2P_GO_DISC_REQ: p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); break; default: p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]); break; } } static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; if (p2p->go_neg_peer == NULL) return; if (p2p->pending_listen_freq) { p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start"); p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->go_neg_peer->status = P2P_SC_SUCCESS; /* * Set new timeout to make sure a previously set one does not expire * too quickly while waiting for the GO Negotiation to complete. */ p2p_set_timeout(p2p, 0, 500000); p2p_connect_send(p2p, p2p->go_neg_peer); } static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; if (p2p->invite_peer == NULL) return; if (p2p->pending_listen_freq) { p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start"); p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, const u8 *ie, size_t ie_len) { struct p2p_message msg; struct p2p_device *dev; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL) { p2p_parse_free(&msg); return; /* not a P2P probe */ } if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* The Probe Request is not part of P2P Device Discovery. It is * not known whether the source address of the frame is the P2P * Device Address or P2P Interface Address. Do not add a new * peer entry based on this frames. */ p2p_parse_free(&msg); return; } dev = p2p_get_device(p2p, addr); if (dev) { if (msg.listen_channel) { int freq; if (dev->country[0] == 0) os_memcpy(dev->country, msg.listen_channel, 3); freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); if (freq > 0 && dev->listen_freq != freq) { p2p_dbg(p2p, "Updated peer " MACSTR " Listen channel (Probe Request): %d -> %d MHz", MAC2STR(addr), dev->listen_freq, freq); dev->listen_freq = freq; } } os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ } dev = p2p_create_device(p2p, addr); if (dev == NULL) { p2p_parse_free(&msg); return; } os_get_reltime(&dev->last_seen); dev->flags |= P2P_DEV_PROBE_REQ_ONLY; if (msg.listen_channel) { os_memcpy(dev->country, msg.listen_channel, 3); dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); } p2p_copy_wps_info(p2p, dev, 1, &msg); if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } p2p_parse_free(&msg); p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, dev->listen_freq); } struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, const u8 *addr, struct p2p_message *msg) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); if (dev) { os_get_reltime(&dev->last_seen); return dev; /* already known */ } dev = p2p_create_device(p2p, addr); if (dev == NULL) return NULL; p2p_add_dev_info(p2p, addr, dev, msg); return dev; } static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type) { if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0) return 1; if (os_memcmp(dev_type, req_dev_type, 2) == 0 && WPA_GET_BE32(&req_dev_type[2]) == 0 && WPA_GET_BE16(&req_dev_type[6]) == 0) return 1; /* Category match with wildcard OUI/sub-category */ return 0; } int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type) { size_t i; for (i = 0; i < num_req_dev_type; i++) { if (dev_type_match(dev_type, req_dev_type[i])) return 1; } return 0; } /** * p2p_match_dev_type - Match local device type with requested type * @p2p: P2P module context from p2p_init() * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) * Returns: 1 on match, 0 on mismatch * * This function can be used to match the Requested Device Type attribute in * WPS IE with the local device types for deciding whether to reply to a Probe * Request frame. */ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) { struct wps_parse_attr attr; size_t i; if (wps_parse_msg(wps, &attr)) return 1; /* assume no Requested Device Type attributes */ if (attr.num_req_dev_type == 0) return 1; /* no Requested Device Type attributes -> match */ if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type, attr.num_req_dev_type)) return 1; /* Own Primary Device Type matches */ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) { if (dev_type_list_match(p2p->cfg->sec_dev_type[i], attr.req_dev_type, attr.num_req_dev_type)) return 1; /* Own Secondary Device Type matches */ } /* No matching device type found */ return 0; } struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, const u8 *query_hash, u8 query_count) { struct wpabuf *buf; u8 *len; int pw_id = -1; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_resp) extra = wpabuf_len(p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); if (query_count) extra += MAX_SVC_ADV_IE_LEN; buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; if (p2p->go_neg_peer) { /* Advertise immediate availability of WPS credential */ pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); } if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for Probe Response"); wpabuf_free(buf); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_resp) wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, p2p->ext_listen_interval); p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); if (query_count) { p2p_buf_add_service_instance(buf, p2p, query_count, query_hash, p2p->p2ps_adv_list); } return buf; } static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf, struct wpabuf *ies, const u8 *addr, int rx_freq) { struct ieee80211_mgmt *resp; u8 channel, op_class; resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, u.probe_resp.variable)); resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_RESP << 4)); os_memcpy(resp->da, addr, ETH_ALEN); os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); resp->u.probe_resp.beacon_int = host_to_le16(100); /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | WLAN_CAPABILITY_PRIVACY | WLAN_CAPABILITY_SHORT_SLOT_TIME); wpabuf_put_u8(buf, WLAN_EID_SSID); wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); wpabuf_put_u8(buf, 8); wpabuf_put_u8(buf, (60 / 5) | 0x80); wpabuf_put_u8(buf, 90 / 5); wpabuf_put_u8(buf, (120 / 5) | 0x80); wpabuf_put_u8(buf, 180 / 5); wpabuf_put_u8(buf, (240 / 5) | 0x80); wpabuf_put_u8(buf, 360 / 5); wpabuf_put_u8(buf, 480 / 5); wpabuf_put_u8(buf, 540 / 5); if (!rx_freq) { channel = p2p->cfg->channel; } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { p2p_err(p2p, "Failed to convert freq to channel"); return -1; } wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); wpabuf_put_u8(buf, 1); wpabuf_put_u8(buf, channel); wpabuf_put_buf(buf, ies); return 0; } static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { struct p2ps_advertisement *adv_data; int any_wfa; p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); /* Wildcard org.wi-fi.wfds matches any WFA spec defined service */ any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0; adv_data = p2p->p2ps_adv_list; while (adv_data) { if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) return 1; /* exact hash match */ if (any_wfa && os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR, os_strlen(P2PS_WILD_HASH_STR)) == 0) return 1; /* WFA service match */ adv_data = adv_data->next; } return 0; } static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, unsigned int rx_freq) { struct ieee802_11_elems elems; struct wpabuf *buf; struct p2p_message msg; struct wpabuf *ies; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it"); return P2P_PREQ_MALFORMED; } if (elems.p2p == NULL) { /* not a P2P probe - ignore it */ p2p_dbg(p2p, "Not a P2P probe - ignore it"); return P2P_PREQ_NOT_P2P; } if (dst && !is_broadcast_ether_addr(dst) && os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Not sent to the broadcast address or our P2P Device Address */ p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it", MAC2STR(dst)); return P2P_PREQ_NOT_PROCESSED; } if (bssid && !is_broadcast_ether_addr(bssid)) { /* Not sent to the Wildcard BSSID */ p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it", MAC2STR(bssid)); return P2P_PREQ_NOT_PROCESSED; } if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN || os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* not using P2P Wildcard SSID - ignore */ p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it"); return P2P_PREQ_NOT_PROCESSED; } if (supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it"); return P2P_PREQ_NOT_P2P; } os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0) { /* Could not parse P2P attributes */ p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it"); return P2P_PREQ_NOT_P2P; } if (msg.service_hash && msg.service_hash_count) { const u8 *hash = msg.service_hash; u8 i; int p2ps_svc_found = 0; p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz", p2p->in_listen, p2p->drv_in_listen, rx_freq, p2p->cfg->channel, p2p->pending_listen_freq); if (!p2p->in_listen && !p2p->drv_in_listen && p2p->pending_listen_freq && rx_freq && rx_freq != p2p->pending_listen_freq) { p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz", rx_freq, p2p->pending_listen_freq); p2p_parse_free(&msg); return P2P_PREQ_NOT_LISTEN; } for (i = 0; i < msg.service_hash_count; i++) { if (p2p_service_find_asp(p2p, hash)) { p2p_dbg(p2p, "Service Hash match found: " MACSTR, MAC2STR(hash)); p2ps_svc_found = 1; break; } hash += P2PS_HASH_LEN; } /* Probed hash unknown */ if (!p2ps_svc_found) { p2p_dbg(p2p, "No Service Hash match found"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } } else { /* This is not a P2PS Probe Request */ p2p_dbg(p2p, "No P2PS Hash in Probe Request"); if (!p2p->in_listen || !p2p->drv_in_listen) { /* not in Listen state - ignore Probe Request */ p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request", p2p->in_listen, p2p->drv_in_listen); p2p_parse_free(&msg); return P2P_PREQ_NOT_LISTEN; } } if (msg.device_id && os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Device ID did not match */ p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it", MAC2STR(msg.device_id)); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } /* Check Requested Device Type match */ if (msg.wps_attributes && !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ p2p_dbg(p2p, "Probe Req requested Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state"); /* * We do not really have a specific BSS that this frame is advertising, * so build a frame that has some information in valid format. This is * really only used for discovery purposes, not to learn exact BSS * parameters. */ ies = p2p_build_probe_resp_ies(p2p, msg.service_hash, msg.service_hash_count); p2p_parse_free(&msg); if (ies == NULL) return P2P_PREQ_NOT_PROCESSED; buf = wpabuf_alloc(200 + wpabuf_len(ies)); if (buf == NULL) { wpabuf_free(ies); return P2P_PREQ_NOT_PROCESSED; } if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) { wpabuf_free(ies); wpabuf_free(buf); return P2P_PREQ_NOT_PROCESSED; } wpabuf_free(ies); p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); wpabuf_free(buf); return P2P_PREQ_PROCESSED; } enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, unsigned int rx_freq, int p2p_lo_started) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); if (p2p_lo_started) { p2p_dbg(p2p, "Probe Response is offloaded, do not reply Probe Request"); return P2P_PREQ_PROCESSED; } res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) return res; /* * Activate a pending GO Negotiation/Invite flow if a received Probe * Request frame is from an expected peer. Some devices may share the * same address for P2P and non-P2P STA running simultaneously. The * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe() * return values verified above ensure we are handling a Probe Request * frame from a P2P peer. */ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) == 0 && !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { /* Received a Probe Request from GO Negotiation peer */ p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); return res; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && p2p->invite_peer && (p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) && os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) == 0) { /* Received a Probe Request from Invite peer */ p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); return res; } return res; } static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, u8 *buf, size_t len, struct wpabuf *p2p_ie) { struct wpabuf *tmp; u8 *lpos; size_t tmplen; int res; u8 group_capab; struct p2p_message msg; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0) return 0; p2p_dbg(p2p, "BSS P2P manageability %s", msg.manageability ? "enabled" : "disabled"); if (!msg.manageability) return 0; /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) * P2P Interface attribute (present if concurrent device and * P2P Management is enabled) */ tmp = wpabuf_alloc(200); if (tmp == NULL) return -1; lpos = p2p_buf_add_ie_hdr(tmp); group_capab = 0; if (p2p->num_groups > 0) { group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; } p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab); if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED)) p2p_buf_add_p2p_interface(tmp, p2p); p2p_buf_update_ie_hdr(tmp, lpos); tmplen = wpabuf_len(tmp); if (tmplen > len) res = -1; else { os_memcpy(buf, wpabuf_head(tmp), tmplen); res = tmplen; } wpabuf_free(tmp); return res; } int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, size_t len, int p2p_group, struct wpabuf *p2p_ie) { struct wpabuf *tmp; u8 *lpos; struct p2p_device *peer; size_t tmplen; int res; size_t extra = 0; if (!p2p_group) return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_assoc_req) extra = wpabuf_len(p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]); /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) * Extended Listen Timing (may be present) * P2P Device Info attribute (shall be present) */ tmp = wpabuf_alloc(200 + extra); if (tmp == NULL) return -1; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_assoc_req) wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]) wpabuf_put_buf(tmp, p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]); peer = bssid ? p2p_get_device(p2p, bssid) : NULL; lpos = p2p_buf_add_ie_hdr(tmp); p2p_buf_add_capability(tmp, p2p->dev_capab, 0); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period, p2p->ext_listen_interval); p2p_buf_add_device_info(tmp, p2p, peer); p2p_buf_update_ie_hdr(tmp, lpos); tmplen = wpabuf_len(tmp); if (tmplen > len) res = -1; else { os_memcpy(buf, wpabuf_head(tmp), tmplen); res = tmplen; } wpabuf_free(tmp); return res; } int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) { struct wpabuf *p2p_ie; int ret; p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (p2p_ie == NULL) return 0; ret = p2p_attr_text(p2p_ie, buf, end); wpabuf_free(p2p_ie); return ret; } struct p2ps_advertisement * p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id) { struct p2ps_advertisement *adv_data; if (!p2p) return NULL; adv_data = p2p->p2ps_adv_list; while (adv_data) { if (adv_data->id == adv_id) return adv_data; adv_data = adv_data->next; } return NULL; } int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) { struct p2ps_advertisement *adv_data; struct p2ps_advertisement **prior; if (!p2p) return -1; adv_data = p2p->p2ps_adv_list; prior = &p2p->p2ps_adv_list; while (adv_data) { if (adv_data->id == adv_id) { p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id); *prior = adv_data->next; os_free(adv_data); return 0; } prior = &adv_data->next; adv_data = adv_data->next; } return -1; } int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, const char *svc_info, const u8 *cpt_priority) { struct p2ps_advertisement *adv_data, *tmp, **prev; u8 buf[P2PS_HASH_LEN]; size_t adv_data_len, adv_len, info_len = 0; int i; if (!p2p || !adv_str || !adv_str[0] || !cpt_priority) return -1; if (!(config_methods & p2p->cfg->config_methods)) { p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x", config_methods, p2p->cfg->config_methods); return -1; } if (!p2ps_gen_hash(p2p, adv_str, buf)) return -1; if (svc_info) info_len = os_strlen(svc_info); adv_len = os_strlen(adv_str); adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 + info_len + 1; adv_data = os_zalloc(adv_data_len); if (!adv_data) return -1; os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN); adv_data->id = adv_id; adv_data->state = svc_state; adv_data->config_methods = config_methods & p2p->cfg->config_methods; adv_data->auto_accept = (u8) auto_accept; os_memcpy(adv_data->svc_name, adv_str, adv_len); for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) { adv_data->cpt_priority[i] = cpt_priority[i]; adv_data->cpt_mask |= cpt_priority[i]; } if (svc_info && info_len) { adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; os_memcpy(adv_data->svc_info, svc_info, info_len); } /* * Group Advertisements by service string. They do not need to be * sorted, but groups allow easier Probe Response instance grouping */ tmp = p2p->p2ps_adv_list; prev = &p2p->p2ps_adv_list; while (tmp) { if (tmp->id == adv_data->id) { if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) { os_free(adv_data); return -1; } adv_data->next = tmp->next; *prev = adv_data; os_free(tmp); goto inserted; } else { if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) { adv_data->next = tmp->next; tmp->next = adv_data; goto inserted; } } prev = &tmp->next; tmp = tmp->next; } /* No svc_name match found */ adv_data->next = p2p->p2ps_adv_list; p2p->p2ps_adv_list = adv_data; inserted: p2p_dbg(p2p, "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x", adv_id, adv_data->config_methods, svc_state, adv_str, adv_data->cpt_mask); return 0; } void p2p_service_flush_asp(struct p2p_data *p2p) { struct p2ps_advertisement *adv, *prev; if (!p2p) return; adv = p2p->p2ps_adv_list; while (adv) { prev = adv; adv = adv->next; os_free(prev); } p2p->p2ps_adv_list = NULL; p2ps_prov_free(p2p); p2p_dbg(p2p, "All ASP advertisements flushed"); } int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_p2p_ie(p2p_ie, &msg)) return -1; if (msg.p2p_device_addr) { os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); return 0; } else if (msg.device_id) { os_memcpy(dev_addr, msg.device_id, ETH_ALEN); return 0; } return -1; } int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) { struct wpabuf *p2p_ie; int ret; p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); if (p2p_ie == NULL) return -1; ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr); wpabuf_free(p2p_ie); return ret; } static void p2p_clear_go_neg(struct p2p_data *p2p) { p2p->go_neg_peer = NULL; p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); } void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) { if (p2p->go_neg_peer == NULL) { p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification"); return; /* No pending Group Formation */ } if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != 0) { p2p_dbg(p2p, "Ignore WPS registration success notification for " MACSTR " (GO Negotiation peer " MACSTR ")", MAC2STR(mac_addr), MAC2STR(p2p->go_neg_peer->intended_addr)); return; /* Ignore unexpected peer address */ } p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR, MAC2STR(mac_addr)); p2p_clear_go_neg(p2p); } void p2p_group_formation_failed(struct p2p_data *p2p) { if (p2p->go_neg_peer == NULL) { p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification"); return; /* No pending Group Formation */ } p2p_dbg(p2p, "Group Formation failed with " MACSTR, MAC2STR(p2p->go_neg_peer->intended_addr)); p2p_clear_go_neg(p2p); } bool is_p2p_6ghz_disabled(struct p2p_data *p2p) { if (p2p) return p2p->cfg->p2p_6ghz_disable; return false; } struct p2p_data * p2p_init(const struct p2p_config *cfg) { struct p2p_data *p2p; if (cfg->max_peers < 1 || cfg->passphrase_len < 8 || cfg->passphrase_len > 63) return NULL; p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); if (p2p == NULL) return NULL; p2p->cfg = (struct p2p_config *) (p2p + 1); os_memcpy(p2p->cfg, cfg, sizeof(*cfg)); if (cfg->dev_name) p2p->cfg->dev_name = os_strdup(cfg->dev_name); if (cfg->manufacturer) p2p->cfg->manufacturer = os_strdup(cfg->manufacturer); if (cfg->model_name) p2p->cfg->model_name = os_strdup(cfg->model_name); if (cfg->model_number) p2p->cfg->model_number = os_strdup(cfg->model_number); if (cfg->serial_number) p2p->cfg->serial_number = os_strdup(cfg->serial_number); if (cfg->pref_chan) { p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan * sizeof(struct p2p_channel)); if (p2p->cfg->pref_chan) { os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan, cfg->num_pref_chan * sizeof(struct p2p_channel)); } else p2p->cfg->num_pref_chan = 0; } p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash); p2p->min_disc_int = 1; p2p->max_disc_int = 3; p2p->max_disc_tu = -1; if (os_get_random(&p2p->next_tie_breaker, 1) < 0) p2p->next_tie_breaker = 0; p2p->next_tie_breaker &= 0x01; if (cfg->sd_request) p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; if (cfg->concurrent_operations) p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER; p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dl_list_init(&p2p->devices); p2p->go_timeout = 100; p2p->client_timeout = 20; p2p->num_p2p_sd_queries = 0; p2p_dbg(p2p, "initialized"); p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); return p2p; } void p2p_deinit(struct p2p_data *p2p) { #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); wpabuf_free(p2p->wfd_ie_probe_resp); wpabuf_free(p2p->wfd_ie_assoc_req); wpabuf_free(p2p->wfd_ie_invitation); wpabuf_free(p2p->wfd_ie_prov_disc_req); wpabuf_free(p2p->wfd_ie_prov_disc_resp); wpabuf_free(p2p->wfd_ie_go_neg); wpabuf_free(p2p->wfd_dev_info); wpabuf_free(p2p->wfd_assoc_bssid); wpabuf_free(p2p->wfd_coupled_sink_info); wpabuf_free(p2p->wfd_r2_dev_info); #endif /* CONFIG_WIFI_DISPLAY */ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); p2p_flush(p2p); p2p_free_req_dev_types(p2p); os_free(p2p->cfg->dev_name); os_free(p2p->cfg->manufacturer); os_free(p2p->cfg->model_name); os_free(p2p->cfg->model_number); os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); p2p_service_flush_asp(p2p); os_free(p2p); } void p2p_flush(struct p2p_data *p2p) { struct p2p_device *dev, *prev; p2p_ext_listen(p2p, 0, 0); p2p_stop_find(p2p); dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, list) { dl_list_del(&dev->list); p2p_device_free(p2p, dev); } p2p_free_sd_queries(p2p); p2p->ssid_set = 0; p2ps_prov_free(p2p); p2p_reset_pending_pd(p2p); p2p->override_pref_op_class = 0; p2p->override_pref_channel = 0; } int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); if (dev == NULL) return -1; p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr)); if (p2p->go_neg_peer == dev) { eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); p2p->go_neg_peer = NULL; } dev->wps_method = WPS_NOT_READY; dev->oob_pw_id = 0; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; return 0; } int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name) { os_free(p2p->cfg->dev_name); if (dev_name) { p2p->cfg->dev_name = os_strdup(dev_name); if (p2p->cfg->dev_name == NULL) return -1; } else p2p->cfg->dev_name = NULL; return 0; } int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer) { os_free(p2p->cfg->manufacturer); p2p->cfg->manufacturer = NULL; if (manufacturer) { p2p->cfg->manufacturer = os_strdup(manufacturer); if (p2p->cfg->manufacturer == NULL) return -1; } return 0; } int p2p_set_model_name(struct p2p_data *p2p, const char *model_name) { os_free(p2p->cfg->model_name); p2p->cfg->model_name = NULL; if (model_name) { p2p->cfg->model_name = os_strdup(model_name); if (p2p->cfg->model_name == NULL) return -1; } return 0; } int p2p_set_model_number(struct p2p_data *p2p, const char *model_number) { os_free(p2p->cfg->model_number); p2p->cfg->model_number = NULL; if (model_number) { p2p->cfg->model_number = os_strdup(model_number); if (p2p->cfg->model_number == NULL) return -1; } return 0; } int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number) { os_free(p2p->cfg->serial_number); p2p->cfg->serial_number = NULL; if (serial_number) { p2p->cfg->serial_number = os_strdup(serial_number); if (p2p->cfg->serial_number == NULL) return -1; } return 0; } void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods) { p2p->cfg->config_methods = config_methods; } void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid) { os_memcpy(p2p->cfg->uuid, uuid, 16); } int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type) { os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8); return 0; } int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], size_t num_dev_types) { if (num_dev_types > P2P_SEC_DEVICE_TYPES) num_dev_types = P2P_SEC_DEVICE_TYPES; p2p->cfg->num_sec_dev_types = num_dev_types; os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8); return 0; } void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p) { int i; for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(p2p->wps_vendor_ext[i]); p2p->wps_vendor_ext[i] = NULL; } } int p2p_add_wps_vendor_extension(struct p2p_data *p2p, const struct wpabuf *vendor_ext) { int i; if (vendor_ext == NULL) return -1; for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (p2p->wps_vendor_ext[i] == NULL) break; } if (i >= P2P_MAX_WPS_VENDOR_EXT) return -1; p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext); if (p2p->wps_vendor_ext[i] == NULL) return -1; return 0; } int p2p_set_country(struct p2p_data *p2p, const char *country) { os_memcpy(p2p->cfg->country, country, 3); return 0; } static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) { int res; if (dev->sd_pending_bcast_queries == 0) { /* Initialize with total number of registered broadcast * SD queries. */ dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries; } res = p2p_start_sd(p2p, dev); if (res == -2) return -2; if (res == 0) return 1; if (dev->req_config_methods && !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) return 1; } return 0; } void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; int found, res; p2p_set_state(p2p, P2P_SEARCH); /* Continue from the device following the last iteration */ found = 0; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (dev == p2p->last_p2p_find_oper) { found = 1; continue; } if (!found) continue; res = p2p_pre_find_operation(p2p, dev); if (res > 0) { p2p->last_p2p_find_oper = dev; return; } if (res == -2) goto skip_sd; } /* * Wrap around to the beginning of the list and continue until the last * iteration device. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { res = p2p_pre_find_operation(p2p, dev); if (res > 0) { p2p->last_p2p_find_oper = dev; return; } if (res == -2) goto skip_sd; if (dev == p2p->last_p2p_find_oper) break; } skip_sd: os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_listen_in_find(p2p, 1); } static void p2p_sd_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", success); p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { if (p2p->sd_peer) { if (is_zero_ether_addr(p2p->sd_query_no_ack)) { os_memcpy(p2p->sd_query_no_ack, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN); p2p_dbg(p2p, "First SD Query no-ACK in this search iteration: " MACSTR, MAC2STR(p2p->sd_query_no_ack)); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } p2p->sd_peer = NULL; if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); return; } if (p2p->sd_peer == NULL) { p2p_dbg(p2p, "No SD peer entry known"); if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); return; } if (p2p->sd_query && p2p->sd_query->for_all_peers) { /* Update the pending broadcast SD query count for this device */ p2p->sd_peer->sd_pending_bcast_queries--; /* * If there are no pending broadcast queries for this device, * mark it as done (-1). */ if (p2p->sd_peer->sd_pending_bcast_queries == 0) p2p->sd_peer->sd_pending_bcast_queries = -1; } /* Wait for response from the peer */ p2p_set_state(p2p, P2P_SD_DURING_FIND); p2p_set_timeout(p2p, 0, 200000); } /** * p2p_retry_pd - Retry any pending provision disc requests in IDLE state * @p2p: P2P module context from p2p_init() */ static void p2p_retry_pd(struct p2p_data *p2p) { struct p2p_device *dev; /* * Retry the prov disc req attempt only for the peer that the user had * requested. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN) != 0) continue; if (!dev->req_config_methods) continue; p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); p2p_send_prov_disc_req(p2p, dev, dev->flags & P2P_DEV_PD_FOR_JOIN, p2p->pd_force_freq); return; } } static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d", success); /* * Postpone resetting the pending action state till after we actually * time out. This allows us to take some action like notifying any * interested parties about no response to the request. * * When the timer (below) goes off we check in IDLE, SEARCH, or * LISTEN_ONLY state, which are the only allowed states to issue a PD * requests in, if this was still pending and then raise notification. */ if (!success) { p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p->user_initiated_pd && (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY)) { /* Retry request from timeout to avoid busy loops */ p2p->pending_action_state = P2P_PENDING_PD; p2p_set_timeout(p2p, 0, 50000); } else if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); else if (p2p->user_initiated_pd) { p2p->pending_action_state = P2P_PENDING_PD; p2p_set_timeout(p2p, 0, 300000); } return; } /* * If after PD Request the peer doesn't expect to receive PD Response * the PD Request ACK indicates a completion of the current PD. This * happens only on the advertiser side sending the follow-on PD Request * with the status different than 12 (Success: accepted by user). */ if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker && p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) { p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK"); if (p2p->send_action_in_progress) { p2p->send_action_in_progress = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p->cfg->p2ps_prov_complete) { p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, p2p->p2ps_prov->status, p2p->p2ps_prov->adv_mac, p2p->p2ps_prov->adv_mac, p2p->p2ps_prov->session_mac, NULL, p2p->p2ps_prov->adv_id, p2p->p2ps_prov->session_id, 0, 0, NULL, 0, 0, 0, NULL, NULL, 0, 0, NULL, 0); } if (p2p->user_initiated_pd) p2p_reset_pending_pd(p2p); p2ps_prov_free(p2p); return; } /* * This postponing, of resetting pending_action_state, needs to be * done only for user initiated PD requests and not internal ones. */ if (p2p->user_initiated_pd) p2p->pending_action_state = P2P_PENDING_PD; else p2p->pending_action_state = P2P_NO_PENDING_ACTION; /* Wait for response from the peer */ if (p2p->state == P2P_SEARCH) p2p_set_state(p2p, P2P_PD_DURING_FIND); p2p_set_timeout(p2p, 0, 200000); } static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d", success); if (p2p->send_action_in_progress) { p2p->send_action_in_progress = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { if (p2p->state == P2P_SEARCH) p2p_continue_find(p2p); return; } if (!p2p->cfg->prov_disc_resp_cb || p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1) { if (p2p->state == P2P_SEARCH) p2p_continue_find(p2p); return; } p2p_dbg(p2p, "Post-Provision Discovery operations started - do not try to continue other P2P operations"); } int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len) { if (os_reltime_before(rx_time, &p2p->find_start)) { /* * The driver may have cached (e.g., in cfg80211 BSS table) the * scan results for relatively long time. To avoid reporting * stale information, update P2P peers only based on results * that have based on frames received after the last p2p_find * operation was started. */ p2p_dbg(p2p, "Ignore old scan result for " MACSTR " (rx_time=%u.%06u find_start=%u.%06u)", MAC2STR(bssid), (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) p2p->find_start.sec, (unsigned int) p2p->find_start.usec); return 0; } p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1); return 0; } void p2p_scan_res_handled(struct p2p_data *p2p, unsigned int delay) { if (!p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); } p2p->p2p_scan_running = 0; /* Use this delay only when p2p_find doesn't set it */ if (!p2p->search_delay) p2p->search_delay = delay; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); if (p2p_run_after_scan(p2p)) return; if (p2p->state == P2P_SEARCH) p2p_continue_find(p2p); } void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, unsigned int bands) { u8 dev_capab; u8 *len; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_req) wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]) wpabuf_put_buf(ies, p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]); len = p2p_buf_add_ie_hdr(ies); dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; /* P2PS requires Probe Request frames to include SD bit */ if (p2p->p2ps_seek && p2p->p2ps_seek_count) dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; p2p_buf_add_capability(ies, dev_capab, 0); if (dev_id) p2p_buf_add_device_id(ies, dev_id); if (p2p->cfg->reg_class && p2p->cfg->channel) p2p_buf_add_listen_channel(ies, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); if (bands & BAND_60_GHZ) p2p_buf_add_device_info(ies, p2p, NULL); if (p2p->p2ps_seek && p2p->p2ps_seek_count) p2p_buf_add_service_hash(ies, p2p); /* TODO: p2p_buf_add_operating_channel() if GO */ p2p_buf_update_ie_hdr(ies, len); } size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) { size_t len = 100; #ifdef CONFIG_WIFI_DISPLAY if (p2p && p2p->wfd_ie_probe_req) len += wpabuf_len(p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p && p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]) len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]); return len; } int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) { return p2p_attr_text(p2p_ie, buf, end); } static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) { struct p2p_device *dev = p2p->go_neg_peer; int timeout; p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success); if (dev == NULL) { p2p_dbg(p2p, "No pending GO Negotiation"); return; } if (success) { if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_set_state(p2p, P2P_IDLE); return; } } else if (dev->go_neg_req_sent) { /* Cancel the increment from p2p_connect_send() on failure */ dev->go_neg_req_sent--; } if (!success && (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && !is_zero_ether_addr(dev->member_in_go_dev)) { p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO", MAC2STR(dev->info.p2p_device_addr)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_send_dev_disc_req(p2p, dev); return; } /* * Use P2P find, if needed, to find the other device from its listen * channel. */ p2p_set_state(p2p, P2P_CONNECT); timeout = success ? 500000 : 100000; if (!success && p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { unsigned int r; /* * Peer is expected to wait our response and we will skip the * listen phase. Add some randomness to the wait time here to * make it less likely to hit cases where we could end up in * sync with peer not listening. */ if (os_get_random((u8 *) &r, sizeof(r)) < 0) r = 0; timeout += r % 100000; } p2p_set_timeout(p2p, 0, timeout); } static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d", success); if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore"); return; } p2p_set_state(p2p, P2P_CONNECT); p2p_set_timeout(p2p, 0, 500000); } static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, const u8 *addr) { p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success); if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { p2p_go_neg_failed(p2p, p2p->go_neg_peer->status); return; } if (success) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); if (dev && dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; } if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND) p2p_continue_find(p2p); } static void p2p_go_neg_conf_cb(struct p2p_data *p2p, enum p2p_send_action_result result) { struct p2p_device *dev; p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); if (result == P2P_SEND_ACTION_FAILED) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_go_neg_failed(p2p, -1); return; } dev = p2p->go_neg_peer; if (result == P2P_SEND_ACTION_NO_ACK) { /* * Retry GO Negotiation Confirmation * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive * ACK for confirmation. */ if (dev && dev->go_neg_conf && dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) { p2p_dbg(p2p, "GO Negotiation Confirm retry %d", dev->go_neg_conf_sent); p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; if (p2p_send_action(p2p, dev->go_neg_conf_freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(dev->go_neg_conf), wpabuf_len(dev->go_neg_conf), 0) >= 0) { dev->go_neg_conf_sent++; return; } p2p_dbg(p2p, "Failed to re-send Action frame"); /* * Continue with the assumption that the first attempt * went through and just the ACK frame was lost. */ } /* * It looks like the TX status for GO Negotiation Confirm is * often showing failure even when the peer has actually * received the frame. Since the peer may change channels * immediately after having received the frame, we may not see * an Ack for retries, so just dropping a single frame may * trigger this. To allow the group formation to succeed if the * peer did indeed receive the frame, continue regardless of * the TX status. */ p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (dev == NULL) return; p2p_go_complete(p2p, dev); } void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, enum p2p_send_action_result result) { enum p2p_pending_action_state state; int success; p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)", p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), result, p2p_state_txt(p2p->state)); success = result == P2P_SEND_ACTION_SUCCESS; state = p2p->pending_action_state; p2p->pending_action_state = P2P_NO_PENDING_ACTION; switch (state) { case P2P_NO_PENDING_ACTION: if (p2p->send_action_in_progress) { p2p->send_action_in_progress = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE: p2p_go_neg_resp_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: p2p_go_neg_resp_failure_cb(p2p, success, dst); break; case P2P_PENDING_GO_NEG_CONFIRM: p2p_go_neg_conf_cb(p2p, result); break; case P2P_PENDING_SD: p2p_sd_cb(p2p, success); break; case P2P_PENDING_PD: p2p_prov_disc_cb(p2p, success); break; case P2P_PENDING_PD_RESPONSE: p2p_prov_disc_resp_cb(p2p, success); break; case P2P_PENDING_INVITATION_REQUEST: p2p_invitation_req_cb(p2p, success); break; case P2P_PENDING_INVITATION_RESPONSE: p2p_invitation_resp_cb(p2p, success); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); break; case P2P_PENDING_DEV_DISC_RESPONSE: p2p_dev_disc_resp_cb(p2p, success); break; case P2P_PENDING_GO_DISC_REQ: p2p_go_disc_req_cb(p2p, success); break; } } void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, unsigned int duration) { if (freq == p2p->pending_client_disc_freq) { p2p_dbg(p2p, "Client discoverability remain-awake completed"); p2p->pending_client_disc_freq = 0; return; } if (freq != p2p->pending_listen_freq) { p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)", freq, duration, p2p->pending_listen_freq); return; } p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", p2p->pending_listen_sec, p2p->pending_listen_usec, p2p->pending_listen_freq); p2p->in_listen = 1; p2p->drv_in_listen = freq; if (p2p->pending_listen_sec || p2p->pending_listen_usec) { /* * Add 20 msec extra wait to avoid race condition with driver * remain-on-channel end event, i.e., give driver more time to * complete the operation before our timeout expires. */ p2p_set_timeout(p2p, p2p->pending_listen_sec, p2p->pending_listen_usec + 20000); } p2p->pending_listen_freq = 0; } int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) { p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq); p2p->drv_in_listen = 0; if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer && p2p->pending_listen_freq) { /* * Better wait a bit if the driver is unable to start * offchannel operation for some reason to continue with * P2P_WAIT_PEER_(IDLE/CONNECT) state transitions. */ p2p_dbg(p2p, "Listen operation did not seem to start - delay idle phase to avoid busy loop"); p2p_set_timeout(p2p, 0, 100000); return 1; } if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); p2p_go_neg_failed(p2p, -1); return 0; } p2p_set_state(p2p, P2P_CONNECT); p2p_connect_send(p2p, p2p->go_neg_peer); return 1; } else if (p2p->state == P2P_SEARCH) { if (p2p->p2p_scan_running) { /* * Search is already in progress. This can happen if * an Action frame RX is reported immediately after * the end of a remain-on-channel operation and the * response frame to that is sent using an offchannel * operation while in p2p_find. Avoid an attempt to * restart a scan here. */ p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one"); return 1; } if (p2p->pending_listen_freq) { /* * Better wait a bit if the driver is unable to start * offchannel operation for some reason. p2p_search() * will be started from internal timeout. */ p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop"); p2p_set_timeout(p2p, 0, 100000); return 1; } if (p2p->search_delay) { p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p_set_timeout(p2p, p2p->search_delay / 1000, (p2p->search_delay % 1000) * 1000); return 1; } p2p_search(p2p); return 1; } return 0; } static void p2p_timeout_connect(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); p2p_go_neg_failed(p2p, -1); return; } if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) && p2p->go_neg_peer->connect_reqs < 120) { p2p_dbg(p2p, "Peer expected to wait our response - skip listen"); p2p_connect_send(p2p, p2p->go_neg_peer); return; } if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) { p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)"); p2p_set_state(p2p, P2P_CONNECT_LISTEN); p2p_set_timeout(p2p, 0, 30000); return; } p2p_set_state(p2p, P2P_CONNECT_LISTEN); p2p_listen_in_find(p2p, 0); } static void p2p_timeout_connect_listen(struct p2p_data *p2p) { if (p2p->go_neg_peer) { if (p2p->drv_in_listen) { p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete"); return; } if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); p2p_go_neg_failed(p2p, -1); return; } p2p_set_state(p2p, P2P_CONNECT); p2p_connect_send(p2p, p2p->go_neg_peer); } else p2p_set_state(p2p, P2P_IDLE); } static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) { p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); if (p2p->cfg->is_concurrent_session_active && p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx)) p2p_set_timeout(p2p, 0, 500000); else p2p_set_timeout(p2p, 0, 200000); } static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) { struct p2p_device *dev = p2p->go_neg_peer; if (dev == NULL) { p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); return; } p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); if (p2p->pending_listen_freq) { p2p_dbg(p2p, "Clear pending_listen_freq for %s", __func__); p2p->pending_listen_freq = 0; } p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); p2p_listen_in_find(p2p, 0); } static void p2p_timeout_sd_during_find(struct p2p_data *p2p) { p2p_dbg(p2p, "Service Discovery Query timeout"); if (p2p->sd_peer) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->sd_peer = NULL; } p2p_continue_find(p2p); } static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) { p2p_dbg(p2p, "Provision Discovery Request timeout"); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_continue_find(p2p); } static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) { u32 adv_id = 0; u8 *adv_mac = NULL; p2p->pending_action_state = P2P_NO_PENDING_ACTION; /* * For user initiated PD requests that we have not gotten any responses * for while in IDLE state, we retry them a couple of times before * giving up. */ if (!p2p->user_initiated_pd) return; p2p_dbg(p2p, "User initiated Provision Discovery Request timeout"); if (p2p->pd_retries) { p2p->pd_retries--; p2p_retry_pd(p2p); } else { struct p2p_device *dev; int for_join = 0; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { if (os_memcmp(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN) != 0) continue; if (dev->req_config_methods && (dev->flags & P2P_DEV_PD_FOR_JOIN)) for_join = 1; } if (p2p->p2ps_prov) { adv_id = p2p->p2ps_prov->adv_id; adv_mac = p2p->p2ps_prov->adv_mac; } if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, p2p->pending_pd_devaddr, for_join ? P2P_PROV_DISC_TIMEOUT_JOIN : P2P_PROV_DISC_TIMEOUT, adv_id, adv_mac, NULL); p2p_reset_pending_pd(p2p); } } static void p2p_timeout_invite(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_set_state(p2p, P2P_INVITE_LISTEN); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { /* * Better remain on operating channel instead of listen channel * when running a group. */ p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel"); p2p_set_timeout(p2p, 0, 100000); return; } p2p_listen_in_find(p2p, 0); } static void p2p_timeout_invite_listen(struct p2p_data *p2p) { if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { p2p_set_state(p2p, P2P_INVITE); p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } else { if (p2p->invite_peer) { p2p_dbg(p2p, "Invitation Request retry limit reached"); if (p2p->cfg->invitation_result) p2p->cfg->invitation_result( p2p->cfg->cb_ctx, -1, NULL, NULL, p2p->invite_peer->info.p2p_device_addr, 0, 0); } p2p_set_state(p2p, P2P_IDLE); } } static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state)); p2p->in_listen = 0; if (p2p->drv_in_listen) { p2p_dbg(p2p, "Driver is still in listen state - stop it"); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); } switch (p2p->state) { case P2P_IDLE: /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); break; case P2P_SEARCH: /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); if (p2p->search_delay && !p2p->in_search_delay) { p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p->in_search_delay = 1; p2p_set_timeout(p2p, p2p->search_delay / 1000, (p2p->search_delay % 1000) * 1000); break; } p2p->in_search_delay = 0; p2p_search(p2p); break; case P2P_CONNECT: p2p_timeout_connect(p2p); break; case P2P_CONNECT_LISTEN: p2p_timeout_connect_listen(p2p); break; case P2P_GO_NEG: break; case P2P_LISTEN_ONLY: /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); if (p2p->ext_listen_only) { p2p_dbg(p2p, "Extended Listen Timing - Listen State completed"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } break; case P2P_WAIT_PEER_CONNECT: p2p_timeout_wait_peer_connect(p2p); break; case P2P_WAIT_PEER_IDLE: p2p_timeout_wait_peer_idle(p2p); break; case P2P_SD_DURING_FIND: p2p_timeout_sd_during_find(p2p); break; case P2P_PROVISIONING: break; case P2P_PD_DURING_FIND: p2p_timeout_prov_disc_during_find(p2p); break; case P2P_INVITE: p2p_timeout_invite(p2p); break; case P2P_INVITE_LISTEN: p2p_timeout_invite_listen(p2p); break; } } int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) { struct p2p_device *dev; dev = p2p_get_device(p2p, peer_addr); p2p_dbg(p2p, "Local request to reject connection attempts by peer " MACSTR, MAC2STR(peer_addr)); if (dev == NULL) { p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr)); return -1; } dev->status = P2P_SC_FAIL_REJECTED_BY_USER; dev->flags |= P2P_DEV_USER_REJECTED; return 0; } const char * p2p_wps_method_text(enum p2p_wps_method method) { switch (method) { case WPS_NOT_READY: return "not-ready"; case WPS_PIN_DISPLAY: return "Display"; case WPS_PIN_KEYPAD: return "Keypad"; case WPS_PBC: return "PBC"; case WPS_NFC: return "NFC"; case WPS_P2PS: return "P2PS"; } return "??"; } static const char * p2p_go_state_text(enum p2p_go_state go_state) { switch (go_state) { case UNKNOWN_GO: return "unknown"; case LOCAL_GO: return "local"; case REMOTE_GO: return "remote"; } return "??"; } const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next) { struct p2p_device *dev; if (addr) dev = p2p_get_device(p2p, addr); else dev = dl_list_first(&p2p->devices, struct p2p_device, list); if (dev && next) { dev = dl_list_first(&dev->list, struct p2p_device, list); if (&dev->list == &p2p->devices) dev = NULL; } if (dev == NULL) return NULL; return &dev->info; } int p2p_get_peer_info_txt(const struct p2p_peer_info *info, char *buf, size_t buflen) { struct p2p_device *dev; int res; char *pos, *end; struct os_reltime now; if (info == NULL) return -1; dev = (struct p2p_device *) (((u8 *) info) - offsetof(struct p2p_device, info)); pos = buf; end = buf + buflen; os_get_reltime(&now); res = os_snprintf(pos, end - pos, "age=%d\n" "listen_freq=%d\n" "wps_method=%s\n" "interface_addr=" MACSTR "\n" "member_in_go_dev=" MACSTR "\n" "member_in_go_iface=" MACSTR "\n" "go_neg_req_sent=%d\n" "go_state=%s\n" "dialog_token=%u\n" "intended_addr=" MACSTR "\n" "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), dev->listen_freq, p2p_wps_method_text(dev->wps_method), MAC2STR(dev->interface_addr), MAC2STR(dev->member_in_go_dev), MAC2STR(dev->member_in_go_iface), dev->go_neg_req_sent, p2p_go_state_text(dev->go_state), dev->dialog_token, MAC2STR(dev->intended_addr), dev->country[0] ? dev->country[0] : '_', dev->country[1] ? dev->country[1] : '_', dev->oper_freq, dev->req_config_methods, dev->flags & P2P_DEV_PROBE_REQ_ONLY ? "[PROBE_REQ_ONLY]" : "", dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", dev->flags & P2P_DEV_NOT_YET_READY ? "[NOT_YET_READY]" : "", dev->flags & P2P_DEV_PD_PEER_DISPLAY ? "[PD_PEER_DISPLAY]" : "", dev->flags & P2P_DEV_PD_PEER_KEYPAD ? "[PD_PEER_KEYPAD]" : "", dev->flags & P2P_DEV_PD_PEER_P2PS ? "[PD_PEER_P2PS]" : "", dev->flags & P2P_DEV_USER_REJECTED ? "[USER_REJECTED]" : "", dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? "[PEER_WAITING_RESPONSE]" : "", dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ? "[PREFER_PERSISTENT_GROUP]" : "", dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ? "[WAIT_GO_NEG_RESPONSE]" : "", dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ? "[WAIT_GO_NEG_CONFIRM]" : "", dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ? "[GROUP_CLIENT_ONLY]" : "", dev->flags & P2P_DEV_FORCE_FREQ ? "[FORCE_FREQ]" : "", dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ? "[LAST_SEEN_AS_GROUP_CLIENT]" : "", dev->status, dev->invitation_reqs); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; if (dev->ext_listen_period) { res = os_snprintf(pos, end - pos, "ext_listen_period=%u\n" "ext_listen_interval=%u\n", dev->ext_listen_period, dev->ext_listen_interval); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } if (dev->oper_ssid_len) { res = os_snprintf(pos, end - pos, "oper_ssid=%s\n", wpa_ssid_txt(dev->oper_ssid, dev->oper_ssid_len)); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } #ifdef CONFIG_WIFI_DISPLAY if (dev->info.wfd_subelems) { res = os_snprintf(pos, end - pos, "wfd_subelems="); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(dev->info.wfd_subelems), wpabuf_len(dev->info.wfd_subelems)); res = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } #endif /* CONFIG_WIFI_DISPLAY */ return pos - buf; } int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) { return p2p_get_device(p2p, addr) != NULL; } void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) { if (enabled) { p2p_dbg(p2p, "Client discoverability enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } else { p2p_dbg(p2p, "Client discoverability disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } } static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1, u32 duration2, u32 interval2) { struct wpabuf *req; struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL; u8 *len; req = wpabuf_alloc(100); if (req == NULL) return NULL; if (duration1 || interval1) { os_memset(&desc1, 0, sizeof(desc1)); desc1.count_type = 1; desc1.duration = duration1; desc1.interval = interval1; ptr1 = &desc1; if (duration2 || interval2) { os_memset(&desc2, 0, sizeof(desc2)); desc2.count_type = 2; desc2.duration = duration2; desc2.interval = interval2; ptr2 = &desc2; } } p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1); len = p2p_buf_add_ie_hdr(req); p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2); p2p_buf_update_ie_hdr(req, len); return req; } int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, const u8 *own_interface_addr, unsigned int freq, u32 duration1, u32 interval1, u32 duration2, u32 interval2) { struct wpabuf *req; p2p_dbg(p2p, "Send Presence Request to GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u int1=%u " "dur2=%u int2=%u", MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), freq, duration1, interval1, duration2, interval2); req = p2p_build_presence_req(duration1, interval1, duration2, interval2); if (req == NULL) return -1; p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, go_interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); return 0; } static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa, size_t noa_len, u8 dialog_token) { struct wpabuf *resp; u8 *len; resp = wpabuf_alloc(100 + noa_len); if (resp == NULL) return NULL; p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token); len = p2p_buf_add_ie_hdr(resp); p2p_buf_add_status(resp, status); if (noa) { wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE); wpabuf_put_le16(resp, noa_len); wpabuf_put_data(resp, noa, noa_len); } else p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL); p2p_buf_update_ie_hdr(resp, len); return resp; } static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_message msg; u8 status; struct wpabuf *resp; size_t g; struct p2p_group *group = NULL; int parsed = 0; u8 noa[50]; int noa_len; p2p_dbg(p2p, "Received P2P Action - P2P Presence Request"); for (g = 0; g < p2p->num_groups; g++) { if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), ETH_ALEN) == 0) { group = p2p->groups[g]; break; } } if (group == NULL) { p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group " MACSTR, MAC2STR(da)); return; } if (p2p_parse(data, len, &msg) < 0) { p2p_dbg(p2p, "Failed to parse P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } parsed = 1; if (msg.noa == NULL) { p2p_dbg(p2p, "No NoA attribute in P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len); fail: if (p2p->cfg->get_noa) noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa, sizeof(noa)); else noa_len = -1; resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL, noa_len > 0 ? noa_len : 0, msg.dialog_token); if (parsed) p2p_parse_free(&msg); if (resp == NULL) return; p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, da, da, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len) { struct p2p_message msg; p2p_dbg(p2p, "Received P2P Action - P2P Presence Response"); if (p2p_parse(data, len, &msg) < 0) { p2p_dbg(p2p, "Failed to parse P2P Presence Response"); return; } if (msg.status == NULL || msg.noa == NULL) { p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response"); p2p_parse_free(&msg); return; } if (p2p->cfg->presence_resp) { p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status, msg.noa, msg.noa_len); } if (*msg.status) { p2p_dbg(p2p, "P2P Presence Request was rejected: status %u", *msg.status); p2p_parse_free(&msg); return; } p2p_dbg(p2p, "P2P Presence Request was accepted"); wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", msg.noa, msg.noa_len); /* TODO: process NoA */ p2p_parse_free(&msg); } static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; if (p2p->ext_listen_interval) { /* Schedule next extended listen timeout */ eloop_register_timeout(p2p->ext_listen_interval_sec, p2p->ext_listen_interval_usec, p2p_ext_listen_timeout, p2p, NULL); } if ((p2p->cfg->is_p2p_in_progress && p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) || (p2p->pending_action_state == P2P_PENDING_PD && p2p->pd_retries > 0)) { p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)", p2p_state_txt(p2p->state)); return; } if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { /* * This should not really happen, but it looks like the Listen * command may fail is something else (e.g., a scan) was * running at an inconvenient time. As a workaround, allow new * Extended Listen operation to be started. */ p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } if (p2p->state != P2P_IDLE) { p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state)); return; } p2p_dbg(p2p, "Extended Listen timeout"); p2p->ext_listen_only = 1; if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing"); p2p->ext_listen_only = 0; } } int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, unsigned int interval) { if (period > 65535 || interval > 65535 || period > interval || (period == 0 && interval > 0) || (period > 0 && interval == 0)) { p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u", period, interval); return -1; } eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); if (interval == 0) { p2p_dbg(p2p, "Disabling Extended Listen Timing"); p2p->ext_listen_period = 0; p2p->ext_listen_interval = 0; return 0; } p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec", period, interval); p2p->ext_listen_period = period; p2p->ext_listen_interval = interval; p2p->ext_listen_interval_sec = interval / 1000; p2p->ext_listen_interval_usec = (interval % 1000) * 1000; eloop_register_timeout(p2p->ext_listen_interval_sec, p2p->ext_listen_interval_usec, p2p_ext_listen_timeout, p2p, NULL); return 0; } void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len) { struct p2p_message msg; if (bssid == NULL || ie == NULL) return; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; if (msg.minor_reason_code == NULL) { p2p_parse_free(&msg); return; } p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); p2p_parse_free(&msg); } void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len) { struct p2p_message msg; if (bssid == NULL || ie == NULL) return; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; if (msg.minor_reason_code == NULL) { p2p_parse_free(&msg); return; } p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); p2p_parse_free(&msg); } void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) { if (enabled) { p2p_dbg(p2p, "Managed P2P Device operations enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; } else { p2p_dbg(p2p, "Managed P2P Device operations disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; } } int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, u8 *op_channel, struct wpa_freq_range_list *avoid_list, struct wpa_freq_range_list *disallow_list) { return p2p_channel_random_social(&p2p->channels, op_class, op_channel, avoid_list, disallow_list); } int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, u8 forced) { if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; /* * Listen channel was set in configuration or set by control interface; * cannot override it. */ if (p2p->cfg->channel_forced && forced == 0) { p2p_dbg(p2p, "Listen channel was previously configured - do not override based on optimization"); return -1; } p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", reg_class, channel); if (p2p->state == P2P_IDLE) { p2p->cfg->reg_class = reg_class; p2p->cfg->channel = channel; p2p->cfg->channel_forced = forced; } else { p2p_dbg(p2p, "Defer setting listen channel"); p2p->pending_reg_class = reg_class; p2p->pending_channel = channel; p2p->pending_channel_forced = forced; } return 0; } u8 p2p_get_listen_channel(struct p2p_data *p2p) { return p2p->cfg->channel; } int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) { p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); if (postfix == NULL) { p2p->cfg->ssid_postfix_len = 0; return 0; } if (len > sizeof(p2p->cfg->ssid_postfix)) return -1; os_memcpy(p2p->cfg->ssid_postfix, postfix, len); p2p->cfg->ssid_postfix_len = len; return 0; } int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel) { if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) return -1; p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u", op_reg_class, op_channel); p2p->cfg->op_reg_class = op_reg_class; p2p->cfg->op_channel = op_channel; p2p->cfg->cfg_op_channel = cfg_op_channel; return 0; } int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan) { struct p2p_channel *n; if (pref_chan) { n = os_memdup(pref_chan, num_pref_chan * sizeof(struct p2p_channel)); if (n == NULL) return -1; } else n = NULL; os_free(p2p->cfg->pref_chan); p2p->cfg->pref_chan = n; p2p->cfg->num_pref_chan = num_pref_chan; return 0; } int p2p_set_no_go_freq(struct p2p_data *p2p, const struct wpa_freq_range_list *list) { struct wpa_freq_range *tmp; if (list == NULL || list->num == 0) { os_free(p2p->no_go_freq.range); p2p->no_go_freq.range = NULL; p2p->no_go_freq.num = 0; return 0; } tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); if (tmp == NULL) return -1; os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); os_free(p2p->no_go_freq.range); p2p->no_go_freq.range = tmp; p2p->no_go_freq.num = list->num; p2p_dbg(p2p, "Updated no GO chan list"); return 0; } int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { struct p2p_device *dev = p2p_get_device(p2p, dev_addr); if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) return -1; os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); return 0; } int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr) { struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); if (dev == NULL) return -1; os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); return 0; } void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) { os_memcpy(p2p->peer_filter, addr, ETH_ALEN); if (is_zero_ether_addr(p2p->peer_filter)) p2p_dbg(p2p, "Disable peer filter"); else p2p_dbg(p2p, "Enable peer filter for " MACSTR, MAC2STR(p2p->peer_filter)); } void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) { p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled"); if (p2p->cross_connect == enabled) return; p2p->cross_connect = enabled; /* TODO: may need to tear down any action group where we are GO(?) */ } int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) { struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); if (dev == NULL) return -1; if (dev->oper_freq <= 0) return -1; return dev->oper_freq; } void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) { p2p_dbg(p2p, "Intra BSS distribution %s", enabled ? "enabled" : "disabled"); p2p->cfg->p2p_intra_bss = enabled; } void p2p_update_channel_list(struct p2p_data *p2p, const struct p2p_channels *chan, const struct p2p_channels *cli_chan) { p2p_dbg(p2p, "Update channel list"); os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); os_memcpy(&p2p->cfg->cli_channels, cli_chan, sizeof(struct p2p_channels)); p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); } int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time) { int res, scheduled; res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, buf, len, wait_time, &scheduled); if (res == 0 && scheduled && p2p->in_listen && freq > 0 && p2p->drv_in_listen > 0 && (unsigned int) p2p->drv_in_listen != freq) { p2p_dbg(p2p, "Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz", p2p->drv_in_listen, freq); p2p_stop_listen_for_freq(p2p, freq); } return res; } void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall) { p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); p2p->best_freq_24 = freq_24; p2p->best_freq_5 = freq_5; p2p->best_freq_overall = freq_overall; } void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq) { p2p_dbg(p2p, "Own frequency preference: %d MHz", freq); p2p->own_freq_preference = freq; } const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) { if (p2p == NULL || p2p->go_neg_peer == NULL) return NULL; return p2p->go_neg_peer->info.p2p_device_addr; } const struct p2p_peer_info * p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) { struct p2p_device *dev; if (addr) { dev = p2p_get_device(p2p, addr); if (!dev) return NULL; if (!next) { if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) return NULL; return &dev->info; } else { do { dev = dl_list_first(&dev->list, struct p2p_device, list); if (!dev || &dev->list == &p2p->devices) return NULL; } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); } } else { dev = dl_list_first(&p2p->devices, struct p2p_device, list); if (!dev) return NULL; while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev = dl_list_first(&dev->list, struct p2p_device, list); if (!dev || &dev->list == &p2p->devices) return NULL; } } return &dev->info; } int p2p_in_progress(struct p2p_data *p2p) { if (p2p == NULL) return 0; if (p2p->state == P2P_SEARCH) return 2; return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; } void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, u8 client_timeout) { if (p2p) { p2p->go_timeout = go_timeout; p2p->client_timeout = client_timeout; } } #ifdef CONFIG_WIFI_DISPLAY static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) { size_t g; struct p2p_group *group; for (g = 0; g < p2p->num_groups; g++) { group = p2p->groups[g]; p2p_group_force_beacon_update_ies(group); } } int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_beacon); p2p->wfd_ie_beacon = ie; p2p_update_wfd_ie_groups(p2p); return 0; } int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_probe_req); p2p->wfd_ie_probe_req = ie; return 0; } int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_probe_resp); p2p->wfd_ie_probe_resp = ie; p2p_update_wfd_ie_groups(p2p); return 0; } int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_assoc_req); p2p->wfd_ie_assoc_req = ie; return 0; } int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_invitation); p2p->wfd_ie_invitation = ie; return 0; } int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_prov_disc_req); p2p->wfd_ie_prov_disc_req = ie; return 0; } int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_prov_disc_resp); p2p->wfd_ie_prov_disc_resp = ie; return 0; } int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie) { wpabuf_free(p2p->wfd_ie_go_neg); p2p->wfd_ie_go_neg = ie; return 0; } int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_dev_info); if (elem) { p2p->wfd_dev_info = wpabuf_dup(elem); if (p2p->wfd_dev_info == NULL) return -1; } else p2p->wfd_dev_info = NULL; return 0; } int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_r2_dev_info); if (elem) { p2p->wfd_r2_dev_info = wpabuf_dup(elem); if (p2p->wfd_r2_dev_info == NULL) return -1; } else p2p->wfd_r2_dev_info = NULL; return 0; } int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_assoc_bssid); if (elem) { p2p->wfd_assoc_bssid = wpabuf_dup(elem); if (p2p->wfd_assoc_bssid == NULL) return -1; } else p2p->wfd_assoc_bssid = NULL; return 0; } int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_coupled_sink_info); if (elem) { p2p->wfd_coupled_sink_info = wpabuf_dup(elem); if (p2p->wfd_coupled_sink_info == NULL) return -1; } else p2p->wfd_coupled_sink_info = NULL; return 0; } #endif /* CONFIG_WIFI_DISPLAY */ int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, int max_disc_tu) { if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0) return -1; p2p->min_disc_int = min_disc_int; p2p->max_disc_int = max_disc_int; p2p->max_disc_tu = max_disc_tu; p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d", min_disc_int, max_disc_int, max_disc_tu); return 0; } void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) { va_list ap; char buf[500]; if (!p2p->cfg->debug_print) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = '\0'; va_end(ap); p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf); } void p2p_info(struct p2p_data *p2p, const char *fmt, ...) { va_list ap; char buf[500]; if (!p2p->cfg->debug_print) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = '\0'; va_end(ap); p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf); } void p2p_err(struct p2p_data *p2p, const char *fmt, ...) { va_list ap; char buf[500]; if (!p2p->cfg->debug_print) return; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = '\0'; va_end(ap); p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf); } void p2p_loop_on_known_peers(struct p2p_data *p2p, void (*peer_callback)(struct p2p_peer_info *peer, void *user_data), void *user_data) { struct p2p_device *dev, *n; dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { peer_callback(&dev->info, user_data); } } #ifdef CONFIG_WPS_NFC static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { struct wpabuf *buf; u8 op_class, channel; enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP; buf = wpabuf_alloc(1000); if (buf == NULL) return NULL; op_class = p2p->cfg->reg_class; channel = p2p->cfg->channel; p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (p2p->num_groups > 0) { int freq = p2p_group_get_freq(p2p->groups[0]); role = P2P_GO_IN_A_GROUP; if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) { p2p_dbg(p2p, "Unknown GO operating frequency %d MHz for NFC handover", freq); wpabuf_free(buf); return NULL; } } else if (client_freq > 0) { role = P2P_CLIENT_IN_A_GROUP; if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) { p2p_dbg(p2p, "Unknown client operating frequency %d MHz for NFC handover", client_freq); wpabuf_free(buf); return NULL; } } p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, channel, role); if (p2p->num_groups > 0) { /* Limit number of clients to avoid very long message */ p2p_buf_add_group_info(p2p->groups[0], buf, 5); p2p_group_buf_add_id(p2p->groups[0], buf); } else if (client_freq > 0 && go_dev_addr && !is_zero_ether_addr(go_dev_addr) && ssid && ssid_len > 0) { /* * Add the optional P2P Group ID to indicate in which group this * device is a P2P Client. */ p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len); } return buf; } struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, ssid_len); } struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, ssid_len); } int p2p_process_nfc_connection_handover(struct p2p_data *p2p, struct p2p_nfc_params *params) { struct p2p_message msg; struct p2p_device *dev; const u8 *p2p_dev_addr; int freq; enum p2p_role_indication role; params->next_step = NO_ACTION; if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, params->p2p_attr, params->p2p_len, &msg)) { p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); p2p_parse_free(&msg); return -1; } if (msg.p2p_device_addr) p2p_dev_addr = msg.p2p_device_addr; else if (msg.device_id) p2p_dev_addr = msg.device_id; else { p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (msg.oob_dev_password) { os_memcpy(params->oob_dev_pw, msg.oob_dev_password, msg.oob_dev_password_len); params->oob_dev_pw_len = msg.oob_dev_password_len; } dev = p2p_create_device(p2p, p2p_dev_addr); if (dev == NULL) { p2p_parse_free(&msg); return -1; } params->peer = &dev->info; os_get_reltime(&dev->last_seen); dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); p2p_copy_wps_info(p2p, dev, 0, &msg); if (!msg.oob_go_neg_channel) { p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); p2p_parse_free(&msg); return -1; } if (msg.oob_go_neg_channel[3] == 0 && msg.oob_go_neg_channel[4] == 0) freq = 0; else freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], msg.oob_go_neg_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); p2p_parse_free(&msg); return -1; } role = msg.oob_go_neg_channel[5]; if (role == P2P_GO_IN_A_GROUP) { p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq); params->go_freq = freq; } else if (role == P2P_CLIENT_IN_A_GROUP) { p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz", freq); params->go_freq = freq; } else p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); dev->oob_go_neg_freq = freq; if (!params->sel && role != P2P_GO_IN_A_GROUP) { freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Own listen channel not known"); p2p_parse_free(&msg); return -1; } p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); dev->oob_go_neg_freq = freq; } if (msg.group_id) { os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN); params->go_ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN, params->go_ssid_len); } if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "Do not report rejected device"); p2p_parse_free(&msg); return 0; } if (!(dev->flags & P2P_DEV_REPORTED)) { p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; } p2p_parse_free(&msg); if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0) params->next_step = BOTH_GO; else if (role == P2P_GO_IN_A_GROUP) params->next_step = JOIN_GROUP; else if (role == P2P_CLIENT_IN_A_GROUP) { dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; params->next_step = PEER_CLIENT; } else if (p2p->num_groups > 0) params->next_step = AUTH_JOIN; else if (params->sel) params->next_step = INIT_GO_NEG; else params->next_step = RESP_GO_NEG; return 0; } void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, int go_intent, const u8 *own_interface_addr) { p2p->authorized_oob_dev_pw_id = dev_pw_id; if (dev_pw_id == 0) { p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover"); return; } p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover", dev_pw_id); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); } #endif /* CONFIG_WPS_NFC */ int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len) { if (len < 8 || len > 63) return -1; p2p->cfg->passphrase_len = len; return 0; } void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem) { p2p->vendor_elem = vendor_elem; } void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, -1); } void p2p_set_own_pref_freq_list(struct p2p_data *p2p, const unsigned int *pref_freq_list, unsigned int size) { unsigned int i; if (size > P2P_MAX_PREF_CHANNELS) size = P2P_MAX_PREF_CHANNELS; p2p->num_pref_freq = size; for (i = 0; i < size; i++) { p2p->pref_freq_list[i] = pref_freq_list[i]; p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", i, p2p->pref_freq_list[i]); } } void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, u8 chan) { p2p->override_pref_op_class = op_class; p2p->override_pref_channel = chan; } struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, unsigned int freq) { struct wpabuf *ies, *buf; u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; int ret; ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (!ies) { wpa_printf(MSG_ERROR, "CTRL: Failed to build Probe Response IEs"); return NULL; } buf = wpabuf_alloc(200 + wpabuf_len(ies)); if (!buf) { wpabuf_free(ies); return NULL; } ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq); wpabuf_free(ies); if (ret) { wpabuf_free(buf); return NULL; } return buf; } + + +bool p2p_is_peer_6ghz_capab(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (!dev) + return false; + + return !!(dev->info.dev_capab & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE); +} + + +void p2p_set_6ghz_dev_capab(struct p2p_data *p2p, bool allow_6ghz) +{ + p2p->p2p_6ghz_capable = allow_6ghz; + p2p->allow_6ghz = allow_6ghz; + p2p_dbg(p2p, "Set 6 GHz capability to %d", allow_6ghz); + + if (allow_6ghz) + p2p->dev_capab |= P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE; + else + p2p->dev_capab &= ~P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE; +} + + +bool is_p2p_6ghz_capable(struct p2p_data *p2p) +{ + return p2p->p2p_6ghz_capable; +} + + +bool p2p_wfd_enabled(struct p2p_data *p2p) +{ +#ifdef CONFIG_WIFI_DISPLAY + return p2p->wfd_ie_probe_req != NULL; +#else /* CONFIG_WIFI_DISPLAY */ + return false; +#endif /* CONFIG_WIFI_DISPLAY */ +} + + +bool p2p_peer_wfd_enabled(struct p2p_data *p2p, const u8 *peer_addr) +{ +#ifdef CONFIG_WIFI_DISPLAY + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + return dev && dev->info.wfd_subelems != NULL; +#else /* CONFIG_WIFI_DISPLAY */ + return false; +#endif /* CONFIG_WIFI_DISPLAY */ +} + + +bool is_p2p_allow_6ghz(struct p2p_data *p2p) +{ + return p2p->allow_6ghz; +} + + +void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value) +{ + p2p->allow_6ghz = value; +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 762bd40bea8b..f606fbb14a81 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -1,2414 +1,2427 @@ /* * Wi-Fi Direct - P2P module * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef P2P_H #define P2P_H #include "common/ieee802_11_defs.h" #include "wps/wps.h" /* P2P ASP Setup Capability */ #define P2PS_SETUP_NONE 0 #define P2PS_SETUP_NEW BIT(0) #define P2PS_SETUP_CLIENT BIT(1) #define P2PS_SETUP_GROUP_OWNER BIT(2) #define P2PS_WILD_HASH_STR "org.wi-fi.wfds" #define P2PS_HASH_LEN 6 #define P2P_MAX_QUERY_HASH 6 #define P2PS_FEATURE_CAPAB_CPT_MAX 2 /** * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels */ #define P2P_MAX_PREF_CHANNELS 100 /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ #define P2P_MAX_REG_CLASSES 15 /** * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class */ #define P2P_MAX_REG_CLASS_CHANNELS 60 /** * struct p2p_channels - List of supported channels */ struct p2p_channels { /** * struct p2p_reg_class - Supported regulatory class */ struct p2p_reg_class { /** * reg_class - Regulatory class (IEEE 802.11-2007, Annex J) */ u8 reg_class; /** * channel - Supported channels */ u8 channel[P2P_MAX_REG_CLASS_CHANNELS]; /** * channels - Number of channel entries in use */ size_t channels; } reg_class[P2P_MAX_REG_CLASSES]; /** * reg_classes - Number of reg_class entries in use */ size_t reg_classes; }; enum p2p_wps_method { WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC, WPS_P2PS }; /** * struct p2p_go_neg_results - P2P Group Owner Negotiation results */ struct p2p_go_neg_results { /** * status - Negotiation result (Status Code) * * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate * failed negotiation. */ int status; /** * role_go - Whether local end is Group Owner */ int role_go; /** * freq - Frequency of the group operational channel in MHz */ int freq; int ht40; int vht; int edmg; u8 max_oper_chwidth; unsigned int vht_center_freq2; /** * he - Indicates if IEEE 802.11ax HE is enabled */ int he; /** * ssid - SSID of the group */ u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID in octets */ size_t ssid_len; /** * psk - WPA pre-shared key (256 bits) (GO only) */ u8 psk[32]; /** * psk_set - Whether PSK field is configured (GO only) */ int psk_set; /** * passphrase - WPA2-Personal passphrase for the group (GO only) */ char passphrase[64]; /** * peer_device_addr - P2P Device Address of the peer */ u8 peer_device_addr[ETH_ALEN]; /** * peer_interface_addr - P2P Interface Address of the peer */ u8 peer_interface_addr[ETH_ALEN]; /** * wps_method - WPS method to be used during provisioning */ enum p2p_wps_method wps_method; #define P2P_MAX_CHANNELS 50 /** * freq_list - Zero-terminated list of possible operational channels */ int freq_list[P2P_MAX_CHANNELS]; /** * persistent_group - Whether the group should be made persistent * 0 = not persistent * 1 = persistent group without persistent reconnect * 2 = persistent group with persistent reconnect */ int persistent_group; /** * peer_config_timeout - Peer configuration timeout (in 10 msec units) */ unsigned int peer_config_timeout; }; struct p2ps_provision { /** * pd_seeker - P2PS provision discovery seeker role */ unsigned int pd_seeker:1; /** * status - Remote returned provisioning status code */ int status; /** * adv_id - P2PS Advertisement ID */ u32 adv_id; /** * session_id - P2PS Session ID */ u32 session_id; /** * method - WPS Method (to be) used to establish session */ u16 method; /** * conncap - Connection Capabilities negotiated between P2P peers */ u8 conncap; /** * role - Info about the roles to be used for this connection */ u8 role; /** * session_mac - MAC address of the peer that started the session */ u8 session_mac[ETH_ALEN]; /** * adv_mac - MAC address of the peer advertised the service */ u8 adv_mac[ETH_ALEN]; /** * cpt_mask - Supported Coordination Protocol Transport mask * * A bitwise mask of supported ASP Coordination Protocol Transports. * This property is set together and corresponds with cpt_priority. */ u8 cpt_mask; /** * cpt_priority - Coordination Protocol Transport priority list * * Priorities of supported ASP Coordination Protocol Transports. * This property is set together and corresponds with cpt_mask. * The CPT priority list is 0 terminated. */ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; /** * force_freq - The only allowed channel frequency in MHz or 0. */ unsigned int force_freq; /** * pref_freq - Preferred operating frequency in MHz or 0. */ unsigned int pref_freq; /** * info - Vendor defined extra Provisioning information */ char info[0]; }; struct p2ps_advertisement { struct p2ps_advertisement *next; /** * svc_info - Pointer to (internal) Service defined information */ char *svc_info; /** * id - P2PS Advertisement ID */ u32 id; /** * config_methods - WPS Methods which are allowed for this service */ u16 config_methods; /** * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined */ u8 state; /** * auto_accept - Automatically Accept provisioning request if possible. */ u8 auto_accept; /** * hash - 6 octet Service Name has to match against incoming Probe Requests */ u8 hash[P2PS_HASH_LEN]; /** * cpt_mask - supported Coordination Protocol Transport mask * * A bitwise mask of supported ASP Coordination Protocol Transports. * This property is set together and corresponds with cpt_priority. */ u8 cpt_mask; /** * cpt_priority - Coordination Protocol Transport priority list * * Priorities of supported ASP Coordinatin Protocol Transports. * This property is set together and corresponds with cpt_mask. * The CPT priority list is 0 terminated. */ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; /** * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage */ char svc_name[0]; }; struct p2p_data; enum p2p_scan_type { P2P_SCAN_SOCIAL, P2P_SCAN_FULL, P2P_SCAN_SPECIFIC, P2P_SCAN_SOCIAL_PLUS_ONE }; #define P2P_MAX_WPS_VENDOR_EXT 10 /** * struct p2p_peer_info - P2P peer information */ struct p2p_peer_info { /** * p2p_device_addr - P2P Device Address of the peer */ u8 p2p_device_addr[ETH_ALEN]; /** * pri_dev_type - Primary Device Type */ u8 pri_dev_type[8]; /** * device_name - Device Name (0..32 octets encoded in UTF-8) */ char device_name[WPS_DEV_NAME_MAX_LEN + 1]; /** * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) */ char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1]; /** * model_name - Model Name (0..32 octets encoded in UTF-8) */ char model_name[WPS_MODEL_NAME_MAX_LEN + 1]; /** * model_number - Model Number (0..32 octets encoded in UTF-8) */ char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1]; /** * serial_number - Serial Number (0..32 octets encoded in UTF-8) */ char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1]; /** * level - Signal level */ int level; /** * config_methods - WPS Configuration Methods */ u16 config_methods; /** * dev_capab - Device Capabilities */ u8 dev_capab; /** * group_capab - Group Capabilities */ u8 group_capab; /** * wps_sec_dev_type_list - WPS secondary device type list * * This list includes from 0 to 16 Secondary Device Types as indicated * by wps_sec_dev_type_list_len (8 * number of types). */ u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN]; /** * wps_sec_dev_type_list_len - Length of secondary device type list */ size_t wps_sec_dev_type_list_len; struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; /** * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) */ struct wpabuf *wfd_subelems; /** * vendor_elems - Unrecognized vendor elements * * This buffer includes any other vendor element than P2P, WPS, and WFD * IE(s) from the frame that was used to discover the peer. */ struct wpabuf *vendor_elems; /** * p2ps_instance - P2PS Application Service Info */ struct wpabuf *p2ps_instance; }; enum p2p_prov_disc_status { P2P_PROV_DISC_SUCCESS, P2P_PROV_DISC_TIMEOUT, P2P_PROV_DISC_REJECTED, P2P_PROV_DISC_TIMEOUT_JOIN, P2P_PROV_DISC_INFO_UNAVAILABLE, }; struct p2p_channel { u8 op_class; u8 chan; }; /** * struct p2p_config - P2P configuration * * This configuration is provided to the P2P module during initialization with * p2p_init(). */ struct p2p_config { /** * country - Country code to use in P2P operations */ char country[3]; /** * reg_class - Regulatory class for own listen channel */ u8 reg_class; /** * channel - Own listen channel */ u8 channel; /** * channel_forced - the listen channel was forced by configuration * or by control interface and cannot be overridden */ u8 channel_forced; /** * Regulatory class for own operational channel */ u8 op_reg_class; /** * op_channel - Own operational channel */ u8 op_channel; /** * cfg_op_channel - Whether op_channel is hardcoded in configuration */ u8 cfg_op_channel; /** * channels - Own supported regulatory classes and channels * * List of supposerted channels per regulatory class. The regulatory * classes are defined in IEEE Std 802.11-2007 Annex J and the * numbering of the clases depends on the configured country code. */ struct p2p_channels channels; /** * cli_channels - Additional client channels * * This list of channels (if any) will be used when advertising local * channels during GO Negotiation or Invitation for the cases where the * local end may become the client. This may allow the peer to become a * GO on additional channels if it supports these options. The main use * case for this is to include passive-scan channels on devices that may * not know their current location and have configured most channels to * not allow initiation of radition (i.e., another device needs to take * master responsibilities). */ struct p2p_channels cli_channels; /** * num_pref_chan - Number of pref_chan entries */ unsigned int num_pref_chan; /** * pref_chan - Preferred channels for GO Negotiation */ struct p2p_channel *pref_chan; /** * p2p_6ghz_disable - Disable 6GHz for P2P operations */ bool p2p_6ghz_disable; /** * pri_dev_type - Primary Device Type (see WPS) */ u8 pri_dev_type[8]; /** * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types */ #define P2P_SEC_DEVICE_TYPES 5 /** * sec_dev_type - Optional secondary device types */ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8]; /** * num_sec_dev_types - Number of sec_dev_type entries */ size_t num_sec_dev_types; /** * dev_addr - P2P Device Address */ u8 dev_addr[ETH_ALEN]; /** * dev_name - Device Name */ char *dev_name; char *manufacturer; char *model_name; char *model_number; char *serial_number; u8 uuid[16]; u16 config_methods; /** * concurrent_operations - Whether concurrent operations are supported */ int concurrent_operations; /** * max_peers - Maximum number of discovered peers to remember * * If more peers are discovered, older entries will be removed to make * room for the new ones. */ size_t max_peers; /** * p2p_intra_bss - Intra BSS communication is supported */ int p2p_intra_bss; /** * ssid_postfix - Postfix data to add to the SSID * * This data will be added to the end of the SSID after the * DIRECT- prefix. */ u8 ssid_postfix[SSID_MAX_LEN - 9]; /** * ssid_postfix_len - Length of the ssid_postfix data */ size_t ssid_postfix_len; /** * max_listen - Maximum listen duration in ms */ unsigned int max_listen; /** * passphrase_len - Passphrase length (8..63) * * This parameter controls the length of the random passphrase that is * generated at the GO. */ unsigned int passphrase_len; /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; /** * debug_print - Debug print * @ctx: Callback context from cb_ctx * @level: Debug verbosity level (MSG_*) * @msg: Debug message */ void (*debug_print)(void *ctx, int level, const char *msg); /* Callbacks to request lower layer driver operations */ /** * p2p_scan - Request a P2P scan/search * @ctx: Callback context from cb_ctx * @type: Scan type * @freq: Specific frequency (MHz) to scan or 0 for no restriction * @num_req_dev_types: Number of requested device types * @req_dev_types: Array containing requested device types * @dev_id: Device ID to search for or %NULL to find all devices * @pw_id: Device Password ID + * @include_6ghz: Include 6 GHz channels in P2P scan * Returns: 0 on success, -1 on failure * * This callback function is used to request a P2P scan or search * operation to be completed. Type type argument specifies which type * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC * request a scan of a single channel specified by freq. * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels * plus one extra channel specified by freq. * * The full scan is used for the initial scan to find group owners from * all. The other types are used during search phase scan of the social * channels (with potential variation if the Listen channel of the * target peer is known or if other channels are scanned in steps). * * The scan results are returned after this call by calling * p2p_scan_res_handler() for each scan result that has a P2P IE and * then calling p2p_scan_res_handled() to indicate that all scan * results have been indicated. */ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id, u16 pw_id); + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id, + bool include_6ghz); /** * send_probe_resp - Transmit a Probe Response frame * @ctx: Callback context from cb_ctx * @buf: Probe Response frame (including the header and body) * @freq: Forced frequency (in MHz) to use or 0. * Returns: 0 on success, -1 on failure * * This function is used to reply to Probe Request frames that were * indicated with a call to p2p_probe_req_rx(). The response is to be * sent on the same channel, unless otherwise specified, or to be * dropped if the driver is not listening to Probe Request frames * anymore. * * Alternatively, the responsibility for building the Probe Response * frames in Listen state may be in another system component in which * case this function need to be implemented (i.e., the function * pointer can be %NULL). The WPS and P2P IEs to be added for Probe * Response frames in such a case are available from the * start_listen() callback. It should be noted that the received Probe * Request frames must be indicated by calling p2p_probe_req_rx() even * if this send_probe_resp() is not used. */ int (*send_probe_resp)(void *ctx, const struct wpabuf *buf, unsigned int freq); /** * send_action - Transmit an Action frame * @ctx: Callback context from cb_ctx * @freq: Frequency in MHz for the channel on which to transmit * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @buf: Frame body (starting from Category field) * @len: Length of buf in octets * @wait_time: How many msec to wait for a response frame * @scheduled: Return value indicating whether the transmissions was * scheduled to happen once the radio is available * Returns: 0 on success, -1 on failure * * The Action frame may not be transmitted immediately and the status * of the transmission must be reported by calling * p2p_send_action_cb() once the frame has either been transmitted or * it has been dropped due to excessive retries or other failure to * transmit. */ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time, int *scheduled); /** * send_action_done - Notify that Action frame sequence was completed * @ctx: Callback context from cb_ctx * * This function is called when the Action frame sequence that was * started with send_action() has been completed, i.e., when there is * no need to wait for a response from the destination peer anymore. */ void (*send_action_done)(void *ctx); /** * start_listen - Start Listen state * @ctx: Callback context from cb_ctx * @freq: Frequency of the listen channel in MHz * @duration: Duration for the Listen state in milliseconds * @probe_resp_ie: IE(s) to be added to Probe Response frames * Returns: 0 on success, -1 on failure * * This Listen state may not start immediately since the driver may * have other pending operations to complete first. Once the Listen * state has started, p2p_listen_cb() must be called to notify the P2P * module. Once the Listen state is stopped, p2p_listen_end() must be * called to notify the P2P module that the driver is not in the Listen * state anymore. * * If the send_probe_resp() is not used for generating the response, * the IEs from probe_resp_ie need to be added to the end of the Probe * Response frame body. If send_probe_resp() is used, the probe_resp_ie * information can be ignored. */ int (*start_listen)(void *ctx, unsigned int freq, unsigned int duration, const struct wpabuf *probe_resp_ie); /** * stop_listen - Stop Listen state * @ctx: Callback context from cb_ctx * * This callback can be used to stop a Listen state operation that was * previously requested with start_listen(). */ void (*stop_listen)(void *ctx); /** * get_noa - Get current Notice of Absence attribute payload * @ctx: Callback context from cb_ctx * @interface_addr: P2P Interface Address of the GO * @buf: Buffer for returning NoA * @buf_len: Buffer length in octets * Returns: Number of octets used in buf, 0 to indicate no NoA is being * advertized, or -1 on failure * * This function is used to fetch the current Notice of Absence * attribute value from GO. */ int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len); /* Callbacks to notify events to upper layer management entity */ /** * dev_found - Notification of a found P2P Device * @ctx: Callback context from cb_ctx * @addr: Source address of the message triggering this notification * @info: P2P peer information * @new_device: Inform if the peer is newly found * * This callback is used to notify that a new P2P Device has been * found. This may happen, e.g., during Search state based on scan * results or during Listen state based on receive Probe Request and * Group Owner Negotiation Request. */ void (*dev_found)(void *ctx, const u8 *addr, const struct p2p_peer_info *info, int new_device); /** * dev_lost - Notification of a lost P2P Device * @ctx: Callback context from cb_ctx * @dev_addr: P2P Device Address of the lost P2P Device * * This callback is used to notify that a P2P Device has been deleted. */ void (*dev_lost)(void *ctx, const u8 *dev_addr); /** * find_stopped - Notification of a p2p_find operation stopping * @ctx: Callback context from cb_ctx */ void (*find_stopped)(void *ctx); /** * go_neg_req_rx - Notification of a receive GO Negotiation Request * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification * @dev_passwd_id: WPS Device Password ID * @go_intent: Peer's GO Intent * * This callback is used to notify that a P2P Device is requesting * group owner negotiation with us, but we do not have all the * necessary information to start GO Negotiation. This indicates that * the local user has not authorized the connection yet by providing a * PIN or PBC button press. This information can be provided with a * call to p2p_connect(). */ void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id, u8 go_intent); /** * go_neg_completed - Notification of GO Negotiation results * @ctx: Callback context from cb_ctx * @res: GO Negotiation results * * This callback is used to notify that Group Owner Negotiation has * been completed. Non-zero struct p2p_go_neg_results::status indicates * failed negotiation. In case of success, this function is responsible * for creating a new group interface (or using the existing interface * depending on driver features), setting up the group interface in * proper mode based on struct p2p_go_neg_results::role_go and * initializing WPS provisioning either as a Registrar (if GO) or as an * Enrollee. Successful WPS provisioning must be indicated by calling * p2p_wps_success_cb(). The callee is responsible for timing out group * formation if WPS provisioning cannot be completed successfully * within 15 seconds. */ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); /** * sd_request - Callback on Service Discovery Request * @ctx: Callback context from cb_ctx * @freq: Frequency (in MHz) of the channel * @sa: Source address of the request * @dialog_token: Dialog token * @update_indic: Service Update Indicator from the source of request * @tlvs: P2P Service Request TLV(s) * @tlvs_len: Length of tlvs buffer in octets * * This callback is used to indicate reception of a service discovery * request. Response to the query must be indicated by calling * p2p_sd_response() with the context information from the arguments to * this callback function. * * This callback handler can be set to %NULL to indicate that service * discovery is not supported. */ void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token, u16 update_indic, const u8 *tlvs, size_t tlvs_len); /** * sd_response - Callback on Service Discovery Response * @ctx: Callback context from cb_ctx * @sa: Source address of the request * @update_indic: Service Update Indicator from the source of response * @tlvs: P2P Service Response TLV(s) * @tlvs_len: Length of tlvs buffer in octets * * This callback is used to indicate reception of a service discovery * response. This callback handler can be set to %NULL if no service * discovery requests are used. The information provided with this call * is replies to the queries scheduled with p2p_sd_request(). */ void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic, const u8 *tlvs, size_t tlvs_len); /** * prov_disc_req - Callback on Provisiong Discovery Request * @ctx: Callback context from cb_ctx * @peer: Source address of the request * @config_methods: Requested WPS Config Method * @dev_addr: P2P Device Address of the found P2P Device * @pri_dev_type: Primary Device Type * @dev_name: Device Name * @supp_config_methods: Supported configuration Methods * @dev_capab: Device Capabilities * @group_capab: Group Capabilities * @group_id: P2P Group ID (or %NULL if not included) * @group_id_len: Length of P2P Group ID * * This callback is used to indicate reception of a Provision Discovery * Request frame that the P2P module accepted. */ void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods, const u8 *dev_addr, const u8 *pri_dev_type, const char *dev_name, u16 supp_config_methods, u8 dev_capab, u8 group_capab, const u8 *group_id, size_t group_id_len); /** * prov_disc_resp - Callback on Provisiong Discovery Response * @ctx: Callback context from cb_ctx * @peer: Source address of the response * @config_methods: Value from p2p_prov_disc_req() or 0 on failure * * This callback is used to indicate reception of a Provision Discovery * Response frame for a pending request scheduled with * p2p_prov_disc_req(). This callback handler can be set to %NULL if * provision discovery is not used. */ void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods); /** * prov_disc_fail - Callback on Provision Discovery failure * @ctx: Callback context from cb_ctx * @peer: Source address of the response * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS * @adv_id: If non-zero, then the adv_id of the PD Request * @adv_mac: P2P Device Address of the advertizer * @deferred_session_resp: Deferred session response sent by advertizer * * This callback is used to indicate either a failure or no response * to an earlier provision discovery request. * * This callback handler can be set to %NULL if provision discovery * is not used or failures do not need to be indicated. */ void (*prov_disc_fail)(void *ctx, const u8 *peer, enum p2p_prov_disc_status status, u32 adv_id, const u8 *adv_mac, const char *deferred_session_resp); /** * invitation_process - Optional callback for processing Invitations * @ctx: Callback context from cb_ctx * @sa: Source address of the Invitation Request * @bssid: P2P Group BSSID from the request or %NULL if not included * @go_dev_addr: GO Device Address from P2P Group ID * @ssid: SSID from P2P Group ID * @ssid_len: Length of ssid buffer in octets * @go: Variable for returning whether the local end is GO in the group * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO) * @force_freq: Variable for returning forced frequency for the group * @persistent_group: Whether this is an invitation to reinvoke a * persistent group (instead of invitation to join an active * group) * @channels: Available operating channels for the group * @dev_pw_id: Device Password ID for NFC static handover or -1 if not * used * Returns: Status code (P2P_SC_*) * * This optional callback can be used to implement persistent reconnect * by allowing automatic restarting of persistent groups without user * interaction. If this callback is not implemented (i.e., is %NULL), * the received Invitation Request frames are replied with * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the * invitation_result() callback. * * If the requested parameters are acceptable and the group is known, * %P2P_SC_SUCCESS may be returned. If the requested group is unknown, * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED * can be returned if there is not enough data to provide immediate * response, i.e., if some sort of user interaction is needed. The * invitation_received() callback will be called in that case * immediately after this call. */ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, int *force_freq, int persistent_group, const struct p2p_channels *channels, int dev_pw_id); /** * invitation_received - Callback on Invitation Request RX * @ctx: Callback context from cb_ctx * @sa: Source address of the Invitation Request * @bssid: P2P Group BSSID or %NULL if not received * @ssid: SSID of the group * @ssid_len: Length of ssid in octets * @go_dev_addr: GO Device Address * @status: Response Status * @op_freq: Operational frequency for the group * * This callback is used to indicate sending of an Invitation Response * for a received Invitation Request. If status == 0 (success), the * upper layer code is responsible for starting the group. status == 1 * indicates need to get user authorization for the group. Other status * values indicate that the invitation request was rejected. */ void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *ssid, size_t ssid_len, const u8 *go_dev_addr, u8 status, int op_freq); /** * invitation_result - Callback on Invitation result * @ctx: Callback context from cb_ctx * @status: Negotiation result (Status Code) * @bssid: P2P Group BSSID or %NULL if not received * @channels: Available operating channels for the group * @addr: Peer address * @freq: Frequency (in MHz) indicated during invitation or 0 * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer * during invitation or 0 * * This callback is used to indicate result of an Invitation procedure * started with a call to p2p_invite(). The indicated status code is * the value received from the peer in Invitation Response with 0 * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a * local failure in transmitting the Invitation Request. */ void (*invitation_result)(void *ctx, int status, const u8 *bssid, const struct p2p_channels *channels, const u8 *addr, int freq, int peer_oper_freq); /** * go_connected - Check whether we are connected to a GO * @ctx: Callback context from cb_ctx * @dev_addr: P2P Device Address of a GO * Returns: 1 if we are connected as a P2P client to the specified GO * or 0 if not. */ int (*go_connected)(void *ctx, const u8 *dev_addr); /** * presence_resp - Callback on Presence Response * @ctx: Callback context from cb_ctx * @src: Source address (GO's P2P Interface Address) * @status: Result of the request (P2P_SC_*) * @noa: Returned NoA value * @noa_len: Length of the NoA buffer in octets */ void (*presence_resp)(void *ctx, const u8 *src, u8 status, const u8 *noa, size_t noa_len); /** * is_concurrent_session_active - Check whether concurrent session is * active on other virtual interfaces * @ctx: Callback context from cb_ctx * Returns: 1 if concurrent session is active on other virtual interface * or 0 if not. */ int (*is_concurrent_session_active)(void *ctx); /** * is_p2p_in_progress - Check whether P2P operation is in progress * @ctx: Callback context from cb_ctx * Returns: 1 if P2P operation (e.g., group formation) is in progress * or 0 if not. */ int (*is_p2p_in_progress)(void *ctx); /** * Determine if we have a persistent group we share with remote peer * and allocate interface for this group if needed * @ctx: Callback context from cb_ctx * @addr: Peer device address to search for * @ssid: Persistent group SSID or %NULL if any * @ssid_len: Length of @ssid * @go_dev_addr: Buffer for returning GO P2P Device Address * @ret_ssid: Buffer for returning group SSID * @ret_ssid_len: Buffer for returning length of @ssid * @intended_iface_addr: Buffer for returning intended iface address * Returns: 1 if a matching persistent group was found, 0 otherwise */ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, u8 *ret_ssid, size_t *ret_ssid_len, u8 *intended_iface_addr); /** * Get information about a possible local GO role * @ctx: Callback context from cb_ctx * @intended_addr: Buffer for returning intended GO interface address * @ssid: Buffer for returning group SSID * @ssid_len: Buffer for returning length of @ssid * @group_iface: Buffer for returning whether a separate group interface * would be used * @freq: Variable for returning the current operating frequency of a * currently running P2P GO. * Returns: 1 if GO info found, 0 otherwise * * This is used to compose New Group settings (SSID, and intended * address) during P2PS provisioning if results of provisioning *might* * result in our being an autonomous GO. */ int (*get_go_info)(void *ctx, u8 *intended_addr, u8 *ssid, size_t *ssid_len, int *group_iface, unsigned int *freq); /** * remove_stale_groups - Remove stale P2PS groups * * Because P2PS stages *potential* GOs, and remote devices can remove * credentials unilaterally, we need to make sure we don't let stale * unusable groups build up. */ int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go, const u8 *ssid, size_t ssid_len); /** * p2ps_prov_complete - P2PS provisioning complete * * When P2PS provisioning completes (successfully or not) we must * transmit all of the results to the upper layers. */ void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev, const u8 *adv_mac, const u8 *ses_mac, const u8 *grp_mac, u32 adv_id, u32 ses_id, u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, const u8 *feat_cap, size_t feat_cap_len, unsigned int freq, const u8 *group_ssid, size_t group_ssid_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response * @ctx: Callback context from cb_ctx * Returns: 1 if operation was started, 0 otherwise * * This callback can be used to perform any pending actions after * provisioning. It is mainly used for P2PS pending group creation. */ int (*prov_disc_resp_cb)(void *ctx); /** * p2ps_group_capability - Determine group capability * @ctx: Callback context from cb_ctx * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap. * @role: Local roles, expressed with P2PS_SETUP_* bitmap. * @force_freq: Variable for returning forced frequency for the group. * @pref_freq: Variable for returning preferred frequency for the group. * Returns: P2PS_SETUP_* bitmap of group capability result. * * This function can be used to determine group capability and * frequencies based on information from P2PS PD exchange and the * current state of ongoing groups and driver capabilities. */ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role, unsigned int *force_freq, unsigned int *pref_freq); /** * get_pref_freq_list - Get preferred frequency list for an interface * @ctx: Callback context from cb_ctx * @go: Whether the use if for GO role * @len: Length of freq_list in entries (both IN and OUT) * @freq_list: Buffer for returning the preferred frequencies (MHz) * Returns: 0 on success, -1 on failure * * This function can be used to query the preferred frequency list from * the driver specific to a particular interface type. */ int (*get_pref_freq_list)(void *ctx, int go, unsigned int *len, unsigned int *freq_list); }; /* P2P module initialization/deinitialization */ /** * p2p_init - Initialize P2P module * @cfg: P2P module configuration * Returns: Pointer to private data or %NULL on failure * * This function is used to initialize global P2P module context (one per * device). The P2P module will keep a copy of the configuration data, so the * caller does not need to maintain this structure. However, the callback * functions and the context parameters to them must be kept available until * the P2P module is deinitialized with p2p_deinit(). */ struct p2p_data * p2p_init(const struct p2p_config *cfg); /** * p2p_deinit - Deinitialize P2P module * @p2p: P2P module context from p2p_init() */ void p2p_deinit(struct p2p_data *p2p); /** * p2p_flush - Flush P2P module state * @p2p: P2P module context from p2p_init() * * This command removes the P2P module state like peer device entries. */ void p2p_flush(struct p2p_data *p2p); /** * p2p_unauthorize - Unauthorize the specified peer device * @p2p: P2P module context from p2p_init() * @addr: P2P peer entry to be unauthorized * Returns: 0 on success, -1 on failure * * This command removes any connection authorization from the specified P2P * peer device address. This can be used, e.g., to cancel effect of a previous * p2p_authorize() or p2p_connect() call that has not yet resulted in completed * GO Negotiation. */ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr); /** * p2p_set_dev_name - Set device name * @p2p: P2P module context from p2p_init() * Returns: 0 on success, -1 on failure * * This function can be used to update the P2P module configuration with * information that was not available at the time of the p2p_init() call. */ int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name); int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer); int p2p_set_model_name(struct p2p_data *p2p, const char *model_name); int p2p_set_model_number(struct p2p_data *p2p, const char *model_number); int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number); void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods); void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid); /** * p2p_set_pri_dev_type - Set primary device type * @p2p: P2P module context from p2p_init() * Returns: 0 on success, -1 on failure * * This function can be used to update the P2P module configuration with * information that was not available at the time of the p2p_init() call. */ int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type); /** * p2p_set_sec_dev_types - Set secondary device types * @p2p: P2P module context from p2p_init() * Returns: 0 on success, -1 on failure * * This function can be used to update the P2P module configuration with * information that was not available at the time of the p2p_init() call. */ int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], size_t num_dev_types); int p2p_set_country(struct p2p_data *p2p, const char *country); /* Commands from upper layer management entity */ enum p2p_discovery_type { P2P_FIND_START_WITH_FULL, P2P_FIND_ONLY_SOCIAL, P2P_FIND_PROGRESSIVE }; /** * p2p_find - Start P2P Find (Device Discovery) * @p2p: P2P module context from p2p_init() * @timeout: Timeout for find operation in seconds or 0 for no timeout * @type: Device Discovery type * @num_req_dev_types: Number of requested device types * @req_dev_types: Requested device types array, must be an array * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no * requested device types. * @dev_id: Device ID to search for or %NULL to find all devices * @search_delay: Extra delay in milliseconds between search iterations * @seek_count: Number of ASP Service Strings in the seek_string array * @seek_string: ASP Service Strings to query for in Probe Requests * @freq: Requested first scan frequency (in MHz) to modify type == * P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan. * If p2p_find is already in progress, this parameter is ignored and full * scan will be executed. + * @include_6ghz: Include 6 GHz channels in P2P find * Returns: 0 on success, -1 on failure */ int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, unsigned int search_delay, - u8 seek_count, const char **seek_string, int freq); + u8 seek_count, const char **seek_string, int freq, + bool include_6ghz); /** * p2p_notify_scan_trigger_status - Indicate scan trigger status * @p2p: P2P module context from p2p_init() * @status: 0 on success, -1 on failure */ void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status); /** * p2p_stop_find - Stop P2P Find (Device Discovery) * @p2p: P2P module context from p2p_init() */ void p2p_stop_find(struct p2p_data *p2p); /** * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq * @p2p: P2P module context from p2p_init() * @freq: Frequency in MHz for next operation * * This is like p2p_stop_find(), but Listen state is not stopped if we are * already on the same frequency. */ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); /** * p2p_listen - Start P2P Listen state for specified duration * @p2p: P2P module context from p2p_init() * @timeout: Listen state duration in milliseconds * Returns: 0 on success, -1 on failure * * This function can be used to request the P2P module to keep the device * discoverable on the listen channel for an extended set of time. At least in * its current form, this is mainly used for testing purposes and may not be of * much use for normal P2P operations. */ int p2p_listen(struct p2p_data *p2p, unsigned int timeout); /** * p2p_stop_listen - Stop P2P Listen * @p2p: P2P module context from p2p_init() */ void p2p_stop_listen(struct p2p_data *p2p); /** * p2p_connect - Start P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * @wps_method: WPS method to be used in provisioning * @go_intent: Local GO intent value (1..15) * @own_interface_addr: Intended interface address to use with the group * @force_freq: The only allowed channel frequency in MHz or 0 * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate * a new SSID * @force_ssid_len: Length of $force_ssid buffer * @pd_before_go_neg: Whether to send Provision Discovery prior to GO * Negotiation as an interoperability workaround when initiating group * formation * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) * Returns: 0 on success, -1 on failure */ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * @wps_method: WPS method to be used in provisioning * @go_intent: Local GO intent value (1..15) * @own_interface_addr: Intended interface address to use with the group * @force_freq: The only allowed channel frequency in MHz or 0 * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate * a new SSID * @force_ssid_len: Length of $force_ssid buffer * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) * Returns: 0 on success, -1 on failure * * This is like p2p_connect(), but the actual group negotiation is not * initiated automatically, i.e., the other end is expected to do that. */ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_reject - Reject peer device (explicitly block connection attempts) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * Returns: 0 on success, -1 on failure */ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); /** * p2p_prov_disc_req - Send Provision Discovery Request * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client * @p2ps_prov: Provisioning info for P2PS * @config_methods: WPS Config Methods value (only one bit set) * @join: Whether this is used by a client joining an active group * @force_freq: Forced TX frequency for the frame (mainly for the join case) * @user_initiated_pd: Flag to indicate if initiated by user or not * Returns: 0 on success, -1 on failure * * This function can be used to request a discovered P2P peer to display a PIN * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame * is transmitted once immediately and if no response is received, the frame * will be sent again whenever the target device is discovered during device * dsicovery (start with a p2p_find() call). Response from the peer is * indicated with the p2p_config::prov_disc_resp() callback. */ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, struct p2ps_provision *p2ps_prov, u16 config_methods, int join, int force_freq, int user_initiated_pd); /** * p2p_sd_request - Schedule a service discovery query * @p2p: P2P module context from p2p_init() * @dst: Destination peer or %NULL to apply for all peers * @tlvs: P2P Service Query TLV(s) * Returns: Reference to the query or %NULL on failure * * Response to the query is indicated with the p2p_config::sd_response() * callback. */ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs); #ifdef CONFIG_WIFI_DISPLAY void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs); #endif /* CONFIG_WIFI_DISPLAY */ /** * p2p_sd_cancel_request - Cancel a pending service discovery query * @p2p: P2P module context from p2p_init() * @req: Query reference from p2p_sd_request() * Returns: 0 if request for cancelled; -1 if not found */ int p2p_sd_cancel_request(struct p2p_data *p2p, void *req); /** * p2p_sd_response - Send response to a service discovery query * @p2p: P2P module context from p2p_init() * @freq: Frequency from p2p_config::sd_request() callback * @dst: Destination address from p2p_config::sd_request() callback * @dialog_token: Dialog token from p2p_config::sd_request() callback * @resp_tlvs: P2P Service Response TLV(s) * * This function is called as a response to the request indicated with * p2p_config::sd_request() callback. */ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs); /** * p2p_sd_service_update - Indicate a change in local services * @p2p: P2P module context from p2p_init() * * This function needs to be called whenever there is a change in availability * of the local services. This will increment the Service Update Indicator * value which will be used in SD Request and Response frames. */ void p2p_sd_service_update(struct p2p_data *p2p); enum p2p_invite_role { P2P_INVITE_ROLE_GO, P2P_INVITE_ROLE_ACTIVE_GO, P2P_INVITE_ROLE_CLIENT }; /** * p2p_invite - Invite a P2P Device into a group * @p2p: P2P module context from p2p_init() * @peer: Device Address of the peer P2P Device * @role: Local role in the group * @bssid: Group BSSID or %NULL if not known * @ssid: Group SSID * @ssid_len: Length of ssid in octets * @force_freq: The only allowed channel frequency in MHz or 0 * @go_dev_addr: Forced GO Device Address or %NULL if none * @persistent_group: Whether this is to reinvoke a persistent group * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover * case or -1 if not used * Returns: 0 on success, -1 on failure */ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, int persistent_group, unsigned int pref_freq, int dev_pw_id); /** * p2p_presence_req - Request GO presence * @p2p: P2P module context from p2p_init() * @go_interface_addr: GO P2P Interface Address * @own_interface_addr: Own P2P Interface Address for this group * @freq: Group operating frequence (in MHz) * @duration1: Preferred presence duration in microseconds * @interval1: Preferred presence interval in microseconds * @duration2: Acceptable presence duration in microseconds * @interval2: Acceptable presence interval in microseconds * Returns: 0 on success, -1 on failure * * If both duration and interval values are zero, the parameter pair is not * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0). */ int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, const u8 *own_interface_addr, unsigned int freq, u32 duration1, u32 interval1, u32 duration2, u32 interval2); /** * p2p_ext_listen - Set Extended Listen Timing * @p2p: P2P module context from p2p_init() * @freq: Group operating frequence (in MHz) * @period: Availability period in milliseconds (1-65535; 0 to disable) * @interval: Availability interval in milliseconds (1-65535; 0 to disable) * Returns: 0 on success, -1 on failure * * This function can be used to enable or disable (period = interval = 0) * Extended Listen Timing. When enabled, the P2P Device will become * discoverable (go into Listen State) every @interval milliseconds for at * least @period milliseconds. */ int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, unsigned int interval); /* Event notifications from upper layer management operations */ /** * p2p_wps_success_cb - Report successfully completed WPS provisioning * @p2p: P2P module context from p2p_init() * @mac_addr: Peer address * * This function is used to report successfully completed WPS provisioning * during group formation in both GO/Registrar and client/Enrollee roles. */ void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr); /** * p2p_group_formation_failed - Report failed WPS provisioning * @p2p: P2P module context from p2p_init() * * This function is used to report failed group formation. This can happen * either due to failed WPS provisioning or due to 15 second timeout during * the provisioning phase. */ void p2p_group_formation_failed(struct p2p_data *p2p); /** * p2p_get_provisioning_info - Get any stored provisioning info * @p2p: P2P module context from p2p_init() * @addr: Peer P2P Device Address * Returns: WPS provisioning information (WPS config method) or 0 if no * information is available * * This function is used to retrieve stored WPS provisioning info for the given * peer. */ u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); /** * p2p_clear_provisioning_info - Clear any stored provisioning info * @p2p: P2P module context from p2p_init() * @iface_addr: Peer P2P Device Address * * This function is used to clear stored WPS provisioning info for the given * peer. */ void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); /* Event notifications from lower layer driver operations */ /** * enum p2p_probe_req_status * * @P2P_PREQ_MALFORMED: frame was not well-formed * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P */ enum p2p_probe_req_status { P2P_PREQ_MALFORMED, P2P_PREQ_NOT_LISTEN, P2P_PREQ_NOT_P2P, P2P_PREQ_NOT_PROCESSED, P2P_PREQ_PROCESSED }; /** * p2p_probe_req_rx - Report reception of a Probe Request frame * @p2p: P2P module context from p2p_init() * @addr: Source MAC address * @dst: Destination MAC address if available or %NULL * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets * @rx_freq: Probe Request frame RX frequency * @p2p_lo_started: Whether P2P Listen Offload is started * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, unsigned int rx_freq, int p2p_lo_started); /** * p2p_rx_action - Report received Action frame * @p2p: P2P module context from p2p_init() * @da: Destination address of the received Action frame * @sa: Source address of the received Action frame * @bssid: Address 3 of the received Action frame * @category: Category of the received Action frame * @data: Action frame body after the Category field * @len: Length of the data buffer in octets * @freq: Frequency (in MHz) on which the frame was received */ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq); /** * p2p_scan_res_handler - Indicate a P2P scan results * @p2p: P2P module context from p2p_init() * @bssid: BSSID of the scan result * @freq: Frequency of the channel on which the device was found in MHz * @rx_time: Time when the result was received * @level: Signal level (signal strength of the received Beacon/Probe Response * frame) * @ies: Pointer to IEs from the scan result * @ies_len: Length of the ies buffer * Returns: 0 to continue or 1 to stop scan result indication * * This function is called to indicate a scan result entry with P2P IE from a * scan requested with struct p2p_config::p2p_scan(). This can be called during * the actual scan process (i.e., whenever a new device is found) or as a * sequence of calls after the full scan has been completed. The former option * can result in optimized operations, but may not be supported by all * driver/firmware designs. The ies buffer need to include at least the P2P IE, * but it is recommended to include all IEs received from the device. The * caller does not need to check that the IEs contain a P2P IE before calling * this function since frames will be filtered internally if needed. * * This function will return 1 if it wants to stop scan result iteration (and * scan in general if it is still in progress). This is used to allow faster * start of a pending operation, e.g., to start a pending GO negotiation. */ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len); /** * p2p_scan_res_handled - Indicate end of scan results * @p2p: P2P module context from p2p_init() * @delay: Search delay for next scan in ms * * This function is called to indicate that all P2P scan results from a scan * have been reported with zero or more calls to p2p_scan_res_handler(). This * function must be called as a response to successful * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() * calls stopped iteration. */ void p2p_scan_res_handled(struct p2p_data *p2p, unsigned int delay); enum p2p_send_action_result { P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */, P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ }; /** * p2p_send_action_cb - Notify TX status of an Action frame * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @result: Result of the transmission attempt * * This function is used to indicate the result of an Action frame transmission * that was requested with struct p2p_config::send_action() callback. */ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, enum p2p_send_action_result result); /** * p2p_listen_cb - Indicate the start of a requested Listen state * @p2p: P2P module context from p2p_init() * @freq: Listen channel frequency in MHz * @duration: Duration for the Listen state in milliseconds * * This function is used to indicate that a Listen state requested with * struct p2p_config::start_listen() callback has started. */ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, unsigned int duration); /** * p2p_listen_end - Indicate the end of a requested Listen state * @p2p: P2P module context from p2p_init() * @freq: Listen channel frequency in MHz * Returns: 0 if no operations were started, 1 if an operation was started * * This function is used to indicate that a Listen state requested with * struct p2p_config::start_listen() callback has ended. */ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len); void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len); /* Per-group P2P state for GO */ struct p2p_group; /** * struct p2p_group_config - P2P group configuration * * This configuration is provided to the P2P module during initialization of * the per-group information with p2p_group_init(). */ struct p2p_group_config { /** * persistent_group - Whether the group is persistent * 0 = not a persistent group * 1 = persistent group without persistent reconnect * 2 = persistent group with persistent reconnect */ int persistent_group; /** * interface_addr - P2P Interface Address of the group */ u8 interface_addr[ETH_ALEN]; /** * max_clients - Maximum number of clients in the group */ unsigned int max_clients; /** * ssid - Group SSID */ u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID */ size_t ssid_len; /** * freq - Operating channel of the group */ int freq; /** * ip_addr_alloc - Whether IP address allocation within 4-way handshake * is supported */ int ip_addr_alloc; /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; /** * ie_update - Notification of IE update * @ctx: Callback context from cb_ctx * @beacon_ies: P2P IE for Beacon frames or %NULL if no change * @proberesp_ies: P2P Ie for Probe Response frames * * P2P module uses this callback function to notify whenever the P2P IE * in Beacon or Probe Response frames should be updated based on group * events. * * The callee is responsible for freeing the returned buffer(s) with * wpabuf_free(). */ void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, struct wpabuf *proberesp_ies); /** * idle_update - Notification of changes in group idle state * @ctx: Callback context from cb_ctx * @idle: Whether the group is idle (no associated stations) */ void (*idle_update)(void *ctx, int idle); }; /** * p2p_group_init - Initialize P2P group * @p2p: P2P module context from p2p_init() * @config: P2P group configuration (will be freed by p2p_group_deinit()) * Returns: Pointer to private data or %NULL on failure * * This function is used to initialize per-group P2P module context. Currently, * this is only used to manage GO functionality and P2P clients do not need to * create an instance of this per-group information. */ struct p2p_group * p2p_group_init(struct p2p_data *p2p, struct p2p_group_config *config); /** * p2p_group_deinit - Deinitialize P2P group * @group: P2P group context from p2p_group_init() */ void p2p_group_deinit(struct p2p_group *group); /** * p2p_group_notif_assoc - Notification of P2P client association with GO * @group: P2P group context from p2p_group_init() * @addr: Interface address of the P2P client * @ie: IEs from the (Re)association Request frame * @len: Length of the ie buffer in octets * Returns: 0 on success, -1 on failure */ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, const u8 *ie, size_t len); /** * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response * @group: P2P group context from p2p_group_init() * @status: Status value (P2P_SC_SUCCESS if association succeeded) * Returns: P2P IE for (Re)association Response or %NULL on failure * * The caller is responsible for freeing the returned buffer with * wpabuf_free(). */ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status); /** * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO * @group: P2P group context from p2p_group_init() * @addr: Interface address of the P2P client */ void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr); /** * p2p_group_notif_formation_done - Notification of completed group formation * @group: P2P group context from p2p_group_init() */ void p2p_group_notif_formation_done(struct p2p_group *group); /** * p2p_group_notif_noa - Notification of NoA change * @group: P2P group context from p2p_group_init() * @noa: Notice of Absence attribute payload, %NULL if none * @noa_len: Length of noa buffer in octets * Returns: 0 on success, -1 on failure * * Notify the P2P group management about a new NoA contents. This will be * inserted into the P2P IEs in Beacon and Probe Response frames with rest of * the group information. */ int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, size_t noa_len); /** * p2p_group_match_dev_type - Match device types in group with requested type * @group: P2P group context from p2p_group_init() * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) * Returns: 1 on match, 0 on mismatch * * This function can be used to match the Requested Device Type attribute in * WPS IE with the device types of a group member for deciding whether a GO * should reply to a Probe Request frame. Match will be reported if the WPS IE * is not requested any specific device type. */ int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps); /** * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id */ int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p); /** * p2p_group_go_discover - Send GO Discoverability Request to a group client * @group: P2P group context from p2p_group_init() * Returns: 0 on success (frame scheduled); -1 if client was not found */ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, const u8 *searching_dev, int rx_freq); /* Generic helper functions */ /** * p2p_ie_text - Build text format description of P2P IE * @p2p_ie: P2P IE * @buf: Buffer for returning text * @end: Pointer to the end of the buf area * Returns: Number of octets written to the buffer or -1 on failure * * This function can be used to parse P2P IE contents into text format * field=value lines. */ int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); /** * p2p_scan_result_text - Build text format description of P2P IE * @ies: Information elements from scan results * @ies_len: ies buffer length in octets * @buf: Buffer for returning text * @end: Pointer to the end of the buf area * Returns: Number of octets written to the buffer or -1 on failure * * This function can be used to parse P2P IE contents into text format * field=value lines. */ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); /** * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated * P2P IE * @p2p_ie: P2P IE * @dev_addr: Buffer for returning P2P Device Address * Returns: 0 on success or -1 if P2P Device Address could not be parsed */ int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); /** * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) * @ies: Information elements from scan results * @ies_len: ies buffer length in octets * @dev_addr: Buffer for returning P2P Device Address * Returns: 0 on success or -1 if P2P Device Address could not be parsed */ int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr); /** * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame * @p2p: P2P module context from p2p_init() * @bssid: BSSID * @buf: Buffer for writing the P2P IE * @len: Maximum buf length in octets * @p2p_group: Whether this is for association with a P2P GO * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none * Returns: Number of octets written into buf or -1 on failure */ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, size_t len, int p2p_group, struct wpabuf *p2p_ie); /** * p2p_scan_ie - Build P2P IE for Probe Request * @p2p: P2P module context from p2p_init() * @ies: Buffer for writing P2P IE * @dev_id: Device ID to search for or %NULL for any * @bands: Frequency bands used in the scan (enum wpa_radio_work_band bitmap) */ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, unsigned int bands); /** * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie * @p2p: P2P module context from p2p_init() * Returns: Number of octets that p2p_scan_ie() may add to the buffer */ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); /** * p2p_go_params - Generate random P2P group parameters * @p2p: P2P module context from p2p_init() * @params: Buffer for parameters * Returns: 0 on success, -1 on failure */ int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); /** * p2p_get_group_capab - Get Group Capability from P2P IE data * @p2p_ie: P2P IE(s) contents * Returns: Group Capability */ u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); /** * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection * @p2p_ie: P2P IE(s) contents * Returns: 0 if cross connection is allow, 1 if not */ int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); /** * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data * @p2p_ie: P2P IE(s) contents * Returns: Pointer to P2P Device Address or %NULL if not included */ const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie); /** * p2p_get_peer_info - Get P2P peer information * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer or %NULL to indicate the first peer * @next: Whether to select the peer entry following the one indicated by addr * Returns: Pointer to peer info or %NULL if not found */ const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next); /** * p2p_get_peer_info_txt - Get internal P2P peer information in text format * @info: Pointer to P2P peer info from p2p_get_peer_info() * @buf: Buffer for returning text * @buflen: Maximum buffer length * Returns: Number of octets written to the buffer or -1 on failure * * Note: This information is internal to the P2P module and subject to change. * As such, this should not really be used by external programs for purposes * other than debugging. */ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, char *buf, size_t buflen); /** * p2p_peer_known - Check whether P2P peer is known * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer * Returns: 1 if the specified device is in the P2P peer table or 0 if not */ int p2p_peer_known(struct p2p_data *p2p, const u8 *addr); /** * p2p_set_client_discoverability - Set client discoverability capability * @p2p: P2P module context from p2p_init() * @enabled: Whether client discoverability will be enabled * * This function can be used to disable (and re-enable) client discoverability. * This capability is enabled by default and should not be disabled in normal * use cases, i.e., this is mainly for testing purposes. */ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); /** * p2p_set_managed_oper - Set managed P2P Device operations capability * @p2p: P2P module context from p2p_init() * @enabled: Whether managed P2P Device operations will be enabled */ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); /** * p2p_config_get_random_social - Return a random social channel * @p2p: P2P config * @op_class: Selected operating class * @op_channel: Selected social channel * @avoid_list: Channel ranges to try to avoid or %NULL * @disallow_list: Channel ranges to discard or %NULL * Returns: 0 on success, -1 on failure * * This function is used before p2p_init is called. A random social channel * from supports bands 2.4 GHz (channels 1,6,11) and 60 GHz (channel 2) is * returned on success. */ int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, u8 *op_channel, struct wpa_freq_range_list *avoid_list, struct wpa_freq_range_list *disallow_list); int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, u8 forced); u8 p2p_get_listen_channel(struct p2p_data *p2p); int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr); int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr); void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); /** * p2p_set_cross_connect - Set cross connection capability * @p2p: P2P module context from p2p_init() * @enabled: Whether cross connection will be enabled */ void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); /** * p2p_set_intra_bss_dist - Set intra BSS distribution * @p2p: P2P module context from p2p_init() * @enabled: Whether intra BSS distribution will be enabled */ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); int p2p_channels_includes_freq(const struct p2p_channels *channels, unsigned int freq); int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, unsigned int max_len); /** * p2p_supported_freq - Check whether channel is supported for P2P * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * Returns: 0 if channel not usable for P2P, 1 if usable for P2P */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); /** * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * Returns: 0 if channel not usable for P2P, 1 if usable for P2P */ int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); /** * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation * @p2p: P2P module context from p2p_init() * @freq: Channel frequency in MHz * Returns: 0 if channel not usable for P2P, 1 if usable for P2P */ int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq); /** * p2p_get_pref_freq - Get channel from preferred channel list * @p2p: P2P module context from p2p_init() * @channels: List of channels * Returns: Preferred channel */ unsigned int p2p_get_pref_freq(struct p2p_data *p2p, const struct p2p_channels *channels); void p2p_update_channel_list(struct p2p_data *p2p, const struct p2p_channels *chan, const struct p2p_channels *cli_chan); bool is_p2p_6ghz_disabled(struct p2p_data *p2p); /** * p2p_set_best_channels - Update best channel information * @p2p: P2P module context from p2p_init() * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band * @freq_5: Frequency (MHz) of best channel in 5 GHz band * @freq_overall: Frequency (MHz) of best channel overall */ void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall); /** * p2p_set_own_freq_preference - Set own preference for channel * @p2p: P2P module context from p2p_init() * @freq: Frequency (MHz) of the preferred channel or 0 if no preference * * This function can be used to set a preference on the operating channel based * on frequencies used on the other virtual interfaces that share the same * radio. If non-zero, this is used to try to avoid multi-channel concurrency. */ void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq); const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); /** * p2p_get_group_num_members - Get number of members in group * @group: P2P group context from p2p_group_init() * Returns: Number of members in the group */ unsigned int p2p_get_group_num_members(struct p2p_group *group); /** * p2p_client_limit_reached - Check if client limit is reached * @group: P2P group context from p2p_group_init() * Returns: 1 if no of clients limit reached */ int p2p_client_limit_reached(struct p2p_group *group); /** * p2p_iterate_group_members - Iterate group members * @group: P2P group context from p2p_group_init() * @next: iteration pointer, must be a pointer to a void * that is set to %NULL * on the first call and not modified later * Returns: A P2P Device Address for each call and %NULL for no more members */ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); /** * p2p_group_get_client_interface_addr - Get P2P Interface Address of a client in a group * @group: P2P group context from p2p_group_init() * @dev_addr: P2P Device Address of the client * Returns: P2P Interface Address of the client if found or %NULL if no match * found */ const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, const u8 *dev_addr); /** * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group * @group: P2P group context from p2p_group_init() * @addr: P2P Interface Address of the client * Returns: P2P Device Address of the client if found or %NULL if no match * found */ const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); /** * p2p_group_is_client_connected - Check whether a specific client is connected * @group: P2P group context from p2p_group_init() * @addr: P2P Device Address of the client * Returns: 1 if client is connected or 0 if not */ int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); /** * p2p_group_get_config - Get the group configuration * @group: P2P group context from p2p_group_init() * Returns: The group configuration pointer */ const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group); /** * p2p_loop_on_all_groups - Run the given callback on all groups * @p2p: P2P module context from p2p_init() * @group_callback: The callback function pointer * @user_data: Some user data pointer which can be %NULL * * The group_callback function can stop the iteration by returning 0. */ void p2p_loop_on_all_groups(struct p2p_data *p2p, int (*group_callback)(struct p2p_group *group, void *user_data), void *user_data); /** * p2p_get_peer_found - Get P2P peer info structure of a found peer * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer or %NULL to indicate the first peer * @next: Whether to select the peer entry following the one indicated by addr * Returns: The first P2P peer info available or %NULL if no such peer exists */ const struct p2p_peer_info * p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next); /** * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions * @p2p: P2P module context from p2p_init() */ void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p); /** * p2p_add_wps_vendor_extension - Add a WPS vendor extension * @p2p: P2P module context from p2p_init() * @vendor_ext: The vendor extensions to add * Returns: 0 on success, -1 on failure * * The wpabuf structures in the array are owned by the P2P * module after this call. */ int p2p_add_wps_vendor_extension(struct p2p_data *p2p, const struct wpabuf *vendor_ext); /** * p2p_set_oper_channel - Set the P2P operating channel * @p2p: P2P module context from p2p_init() * @op_reg_class: Operating regulatory class to set * @op_channel: operating channel to set * @cfg_op_channel : Whether op_channel is hardcoded in configuration * Returns: 0 on success, -1 on failure */ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel); /** * p2p_set_pref_chan - Set P2P preferred channel list * @p2p: P2P module context from p2p_init() * @num_pref_chan: Number of entries in pref_chan list * @pref_chan: Preferred channels or %NULL to remove preferences * Returns: 0 on success, -1 on failure */ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan); /** * p2p_set_no_go_freq - Set no GO channel ranges * @p2p: P2P module context from p2p_init() * @list: Channel ranges or %NULL to remove restriction * Returns: 0 on success, -1 on failure */ int p2p_set_no_go_freq(struct p2p_data *p2p, const struct wpa_freq_range_list *list); /** * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not * in search state, or 2 if search state operation is in progress */ int p2p_in_progress(struct p2p_data *p2p); const char * p2p_wps_method_text(enum p2p_wps_method method); /** * p2p_set_config_timeout - Set local config timeouts * @p2p: P2P module context from p2p_init() * @go_timeout: Time in 10 ms units it takes to start the GO mode * @client_timeout: Time in 10 ms units it takes to start the client mode */ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, u8 client_timeout); int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem); struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); /** * p2p_set_disc_int - Set min/max discoverable interval for p2p_find * @p2p: P2P module context from p2p_init() * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1 * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3 * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or * -1 not to limit * Returns: 0 on success, or -1 on failure * * This function can be used to configure minDiscoverableInterval and * maxDiscoverableInterval parameters for the Listen state during device * discovery (p2p_find). A random number of 100 TU units is picked for each * Listen state iteration from [min_disc_int,max_disc_int] range. * * max_disc_tu can be used to further limit the discoverable duration. However, * it should be noted that use of this parameter is not recommended since it * would not be compliant with the P2P specification. */ int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, int max_disc_tu); /** * p2p_get_state_txt - Get current P2P state for debug purposes * @p2p: P2P module context from p2p_init() * Returns: Name of the current P2P module state * * It should be noted that the P2P module state names are internal information * and subject to change at any point, i.e., this information should be used * mainly for debugging purposes. */ const char * p2p_get_state_txt(struct p2p_data *p2p); struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len); struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, int client_freq, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len); struct p2p_nfc_params { int sel; const u8 *wsc_attr; size_t wsc_len; const u8 *p2p_attr; size_t p2p_len; enum { NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG, BOTH_GO, PEER_CLIENT } next_step; struct p2p_peer_info *peer; u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + WPS_OOB_DEVICE_PASSWORD_LEN]; size_t oob_dev_pw_len; int go_freq; u8 go_dev_addr[ETH_ALEN]; u8 go_ssid[SSID_MAX_LEN]; size_t go_ssid_len; }; int p2p_process_nfc_connection_handover(struct p2p_data *p2p, struct p2p_nfc_params *params); void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, int go_intent, const u8 *own_interface_addr); int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len); void p2p_loop_on_known_peers(struct p2p_data *p2p, void (*peer_callback)(struct p2p_peer_info *peer, void *user_data), void *user_data); void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem); void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr); struct p2ps_advertisement * p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, const char *svc_info, const u8 *cpt_priority); int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); void p2p_service_flush_asp(struct p2p_data *p2p); struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); /** * p2p_expire_peers - Periodic cleanup function to expire peers * @p2p: P2P module context from p2p_init() * * This is a cleanup function that the entity calling p2p_init() is * expected to call periodically to clean up expired peer entries. */ void p2p_expire_peers(struct p2p_data *p2p); void p2p_set_own_pref_freq_list(struct p2p_data *p2p, const unsigned int *pref_freq_list, unsigned int size); void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, u8 chan); /** * p2p_group_get_common_freqs - Get the group common frequencies * @group: P2P group context from p2p_group_init() * @common_freqs: On return will hold the group common frequencies * @num: On return will hold the number of group common frequencies * Returns: 0 on success, -1 otherwise */ int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, unsigned int *num); struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, unsigned int freq); +void p2p_set_6ghz_dev_capab(struct p2p_data *p2p, bool allow_6ghz); +bool is_p2p_6ghz_capable(struct p2p_data *p2p); +bool p2p_is_peer_6ghz_capab(struct p2p_data *p2p, const u8 *addr); +bool p2p_peer_wfd_enabled(struct p2p_data *p2p, const u8 *peer_addr); +bool p2p_wfd_enabled(struct p2p_data *p2p); +bool is_p2p_allow_6ghz(struct p2p_data *p2p); +void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value); +int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 4195c5f07dd1..8220e85506a3 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -1,895 +1,900 @@ /* * P2P - Internal definitions for P2P module * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef P2P_I_H #define P2P_I_H #include "utils/list.h" #include "p2p.h" #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 /* * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P * Device over the P2P Client Info received from a GO. */ #define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1 enum p2p_role_indication; /* * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN * must equal 248 or less. Must have a minimum size of 19. */ #define MAX_SVC_ADV_LEN 600 #define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240))) enum p2p_go_state { UNKNOWN_GO, LOCAL_GO, REMOTE_GO }; /** * struct p2p_device - P2P Device data (internal to P2P module) */ struct p2p_device { struct dl_list list; struct os_reltime last_seen; int listen_freq; int oob_go_neg_freq; enum p2p_wps_method wps_method; u16 oob_pw_id; struct p2p_peer_info info; /* * If the peer was discovered based on an interface address (e.g., GO * from Beacon/Probe Response), the interface address is stored here. * p2p_device_addr must still be set in such a case to the unique * identifier for the P2P Device. * * This field is also used during P2PS PD to store the intended GO * address of the peer. */ u8 interface_addr[ETH_ALEN]; /* * P2P Device Address of the GO in whose group this P2P Device is a * client. */ u8 member_in_go_dev[ETH_ALEN]; /* * P2P Interface Address of the GO in whose group this P2P Device is a * client. */ u8 member_in_go_iface[ETH_ALEN]; int go_neg_req_sent; enum p2p_go_state go_state; u8 dialog_token; u8 tie_breaker; u8 intended_addr[ETH_ALEN]; char country[3]; struct p2p_channels channels; int oper_freq; u8 oper_ssid[SSID_MAX_LEN]; size_t oper_ssid_len; /** * req_config_methods - Pending provision discovery methods */ u16 req_config_methods; /** * wps_prov_info - Stored provisioning WPS config method * * This is used to store pending WPS config method between Provisioning * Discovery and connection to a running group. */ u16 wps_prov_info; #define P2P_DEV_PROBE_REQ_ONLY BIT(0) #define P2P_DEV_REPORTED BIT(1) #define P2P_DEV_NOT_YET_READY BIT(2) #define P2P_DEV_PD_PEER_DISPLAY BIT(5) #define P2P_DEV_PD_PEER_KEYPAD BIT(6) #define P2P_DEV_USER_REJECTED BIT(7) #define P2P_DEV_PEER_WAITING_RESPONSE BIT(8) #define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9) #define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10) #define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11) #define P2P_DEV_GROUP_CLIENT_ONLY BIT(12) #define P2P_DEV_FORCE_FREQ BIT(13) #define P2P_DEV_PD_FOR_JOIN BIT(14) #define P2P_DEV_REPORTED_ONCE BIT(15) #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) #define P2P_DEV_NO_PREF_CHAN BIT(18) #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) #define P2P_DEV_P2PS_REPORTED BIT(20) #define P2P_DEV_PD_PEER_P2PS BIT(21) #define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22) unsigned int flags; int status; /* enum p2p_status_code */ unsigned int wait_count; unsigned int connect_reqs; unsigned int invitation_reqs; unsigned int sd_reqs; u16 ext_listen_period; u16 ext_listen_interval; u8 go_timeout; u8 client_timeout; /** * go_neg_conf_sent - Number of GO Negotiation Confirmation retries */ u8 go_neg_conf_sent; /** * freq - Frquency on which the GO Negotiation Confirmation is sent */ int go_neg_conf_freq; /** * go_neg_conf - GO Negotiation Confirmation frame */ struct wpabuf *go_neg_conf; int sd_pending_bcast_queries; }; struct p2p_sd_query { struct p2p_sd_query *next; u8 peer[ETH_ALEN]; int for_all_peers; int wsd; /* Wi-Fi Display Service Discovery Request */ struct wpabuf *tlvs; }; /** * struct p2p_data - P2P module data (internal to P2P module) */ struct p2p_data { /** * cfg - P2P module configuration * * This is included in the same memory allocation with the * struct p2p_data and as such, must not be freed separately. */ struct p2p_config *cfg; /** * state - The current P2P state */ enum p2p_state { /** * P2P_IDLE - Idle */ P2P_IDLE, /** * P2P_SEARCH - Search (Device Discovery) */ P2P_SEARCH, /** * P2P_CONNECT - Trying to start GO Negotiation */ P2P_CONNECT, /** * P2P_CONNECT_LISTEN - Listen during GO Negotiation start */ P2P_CONNECT_LISTEN, /** * P2P_GO_NEG - In GO Negotiation */ P2P_GO_NEG, /** * P2P_LISTEN_ONLY - Listen only */ P2P_LISTEN_ONLY, /** * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg */ P2P_WAIT_PEER_CONNECT, /** * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg */ P2P_WAIT_PEER_IDLE, /** * P2P_SD_DURING_FIND - Service Discovery during find */ P2P_SD_DURING_FIND, /** * P2P_PROVISIONING - Provisioning (during group formation) */ P2P_PROVISIONING, /** * P2P_PD_DURING_FIND - Provision Discovery during find */ P2P_PD_DURING_FIND, /** * P2P_INVITE - Trying to start Invite */ P2P_INVITE, /** * P2P_INVITE_LISTEN - Listen during Invite */ P2P_INVITE_LISTEN, } state; /** * min_disc_int - minDiscoverableInterval */ int min_disc_int; /** * max_disc_int - maxDiscoverableInterval */ int max_disc_int; /** * max_disc_tu - Maximum number of TUs for discoverable interval */ int max_disc_tu; /** * devices - List of known P2P Device peers */ struct dl_list devices; /** * go_neg_peer - Pointer to GO Negotiation peer */ struct p2p_device *go_neg_peer; /** * invite_peer - Pointer to Invite peer */ struct p2p_device *invite_peer; /** * last_p2p_find_oper - Pointer to last pre-find operation peer */ struct p2p_device *last_p2p_find_oper; const u8 *invite_go_dev_addr; u8 invite_go_dev_addr_buf[ETH_ALEN]; int invite_dev_pw_id; unsigned int retry_invite_req:1; unsigned int retry_invite_req_sent:1; /** * sd_peer - Pointer to Service Discovery peer */ struct p2p_device *sd_peer; /** * sd_query - Pointer to Service Discovery query */ struct p2p_sd_query *sd_query; /** * num_p2p_sd_queries - Total number of broadcast SD queries present in * the list */ int num_p2p_sd_queries; /** * sd_query_no_ack - The first peer (Dev Addr) that did not ACK SD Query * * This is used to track the first peer that did not ACK an SD Query * within a single P2P Search iteration. All zeros address means no such * peer was yet seen. This information is used to allow a new Listen and * Search phases to be once every pending SD Query has been sent once to * each peer instead of looping all pending attempts continuously until * running out of retry maximums. */ u8 sd_query_no_ack[ETH_ALEN]; /* GO Negotiation data */ /** * intended_addr - Local Intended P2P Interface Address * * This address is used during group owner negotiation as the Intended * P2P Interface Address and the group interface will be created with * address as the local address in case of successfully completed * negotiation. */ u8 intended_addr[ETH_ALEN]; /** * go_intent - Local GO Intent to be used during GO Negotiation */ u8 go_intent; /** * next_tie_breaker - Next tie-breaker value to use in GO Negotiation */ u8 next_tie_breaker; /** * ssid - Selected SSID for GO Negotiation (if local end will be GO) */ u8 ssid[SSID_MAX_LEN]; /** * ssid_len - ssid length in octets */ size_t ssid_len; /** * ssid_set - Whether SSID is already set for GO Negotiation */ int ssid_set; /** * Regulatory class for own operational channel */ u8 op_reg_class; /** * op_channel - Own operational channel */ u8 op_channel; /** * channels - Own supported regulatory classes and channels * * List of supposerted channels per regulatory class. The regulatory * classes are defined in IEEE Std 802.11-2007 Annex J and the * numbering of the clases depends on the configured country code. */ struct p2p_channels channels; struct wpa_freq_range_list no_go_freq; enum p2p_pending_action_state { P2P_NO_PENDING_ACTION, P2P_PENDING_GO_NEG_REQUEST, P2P_PENDING_GO_NEG_RESPONSE, P2P_PENDING_GO_NEG_RESPONSE_FAILURE, P2P_PENDING_GO_NEG_CONFIRM, P2P_PENDING_SD, P2P_PENDING_PD, P2P_PENDING_PD_RESPONSE, P2P_PENDING_INVITATION_REQUEST, P2P_PENDING_INVITATION_RESPONSE, P2P_PENDING_DEV_DISC_REQUEST, P2P_PENDING_DEV_DISC_RESPONSE, P2P_PENDING_GO_DISC_REQ } pending_action_state; unsigned int pending_listen_freq; unsigned int pending_listen_sec; unsigned int pending_listen_usec; u8 dev_capab; int in_listen; int drv_in_listen; /** * sd_queries - Pending service discovery queries */ struct p2p_sd_query *sd_queries; /** * srv_update_indic - Service Update Indicator for local services */ u16 srv_update_indic; struct wpabuf *sd_resp; /* Fragmented SD response */ u8 sd_resp_addr[ETH_ALEN]; u8 sd_resp_dialog_token; size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; struct wpabuf *sd_rx_resp; /* Reassembled SD response */ u16 sd_rx_update_indic; /* P2P Invitation data */ enum p2p_invite_role inv_role; u8 inv_bssid[ETH_ALEN]; int inv_bssid_set; u8 inv_ssid[SSID_MAX_LEN]; size_t inv_ssid_len; u8 inv_sa[ETH_ALEN]; u8 inv_group_bssid[ETH_ALEN]; u8 *inv_group_bssid_ptr; u8 inv_go_dev_addr[ETH_ALEN]; u8 inv_status; int inv_op_freq; int inv_persistent; enum p2p_discovery_type find_type; int find_specified_freq; unsigned int last_p2p_find_timeout; u8 last_prog_scan_class; u8 last_prog_scan_chan; unsigned int find_pending_full:1; int p2p_scan_running; enum p2p_after_scan { P2P_AFTER_SCAN_NOTHING, P2P_AFTER_SCAN_LISTEN, P2P_AFTER_SCAN_CONNECT } start_after_scan; u8 after_scan_peer[ETH_ALEN]; unsigned int send_action_in_progress:1; /* Requested device types for find/search */ unsigned int num_req_dev_types; u8 *req_dev_types; u8 *find_dev_id; u8 find_dev_id_buf[ETH_ALEN]; struct os_reltime find_start; /* time of last p2p_find start */ struct p2p_group **groups; size_t num_groups; struct p2p_device *pending_client_disc_go; u8 pending_client_disc_addr[ETH_ALEN]; u8 pending_dev_disc_dialog_token; u8 pending_dev_disc_addr[ETH_ALEN]; int pending_dev_disc_freq; unsigned int pending_client_disc_freq; int ext_listen_only; unsigned int ext_listen_period; unsigned int ext_listen_interval; unsigned int ext_listen_interval_sec; unsigned int ext_listen_interval_usec; u8 peer_filter[ETH_ALEN]; int cross_connect; int best_freq_24; int best_freq_5; int best_freq_overall; int own_freq_preference; /** * wps_vendor_ext - WPS Vendor Extensions to add */ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; /* * user_initiated_pd - Whether a PD request is user initiated or not. */ u8 user_initiated_pd; /* * Keep track of which peer a given PD request was sent to. * Used to raise a timeout alert in case there is no response. */ u8 pending_pd_devaddr[ETH_ALEN]; /* * Retry counter for provision discovery requests when issued * in IDLE state. */ int pd_retries; /** * pd_force_freq - Forced frequency for PD retries or 0 to auto-select * * This is is used during PD retries for join-a-group case to use the * correct operating frequency determined from a BSS entry for the GO. */ int pd_force_freq; u8 go_timeout; u8 client_timeout; /* Extra delay in milliseconds between search iterations */ unsigned int search_delay; int in_search_delay; u8 pending_reg_class; u8 pending_channel; u8 pending_channel_forced; /* ASP Support */ struct p2ps_advertisement *p2ps_adv_list; struct p2ps_provision *p2ps_prov; u8 wild_card_hash[P2PS_HASH_LEN]; u8 p2ps_seek; u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; u8 p2ps_seek_count; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; struct wpabuf *wfd_ie_probe_req; struct wpabuf *wfd_ie_probe_resp; struct wpabuf *wfd_ie_assoc_req; struct wpabuf *wfd_ie_invitation; struct wpabuf *wfd_ie_prov_disc_req; struct wpabuf *wfd_ie_prov_disc_resp; struct wpabuf *wfd_ie_go_neg; struct wpabuf *wfd_dev_info; struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; struct wpabuf *wfd_r2_dev_info; #endif /* CONFIG_WIFI_DISPLAY */ u16 authorized_oob_dev_pw_id; struct wpabuf **vendor_elem; unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; unsigned int num_pref_freq; /* Override option for preferred operating channel in GO Negotiation */ u8 override_pref_op_class; u8 override_pref_channel; + bool p2p_6ghz_capable; + bool include_6ghz; + bool allow_6ghz; }; /** * struct p2p_message - Parsed P2P message (or P2P IE) */ struct p2p_message { struct wpabuf *p2p_attributes; struct wpabuf *wps_attributes; struct wpabuf *wfd_subelems; u8 dialog_token; const u8 *capability; const u8 *go_intent; const u8 *status; const u8 *listen_channel; const u8 *operating_channel; const u8 *channel_list; u8 channel_list_len; const u8 *config_timeout; const u8 *intended_addr; const u8 *group_bssid; const u8 *invitation_flags; const u8 *group_info; size_t group_info_len; const u8 *group_id; size_t group_id_len; const u8 *device_id; const u8 *manageability; const u8 *noa; size_t noa_len; const u8 *ext_listen_timing; const u8 *minor_reason_code; const u8 *oob_go_neg_channel; /* P2P Device Info */ const u8 *p2p_device_info; size_t p2p_device_info_len; const u8 *p2p_device_addr; const u8 *pri_dev_type; u8 num_sec_dev_types; char device_name[WPS_DEV_NAME_MAX_LEN + 1]; u16 config_methods; /* WPS IE */ u16 dev_password_id; int dev_password_id_present; u16 wps_config_methods; const u8 *wps_pri_dev_type; const u8 *wps_sec_dev_type_list; size_t wps_sec_dev_type_list_len; const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT]; const u8 *manufacturer; size_t manufacturer_len; const u8 *model_name; size_t model_name_len; const u8 *model_number; size_t model_number_len; const u8 *serial_number; size_t serial_number_len; const u8 *oob_dev_password; size_t oob_dev_password_len; /* DS Parameter Set IE */ const u8 *ds_params; /* SSID IE */ const u8 *ssid; /* P2PS */ u8 service_hash_count; const u8 *service_hash; const u8 *session_info; size_t session_info_len; const u8 *conn_cap; const u8 *adv_id; const u8 *adv_mac; const u8 *adv_service_instance; size_t adv_service_instance_len; const u8 *session_id; const u8 *session_mac; const u8 *feature_cap; size_t feature_cap_len; const u8 *persistent_dev; const u8 *persistent_ssid; size_t persistent_ssid_len; const u8 *pref_freq_list; size_t pref_freq_list_len; }; #define P2P_MAX_GROUP_ENTRIES 50 struct p2p_group_info { unsigned int num_clients; struct p2p_client_info { const u8 *p2p_device_addr; const u8 *p2p_interface_addr; u8 dev_capab; u16 config_methods; const u8 *pri_dev_type; u8 num_sec_dev_types; const u8 *sec_dev_types; const char *dev_name; size_t dev_name_len; } client[P2P_MAX_GROUP_ENTRIES]; }; /* p2p_utils.c */ int p2p_random(char *buf, size_t len); int p2p_channel_to_freq(int op_class, int channel); int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); void p2p_channels_union_inplace(struct p2p_channels *res, const struct p2p_channels *b); void p2p_channels_union(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); void p2p_channels_remove_freqs(struct p2p_channels *chan, const struct wpa_freq_range_list *list); int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel); void p2p_channels_dump(struct p2p_data *p2p, const char *title, const struct p2p_channels *chan); int p2p_channel_select(struct p2p_channels *chans, const int *classes, u8 *op_class, u8 *op_channel); int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, u8 *op_channel, struct wpa_freq_range_list *avoid_list, struct wpa_freq_range_list *disallow_list); +void p2p_copy_channels(struct p2p_channels *dst, const struct p2p_channels *src, + bool allow_6ghz); /* p2p_parse.c */ void p2p_copy_filter_devname(char *dst, size_t dst_len, const void *src, size_t src_len); int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, size_t p2p_len, struct p2p_message *msg); void p2p_parse_free(struct p2p_message *msg); int p2p_attr_text(struct wpabuf *data, char *buf, char *end); int p2p_group_info_parse(const u8 *gi, size_t gi_len, struct p2p_group_info *info); /* p2p_build.c */ struct p2p_noa_desc { u8 count_type; u32 duration; u32 interval; u32 start_time; }; /* p2p_group.c */ const u8 * p2p_group_get_interface_addr(struct p2p_group *group); u8 p2p_group_presence_req(struct p2p_group *group, const u8 *client_interface_addr, const u8 *noa, size_t noa_len); int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, size_t group_id_len); void p2p_group_update_ies(struct p2p_group *group); void p2p_group_force_beacon_update_ies(struct p2p_group *group); struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, int max_clients); void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf); int p2p_group_get_freq(struct p2p_group *group); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf); void p2p_buf_add_status(struct wpabuf *buf, u8 status); void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, struct p2p_device *peer); void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr); void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len); void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab); void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent); void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, u8 reg_class, u8 channel); void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, u8 reg_class, u8 channel); void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan); void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, u8 client_timeout); void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr); void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid); void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags); void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2); void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval); void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, u8 oper_class, u8 channel, enum p2p_role_indication role); void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p); void p2p_buf_add_session_info(struct wpabuf *buf, const char *info); void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap); void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac); void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, u8 count, const u8 *hash, struct p2ps_advertisement *adv_list); void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac); void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask); void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); void p2p_buf_add_pref_channel_list(struct wpabuf *buf, const u32 *preferred_freq_list, u32 size); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, struct p2p_device *dev); void p2p_free_sd_queries(struct p2p_data *p2p); void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); /* p2p_go_neg.c */ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_device *dev, const u8 *channel_list, size_t channel_list_len); void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection); void p2p_check_pref_chan(struct p2p_data *p2p, int go, struct p2p_device *dev, struct p2p_message *msg); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); void p2ps_prov_free(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); /* p2p_dev_disc.c */ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success); int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev); void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success); void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); void p2p_go_disc_req_cb(struct p2p_data *p2p, int success); void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, const u8 *data, size_t len, int rx_freq); /* p2p.c */ void p2p_set_state(struct p2p_data *p2p, int new_state); void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec); void p2p_clear_timeout(struct p2p_data *p2p); void p2p_continue_find(struct p2p_data *p2p); struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, const u8 *addr, struct p2p_message *msg); void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg); int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res); struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr); void p2p_go_neg_failed(struct p2p_data *p2p, int status); void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type); struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, const u8 *query_hash, u8 query_count); void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go); void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx); int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, u8 *status); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_info(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_err(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); #endif /* P2P_I_H */ diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 77d662a47ae2..ab0072219d2e 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -1,719 +1,720 @@ /* * Wi-Fi Direct - P2P Invitation procedure * Copyright (c) 2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "p2p_i.h" #include "p2p.h" static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *buf; u8 *len; const u8 *dev_addr; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (os_memcmp(p2p_group_get_interface_addr(g), p2p->inv_bssid, ETH_ALEN) != 0) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; peer->dialog_token++; if (peer->dialog_token == 0) peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || !(peer->flags & P2P_DEV_NO_PREF_CHAN)) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); if (go_dev_addr) dev_addr = go_dev_addr; else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) dev_addr = peer->info.p2p_device_addr; else dev_addr = p2p->cfg->dev_addr; p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, p2p->num_pref_freq); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); if (dev_pw_id >= 0) { /* WSC IE in Invitation Request for NFC static handover */ p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); } return buf; } static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, struct p2p_device *peer, u8 dialog_token, u8 status, const u8 *group_bssid, u8 reg_class, u8 channel, struct p2p_channels *channels) { struct wpabuf *buf; u8 *len; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && group_bssid) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (os_memcmp(p2p_group_get_interface_addr(g), group_bssid, ETH_ALEN) != 0) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ if (reg_class && channel) p2p_buf_add_operating_channel(buf, p2p->cfg->country, reg_class, channel); if (group_bssid) p2p_buf_add_group_bssid(buf, group_bssid); if (channels) p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); return buf; } void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; struct p2p_message msg; struct wpabuf *resp = NULL; u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; int freq; int go = 0; u8 group_bssid[ETH_ALEN], *bssid; int op_freq = 0; u8 reg_class = 0, channel = 0; struct p2p_channels all_channels, intersection, *channels = NULL; int persistent; os_memset(group_bssid, 0, sizeof(group_bssid)); p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR, MAC2STR(sa)); if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { p2p_dbg(p2p, "Invitation Request add device failed " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } dev = p2p_get_device(p2p, sa); if (dev == NULL) { p2p_dbg(p2p, "Reject Invitation Request from unknown peer " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } } if (!msg.group_id || !msg.channel_list) { p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (msg.invitation_flags) persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE; else { /* Invitation Flags is a mandatory attribute starting from P2P * spec 1.06. As a backwards compatibility mechanism, assume * the request was for a persistent group if the attribute is * missing. */ p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request"); persistent = 1; } p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels, &all_channels); if (p2p_peer_channels_check(p2p, &all_channels, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels); p2p_channels_dump(p2p, "own client channels", &all_channels); p2p_channels_dump(p2p, "peer channels", &dev->channels); p2p_channels_intersect(&all_channels, &dev->channels, &intersection); p2p_channels_dump(p2p, "intersection", &intersection); if (p2p->cfg->invitation_process) { status = p2p->cfg->invitation_process( p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, &go, group_bssid, &op_freq, persistent, &intersection, msg.dev_password_id_present ? msg.dev_password_id : -1); } if (go) { p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, &intersection); p2p_channels_dump(p2p, "intersection(GO)", &intersection); if (intersection.reg_classes == 0) { p2p_dbg(p2p, "No common channels found (GO)"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } } if (op_freq) { p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", op_freq); if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (!p2p_channels_includes(&intersection, reg_class, channel)) { p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (status == P2P_SC_SUCCESS) channels = &intersection; } else { p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use"); /* Default to own configuration as a starting point */ p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; p2p_dbg(p2p, "Own default op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); /* Use peer preference if specified and compatible */ if (msg.operating_channel) { int req_freq; req_freq = p2p_channel_to_freq( msg.operating_channel[3], msg.operating_channel[4]); p2p_dbg(p2p, "Peer operating channel preference: %d MHz", req_freq); if (req_freq > 0 && p2p_channels_includes(&intersection, msg.operating_channel[3], msg.operating_channel[4])) { p2p->op_reg_class = msg.operating_channel[3]; p2p->op_channel = msg.operating_channel[4]; p2p_dbg(p2p, "Use peer preference op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); } else { p2p_dbg(p2p, "Cannot use peer channel preference"); } } /* Reselect the channel only for the case of the GO */ if (go && !p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); p2p_dbg(p2p, "Re-selection result: op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)", p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } } else if (go && !(dev->flags & P2P_DEV_FORCE_FREQ) && !p2p->cfg->cfg_op_channel) { p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } /* * Use the driver preferred frequency list extension if * supported. */ p2p_check_pref_chan(p2p, go, dev, &msg); op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)", p2p->cfg->country[0], p2p->cfg->country[1], p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq); if (status == P2P_SC_SUCCESS) { reg_class = p2p->op_reg_class; channel = p2p->op_channel; channels = &intersection; } } fail: if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid)) bssid = group_bssid; else bssid = NULL; resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, bssid, reg_class, channel, channels); if (resp == NULL) goto out; if (rx_freq > 0) freq = rx_freq; else freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); goto out; } /* * Store copy of invitation data to be used when processing TX status * callback for the Acton frame. */ os_memcpy(p2p->inv_sa, sa, ETH_ALEN); if (msg.group_bssid) { os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN); p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; } else p2p->inv_group_bssid_ptr = NULL; if (msg.group_id) { if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; } os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); } else { p2p->inv_ssid_len = 0; os_memset(p2p->inv_go_dev_addr, 0, ETH_ALEN); } p2p->inv_status = status; p2p->inv_op_freq = op_freq; p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } out: wpabuf_free(resp); p2p_parse_free(&msg); } void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { struct p2p_device *dev; struct p2p_message msg; struct p2p_channels intersection, *channels = NULL; p2p_dbg(p2p, "Received Invitation Response from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL) { p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " MACSTR, MAC2STR(sa)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } if (dev != p2p->invite_peer) { p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " MACSTR, MAC2STR(sa)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } if (p2p_parse(data, len, &msg)) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } if (!msg.status) { p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); p2p_parse_free(&msg); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } /* * We should not really receive a replayed response twice since * duplicate frames are supposed to be dropped. However, not all drivers * do that for pre-association frames. We did not use to verify dialog * token matches for invitation response frames, but that check can be * safely used to drop a replayed response to the previous Invitation * Request in case the suggested operating channel was changed. This * allows a duplicated reject frame to be dropped with the assumption * that the real response follows after it. */ if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && p2p->retry_invite_req_sent && msg.dialog_token != dev->dialog_token) { p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && p2p->retry_invite_req && p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, &p2p->op_channel, NULL, NULL) == 0) { p2p->retry_invite_req = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p_set_state(p2p, P2P_INVITE); p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel", p2p->op_reg_class, p2p->op_channel); p2p->retry_invite_req_sent = 1; p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); p2p_parse_free(&msg); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->retry_invite_req = 0; if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ /* Try to survive without peer channel list */ channels = &p2p->channels; } else if (!msg.channel_list) { /* Non-success cases are not required to include Channel List */ channels = &p2p->channels; } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); p2p_parse_free(&msg); return; } else { p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); channels = &intersection; } if (p2p->cfg->invitation_result) { int peer_oper_freq = 0; int freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq < 0) freq = 0; if (msg.operating_channel) { peer_oper_freq = p2p_channel_to_freq( msg.operating_channel[3], msg.operating_channel[4]); if (peer_oper_freq < 0) peer_oper_freq = 0; } /* * Use the driver preferred frequency list extension if * supported. */ p2p_check_pref_chan(p2p, 0, dev, &msg); p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, msg.group_bssid, channels, sa, freq, peer_oper_freq); } p2p_parse_free(&msg); p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); p2p->invite_peer = NULL; } int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *req; int freq; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) freq = dev->oob_go_neg_freq; if (freq <= 0) { p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " MACSTR " to send Invitation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id); if (req == NULL) return -1; if (p2p->state != P2P_IDLE) p2p_stop_listen_for_freq(p2p, freq); p2p_dbg(p2p, "Sending Invitation Request"); p2p_set_state(p2p, P2P_INVITE); p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; p2p->invite_peer = dev; dev->invitation_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 500) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); } else { dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK; } wpabuf_free(req); return 0; } void p2p_invitation_req_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success); if (p2p->invite_peer == NULL) { p2p_dbg(p2p, "No pending Invite"); return; } if (success) p2p->invite_peer->flags &= ~P2P_DEV_WAIT_INV_REQ_ACK; /* * Use P2P find, if needed, to find the other device from its listen * channel. */ p2p_set_state(p2p, P2P_INVITE); p2p_set_timeout(p2p, 0, success ? 500000 : 100000); } void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (!success) p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported"); if (p2p->cfg->invitation_received) { p2p->cfg->invitation_received(p2p->cfg->cb_ctx, p2p->inv_sa, p2p->inv_group_bssid_ptr, p2p->inv_ssid, p2p->inv_ssid_len, p2p->inv_go_dev_addr, p2p->inv_status, p2p->inv_op_freq); } } int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, int persistent_group, unsigned int pref_freq, int dev_pw_id) { struct p2p_device *dev; p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d " - "force_freq=%u", - MAC2STR(peer), role, persistent_group, force_freq); + "force_freq=%u allow_6ghz=%d", + MAC2STR(peer), role, persistent_group, force_freq, + p2p->allow_6ghz); if (bssid) p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid)); if (go_dev_addr) { p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR, MAC2STR(go_dev_addr)); os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; } else p2p->invite_go_dev_addr = NULL; wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID", ssid, ssid_len); if (dev_pw_id >= 0) { p2p_dbg(p2p, "Invitation to use Device Password ID %d", dev_pw_id); } p2p->invite_dev_pw_id = dev_pw_id; p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO && persistent_group && !force_freq; p2p->retry_invite_req_sent = 0; dev = p2p_get_device(p2p, peer); if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && dev->oob_go_neg_freq <= 0)) { p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR, MAC2STR(peer)); return -1; } if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, role != P2P_INVITE_ROLE_CLIENT) < 0) return -1; if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && !pref_freq) dev->flags |= P2P_DEV_NO_PREF_CHAN; else dev->flags &= ~P2P_DEV_NO_PREF_CHAN; if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer)); } /* TODO: use device discoverability request through GO */ } dev->invitation_reqs = 0; if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); p2p->inv_role = role; p2p->inv_bssid_set = bssid != NULL; if (bssid) os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN); os_memcpy(p2p->inv_ssid, ssid, ssid_len); p2p->inv_ssid_len = ssid_len; p2p->inv_persistent = persistent_group; return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); } diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 1a62a44a2df3..7d21f68819c7 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -1,498 +1,537 @@ /* * P2P - generic helper functions * Copyright (c) 2009, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "common/defs.h" #include "common/ieee802_11_common.h" #include "p2p_i.h" /** * p2p_random - Generate random string for SSID and passphrase * @buf: Buffer for returning the result * @len: Number of octets to write to the buffer * Returns: 0 on success, -1 on failure * * This function generates a random string using the following character set: * 'A'-'Z', 'a'-'z', '0'-'9'. */ int p2p_random(char *buf, size_t len) { u8 val; size_t i; u8 letters = 'Z' - 'A' + 1; u8 numbers = 10; if (os_get_random((unsigned char *) buf, len)) return -1; /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */ for (i = 0; i < len; i++) { val = buf[i]; val %= 2 * letters + numbers; if (val < letters) buf[i] = 'A' + val; else if (val < 2 * letters) buf[i] = 'a' + (val - letters); else buf[i] = '0' + (val - 2 * letters); } return 0; } /** * p2p_channel_to_freq - Convert channel info to frequency * @op_class: Operating class * @channel: Channel number * Returns: Frequency in MHz or -1 if the specified channel is unknown */ int p2p_channel_to_freq(int op_class, int channel) { return ieee80211_chan_to_freq(NULL, op_class, channel); } /** * p2p_freq_to_channel - Convert frequency into channel info * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: 0 on success, -1 if the specified frequency is unknown */ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) == NUM_HOSTAPD_MODES) return -1; return 0; } static void p2p_reg_class_intersect(const struct p2p_reg_class *a, const struct p2p_reg_class *b, struct p2p_reg_class *res) { size_t i, j; res->reg_class = a->reg_class; for (i = 0; i < a->channels; i++) { for (j = 0; j < b->channels; j++) { if (a->channel[i] != b->channel[j]) continue; res->channel[res->channels] = a->channel[i]; res->channels++; if (res->channels == P2P_MAX_REG_CLASS_CHANNELS) return; } } } /** * p2p_channels_intersect - Intersection of supported channel lists * @a: First set of supported channels * @b: Second set of supported channels * @res: Data structure for returning the intersection of support channels * * This function can be used to find a common set of supported channels. Both * input channels sets are assumed to use the same country code. If different * country codes are used, the regulatory class numbers may not be matched * correctly and results are undefined. */ void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res) { size_t i, j; os_memset(res, 0, sizeof(*res)); for (i = 0; i < a->reg_classes; i++) { const struct p2p_reg_class *a_reg = &a->reg_class[i]; for (j = 0; j < b->reg_classes; j++) { const struct p2p_reg_class *b_reg = &b->reg_class[j]; if (a_reg->reg_class != b_reg->reg_class) continue; p2p_reg_class_intersect( a_reg, b_reg, &res->reg_class[res->reg_classes]); if (res->reg_class[res->reg_classes].channels) { res->reg_classes++; if (res->reg_classes == P2P_MAX_REG_CLASSES) return; } } } } static void p2p_op_class_union(struct p2p_reg_class *cl, const struct p2p_reg_class *b_cl) { size_t i, j; for (i = 0; i < b_cl->channels; i++) { for (j = 0; j < cl->channels; j++) { if (b_cl->channel[i] == cl->channel[j]) break; } if (j == cl->channels) { if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS) return; cl->channel[cl->channels++] = b_cl->channel[i]; } } } /** * p2p_channels_union_inplace - Inplace union of channel lists * @res: Input data and place for returning union of the channel sets * @b: Second set of channels */ void p2p_channels_union_inplace(struct p2p_channels *res, const struct p2p_channels *b) { size_t i, j; for (i = 0; i < res->reg_classes; i++) { struct p2p_reg_class *cl = &res->reg_class[i]; for (j = 0; j < b->reg_classes; j++) { const struct p2p_reg_class *b_cl = &b->reg_class[j]; if (cl->reg_class != b_cl->reg_class) continue; p2p_op_class_union(cl, b_cl); } } for (j = 0; j < b->reg_classes; j++) { const struct p2p_reg_class *b_cl = &b->reg_class[j]; for (i = 0; i < res->reg_classes; i++) { struct p2p_reg_class *cl = &res->reg_class[i]; if (cl->reg_class == b_cl->reg_class) break; } if (i == res->reg_classes) { if (res->reg_classes == P2P_MAX_REG_CLASSES) return; os_memcpy(&res->reg_class[res->reg_classes++], b_cl, sizeof(struct p2p_reg_class)); } } } /** * p2p_channels_union - Union of channel lists * @a: First set of channels * @b: Second set of channels * @res: Data structure for returning the union of channels */ void p2p_channels_union(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res) { os_memcpy(res, a, sizeof(*res)); p2p_channels_union_inplace(res, b); } void p2p_channels_remove_freqs(struct p2p_channels *chan, const struct wpa_freq_range_list *list) { size_t o, c; if (list == NULL) return; o = 0; while (o < chan->reg_classes) { struct p2p_reg_class *op = &chan->reg_class[o]; c = 0; while (c < op->channels) { int freq = p2p_channel_to_freq(op->reg_class, op->channel[c]); if (freq > 0 && freq_range_list_includes(list, freq)) { op->channels--; os_memmove(&op->channel[c], &op->channel[c + 1], op->channels - c); } else c++; } if (op->channels == 0) { chan->reg_classes--; os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], (chan->reg_classes - o) * sizeof(struct p2p_reg_class)); } else o++; } } /** * p2p_channels_includes - Check whether a channel is included in the list * @channels: List of supported channels * @reg_class: Regulatory class of the channel to search * @channel: Channel number of the channel to search * Returns: 1 if channel was found or 0 if not */ int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel) { size_t i, j; for (i = 0; i < channels->reg_classes; i++) { const struct p2p_reg_class *reg = &channels->reg_class[i]; if (reg->reg_class != reg_class) continue; for (j = 0; j < reg->channels; j++) { if (reg->channel[j] == channel) return 1; } } return 0; } int p2p_channels_includes_freq(const struct p2p_channels *channels, unsigned int freq) { size_t i, j; for (i = 0; i < channels->reg_classes; i++) { const struct p2p_reg_class *reg = &channels->reg_class[i]; for (j = 0; j < reg->channels; j++) { if (p2p_channel_to_freq(reg->reg_class, reg->channel[j]) == (int) freq) return 1; } } return 0; } int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel); } int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel) && !freq_range_list_includes(&p2p->no_go_freq, freq); } int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel) || p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class, op_channel); } unsigned int p2p_get_pref_freq(struct p2p_data *p2p, const struct p2p_channels *channels) { unsigned int i; int freq = 0; const struct p2p_channels *tmpc = channels ? channels : &p2p->cfg->channels; if (tmpc == NULL) return 0; for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, p2p->cfg->pref_chan[i].chan); if (p2p_channels_includes_freq(tmpc, freq)) return freq; } return 0; } void p2p_channels_dump(struct p2p_data *p2p, const char *title, const struct p2p_channels *chan) { char buf[500], *pos, *end; size_t i, j; int ret; pos = buf; end = pos + sizeof(buf); for (i = 0; i < chan->reg_classes; i++) { const struct p2p_reg_class *c; c = &chan->reg_class[i]; ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); if (os_snprintf_error(end - pos, ret)) break; pos += ret; for (j = 0; j < c->channels; j++) { ret = os_snprintf(pos, end - pos, "%s%u", j == 0 ? "" : ",", c->channel[j]); if (os_snprintf_error(end - pos, ret)) break; pos += ret; } } *pos = '\0'; p2p_dbg(p2p, "%s:%s", title, buf); } static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels) { unsigned int r; if (os_get_random((u8 *) &r, sizeof(r)) < 0) r = 0; r %= num_channels; return channels[r]; } int p2p_channel_select(struct p2p_channels *chans, const int *classes, u8 *op_class, u8 *op_channel) { unsigned int i, j; for (j = 0; classes == NULL || classes[j]; j++) { for (i = 0; i < chans->reg_classes; i++) { struct p2p_reg_class *c = &chans->reg_class[i]; if (c->channels == 0) continue; if (classes == NULL || c->reg_class == classes[j]) { /* * Pick one of the available channels in the * operating class at random. */ *op_class = c->reg_class; *op_channel = p2p_channel_pick_random( c->channel, c->channels); return 0; } } if (classes == NULL) break; } return -1; } int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, u8 *op_channel, struct wpa_freq_range_list *avoid_list, struct wpa_freq_range_list *disallow_list) { u8 chan[4]; unsigned int num_channels = 0; /* Try to find available social channels from 2.4 GHz. * If the avoid_list includes any of the 2.4 GHz social channels, that * channel is not allowed by p2p_channels_includes() rules. However, it * is assumed to allow minimal traffic for P2P negotiation, so allow it * here for social channel selection unless explicitly disallowed in the * disallow_list. */ if (p2p_channels_includes(chans, 81, 1) || (freq_range_list_includes(avoid_list, 2412) && !freq_range_list_includes(disallow_list, 2412))) chan[num_channels++] = 1; if (p2p_channels_includes(chans, 81, 6) || (freq_range_list_includes(avoid_list, 2437) && !freq_range_list_includes(disallow_list, 2437))) chan[num_channels++] = 6; if (p2p_channels_includes(chans, 81, 11) || (freq_range_list_includes(avoid_list, 2462) && !freq_range_list_includes(disallow_list, 2462))) chan[num_channels++] = 11; /* Try to find available social channels from 60 GHz */ if (p2p_channels_includes(chans, 180, 2)) chan[num_channels++] = 2; if (num_channels == 0) return -1; *op_channel = p2p_channel_pick_random(chan, num_channels); if (*op_channel == 2) *op_class = 180; else *op_class = 81; return 0; } int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, unsigned int max_len) { unsigned int i, idx; if (!channels || max_len == 0) return 0; for (i = 0, idx = 0; i < channels->reg_classes; i++) { const struct p2p_reg_class *c = &channels->reg_class[i]; unsigned int j; if (idx + 1 == max_len) break; for (j = 0; j < c->channels; j++) { int freq; unsigned int k; if (idx + 1 == max_len) break; freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; for (k = 0; k < idx; k++) { if (freq_list[k] == freq) break; } if (k < idx) continue; freq_list[idx++] = freq; } } freq_list[idx] = 0; return idx; } + + +void p2p_copy_channels(struct p2p_channels *dst, + const struct p2p_channels *src, bool allow_6ghz) +{ + size_t i, j; + + if (allow_6ghz) { + os_memcpy(dst, src, sizeof(struct p2p_channels)); + return; + } + + for (i = 0, j = 0; i < P2P_MAX_REG_CLASSES; i++) { + if (is_6ghz_op_class(src->reg_class[i].reg_class)) + continue; + os_memcpy(&dst->reg_class[j], &src->reg_class[i], + sizeof(struct p2p_reg_class)); + j++; + } + dst->reg_classes = j; +} + + +int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (is_6ghz_freq(pref_freq_list[i])) { + wpa_printf(MSG_DEBUG, "P2P: Remove 6 GHz channel %d", + pref_freq_list[i]); + size--; + os_memmove(&pref_freq_list[i], &pref_freq_list[i + 1], + (size - i) * sizeof(pref_freq_list[0])); + i--; + } + } + return i; +} diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index cb2a8674a81b..97a01a2f81e8 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -1,664 +1,668 @@ /* * WPA Supplicant - RSN PMKSA cache * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "wpa_i.h" #include "pmksa_cache.h" #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) static const int pmksa_cache_max_entries = 32; struct rsn_pmksa_cache { struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ int pmksa_count; /* number of entries in PMKSA cache */ struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason); void *ctx; }; static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { bin_clear_free(entry, sizeof(*entry)); } static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, enum pmksa_free_reason reason) { wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, entry->fils_cache_id_set ? entry->fils_cache_id : NULL); pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); } static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; struct os_reltime now; os_get_reltime(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; pmksa->pmksa = entry->next; wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(entry->aa)); pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); } pmksa_cache_set_expiration(pmksa); } static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; pmksa->sm->cur_pmksa = NULL; eapol_sm_request_reauth(pmksa->sm->eapol); } static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); if (pmksa->pmksa == NULL) return; os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) sec = 0; eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, NULL); } } /** * pmksa_cache_add - Add a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) * @pmkid: Calculated PMKID * @kck: Key confirmation key or %NULL if not yet derived * @kck_len: KCK length in bytes * @aa: Authenticator address * @spa: Supplicant address * @network_ctx: Network configuration context for this PMK * @akmp: WPA_KEY_MGMT_* used in key derivation * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA * cache. If an old entry is already in the cache for the same Authenticator, * this entry will be replaced with the new entry. PMKID will be calculated * based on the PMK and the driver interface is notified of the new PMKID. */ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp, const u8 *cache_id) { struct rsn_pmksa_cache_entry *entry; struct os_reltime now; if (pmk_len > PMK_LEN_MAX) return NULL; if (wpa_key_mgmt_suite_b(akmp) && !kck) return NULL; entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; if (pmkid) os_memcpy(entry->pmkid, pmkid, PMKID_LEN); else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; entry->akmp = akmp; if (cache_id) { entry->fils_cache_id_set = 1; os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN); } os_memcpy(entry->aa, aa, ETH_ALEN); entry->network_ctx = network_ctx; return pmksa_cache_add_entry(pmksa, entry); } struct rsn_pmksa_cache_entry * pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; /* Replace an old entry for the same Authenticator (if found) with the * new entry */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) { if (pos->pmk_len == entry->pmk_len && os_memcmp_const(pos->pmk, entry->pmk, entry->pmk_len) == 0 && os_memcmp_const(pos->pmkid, entry->pmkid, PMKID_LEN) == 0) { wpa_printf(MSG_DEBUG, "WPA: reusing previous " "PMKSA entry"); os_free(entry); return pos; } if (prev == NULL) pmksa->pmksa = pos->next; else prev->next = pos->next; /* * If OKC is used, there may be other PMKSA cache * entries based on the same PMK. These needs to be * flushed so that a new entry can be created based on * the new PMK. Only clear other entries if they have a * matching PMK and this PMK has been used successfully * with the current AP, i.e., if opportunistic flag has * been cleared in wpa_supplicant_key_neg_complete(). */ wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " "the current AP and any PMKSA cache entry " "that was based on the old PMK"); if (!pos->opportunistic) pmksa_cache_flush(pmksa, entry->network_ctx, - pos->pmk, pos->pmk_len); + pos->pmk, pos->pmk_len, + false); pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } prev = pos; pos = pos->next; } if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { /* Remove the oldest entry to make room for the new entry */ pos = pmksa->pmksa; if (pos == pmksa->sm->cur_pmksa) { /* * Never remove the current PMKSA cache entry, since * it's in use, and removing it triggers a needless * deauthentication. */ pos = pos->next; pmksa->pmksa->next = pos ? pos->next : NULL; } else pmksa->pmksa = pos->next; if (pos) { wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " "PMKSA cache entry (for " MACSTR ") to " "make room for new one", MAC2STR(pos->aa)); pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); } } /* Add the new entry; order by expiration time */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (pos->expiration > entry->expiration) break; prev = pos; pos = pos->next; } if (prev == NULL) { entry->next = pmksa->pmksa; pmksa->pmksa = entry; pmksa_cache_set_expiration(pmksa); } else { entry->next = prev->next; prev->next = entry; } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa), entry->network_ctx, entry->akmp); wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, entry->fils_cache_id_set ? entry->fils_cache_id : NULL, entry->pmk, entry->pmk_len, pmksa->sm->dot11RSNAConfigPMKLifetime, pmksa->sm->dot11RSNAConfigPMKReauthThreshold, entry->akmp); return entry; } /** * pmksa_cache_flush - Flush PMKSA cache entries for a specific network * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context or %NULL to flush all entries * @pmk: PMK to match for or %NULL to match all PMKs * @pmk_len: PMK length + * @external_only: Flush only PMKSA cache entries configured by external + * applications */ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *pmk, size_t pmk_len) + const u8 *pmk, size_t pmk_len, bool external_only) { struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; int removed = 0; entry = pmksa->pmksa; while (entry) { if ((entry->network_ctx == network_ctx || network_ctx == NULL) && (pmk == NULL || (pmk_len == entry->pmk_len && - os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { + os_memcmp(pmk, entry->pmk, pmk_len) == 0)) && + (!external_only || entry->external)) { wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " "for " MACSTR, MAC2STR(entry->aa)); if (prev) prev->next = entry->next; else pmksa->pmksa = entry->next; tmp = entry; entry = entry->next; pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); removed++; } else { prev = entry; entry = entry->next; } } if (removed) pmksa_cache_set_expiration(pmksa); } /** * pmksa_cache_deinit - Free all entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() */ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) { struct rsn_pmksa_cache_entry *entry, *prev; if (pmksa == NULL) return; entry = pmksa->pmksa; pmksa->pmksa = NULL; while (entry) { prev = entry; entry = entry->next; os_free(prev); } pmksa_cache_set_expiration(pmksa); os_free(pmksa); } /** * pmksa_cache_get - Fetch a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @aa: Authenticator address or %NULL to match any * @pmkid: PMKID or %NULL to match any * @network_ctx: Network context or %NULL to match any * @akmp: Specific AKMP to search for or 0 for any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx, int akmp) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && (pmkid == NULL || os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && (!akmp || akmp == entry->akmp) && (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; } return NULL; } static struct rsn_pmksa_cache_entry * pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa) { struct rsn_pmksa_cache_entry *new_entry; os_time_t old_expiration = old_entry->expiration; const u8 *pmkid = NULL; if (wpa_key_mgmt_sae(old_entry->akmp) || wpa_key_mgmt_fils(old_entry->akmp)) pmkid = old_entry->pmkid; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, pmkid, NULL, 0, aa, pmksa->sm->own_addr, old_entry->network_ctx, old_entry->akmp, old_entry->fils_cache_id_set ? old_entry->fils_cache_id : NULL); if (new_entry == NULL) return NULL; /* TODO: reorder entries based on expiration time? */ new_entry->expiration = old_expiration; new_entry->opportunistic = 1; return new_entry; } /** * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context * @aa: Authenticator address for the new AP * @akmp: Specific AKMP to search for or 0 for any * Returns: Pointer to a new PMKSA cache entry or %NULL if not available * * Try to create a new PMKSA cache entry opportunistically by guessing that the * new AP is sharing the same PMK as another AP that has the same SSID and has * already an entry in PMKSA cache. */ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa, int akmp) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); if (network_ctx == NULL) return NULL; while (entry) { if (entry->network_ctx == network_ctx && (!akmp || entry->akmp == akmp)) { struct os_reltime now; if (wpa_key_mgmt_sae(entry->akmp) && os_get_reltime(&now) == 0 && entry->reauth_time < now.sec) { wpa_printf(MSG_DEBUG, "RSN: Do not clone PMKSA cache entry for " MACSTR " since its reauth threshold has passed", MAC2STR(entry->aa)); entry = entry->next; continue; } entry = pmksa_cache_clone_entry(pmksa, entry, aa); if (entry) { wpa_printf(MSG_DEBUG, "RSN: added " "opportunistic PMKSA cache entry " "for " MACSTR, MAC2STR(aa)); } return entry; } entry = entry->next; } return NULL; } static struct rsn_pmksa_cache_entry * pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa, const void *network_ctx, const u8 *cache_id) { struct rsn_pmksa_cache_entry *entry; for (entry = pmksa->pmksa; entry; entry = entry->next) { if (network_ctx == entry->network_ctx && entry->fils_cache_id_set && os_memcmp(cache_id, entry->fils_cache_id, FILS_CACHE_ID_LEN) == 0) return entry; } return NULL; } /** * pmksa_cache_get_current - Get the current used PMKSA entry * @sm: Pointer to WPA state machine data from wpa_sm_init() * Returns: Pointer to the current PMKSA cache entry or %NULL if not available */ struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) { if (sm == NULL) return NULL; return sm->cur_pmksa; } /** * pmksa_cache_clear_current - Clear the current PMKSA entry selection * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void pmksa_cache_clear_current(struct wpa_sm *sm) { if (sm == NULL) return; if (sm->cur_pmksa) wpa_printf(MSG_DEBUG, "RSN: Clear current PMKSA entry selection"); sm->cur_pmksa = NULL; } /** * pmksa_cache_set_current - Set the current PMKSA entry selection * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmkid: PMKID for selecting PMKSA or %NULL if not used * @bssid: BSSID for PMKSA or %NULL if not used * @network_ctx: Network configuration context * @try_opportunistic: Whether to allow opportunistic PMKSA caching * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used * Returns: 0 if PMKSA was found or -1 if no matching entry was found */ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, int try_opportunistic, const u8 *fils_cache_id, int akmp) { struct rsn_pmksa_cache *pmksa = sm->pmksa; wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " "try_opportunistic=%d akmp=0x%x", network_ctx, try_opportunistic, akmp); if (pmkid) wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", pmkid, PMKID_LEN); if (bssid) wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, MAC2STR(bssid)); if (fils_cache_id) wpa_printf(MSG_DEBUG, "RSN: Search for FILS Cache Identifier %02x%02x", fils_cache_id[0], fils_cache_id[1]); sm->cur_pmksa = NULL; if (pmkid) sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, network_ctx, akmp); if (sm->cur_pmksa == NULL && bssid) sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, network_ctx, akmp); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, network_ctx, bssid, akmp); if (sm->cur_pmksa == NULL && fils_cache_id) sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa, network_ctx, fils_cache_id); if (sm->cur_pmksa) { struct os_reltime now; if (wpa_key_mgmt_sae(sm->cur_pmksa->akmp) && os_get_reltime(&now) == 0 && sm->cur_pmksa->reauth_time < now.sec) { wpa_printf(MSG_DEBUG, "RSN: Do not allow PMKSA cache entry for " MACSTR " to be used for SAE since its reauth threshold has passed", MAC2STR(sm->cur_pmksa->aa)); sm->cur_pmksa = NULL; return -1; } wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); return 0; } wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); return -1; } /** * pmksa_cache_list - Dump text list of entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @buf: Buffer for the list * @len: Length of the buffer * Returns: number of bytes written to buffer * * This function is used to generate a text format representation of the * current PMKSA cache contents for the ctrl_iface PMKSA command. */ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) { int i, ret; char *pos = buf; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; int cache_id_used = 0; for (entry = pmksa->pmksa; entry; entry = entry->next) { if (entry->fils_cache_id_set) { cache_id_used = 1; break; } } os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic%s\n", cache_id_used ? " / FILS Cache Identifier" : ""); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; i = 0; entry = pmksa->pmksa; while (entry) { i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", i, MAC2STR(entry->aa)); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, PMKID_LEN); ret = os_snprintf(pos, buf + len - pos, " %d %d", (int) (entry->expiration - now.sec), entry->opportunistic); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; if (entry->fils_cache_id_set) { ret = os_snprintf(pos, buf + len - pos, " %02x%02x", entry->fils_cache_id[0], entry->fils_cache_id[1]); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, buf + len - pos, "\n"); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; entry = entry->next; } return pos - buf; } struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa) { return pmksa->pmksa; } /** * pmksa_cache_init - Initialize PMKSA cache * @free_cb: Callback function to be called when a PMKSA cache entry is freed * @ctx: Context pointer for free_cb function * @sm: Pointer to WPA state machine data from wpa_sm_init() * Returns: Pointer to PMKSA cache data or %NULL on failure */ struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { struct rsn_pmksa_cache *pmksa; pmksa = os_zalloc(sizeof(*pmksa)); if (pmksa) { pmksa->free_cb = free_cb; pmksa->ctx = ctx; pmksa->sm = sm; } return pmksa; } #endif /* IEEE8021X_EAPOL */ diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index 83faa05844d6..ae7bc13fa118 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -1,166 +1,168 @@ /* * wpa_supplicant - WPA2/RSN PMKSA cache functions * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef PMKSA_CACHE_H #define PMKSA_CACHE_H /** * struct rsn_pmksa_cache_entry - PMKSA cache entry */ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next; u8 pmkid[PMKID_LEN]; u8 pmk[PMK_LEN_MAX]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ u8 aa[ETH_ALEN]; /* * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA * cache entry is applicable to all BSSs (any BSSID/aa[]) that * advertise the same FILS Cache Identifier within the same ESS. */ u8 fils_cache_id[2]; unsigned int fils_cache_id_set:1; unsigned int dpp_pfs:1; os_time_t reauth_time; /** * network_ctx - Network configuration context * * This field is only used to match PMKSA cache entries to a specific * network configuration (e.g., a specific SSID and security policy). * This can be a pointer to the configuration entry, but PMKSA caching * code does not dereference the value and this could be any kind of * identifier. */ void *network_ctx; int opportunistic; + bool external; }; struct rsn_pmksa_cache; enum pmksa_free_reason { PMKSA_FREE, PMKSA_REPLACE, PMKSA_EXPIRE, }; #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx, int akmp); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp, const u8 *cache_id); struct rsn_pmksa_cache_entry * pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, int try_opportunistic, const u8 *fils_cache_id, int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa, int akmp); void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *pmk, size_t pmk_len); + const u8 *pmk, size_t pmk_len, bool external_only); #else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; } static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) { } static inline struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx, int akmp) { return NULL; } static inline struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) { return NULL; } static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) { return -1; } static inline struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa) { return NULL; } static inline struct rsn_pmksa_cache_entry * pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { return NULL; } static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp, const u8 *cache_id) { return NULL; } static inline void pmksa_cache_clear_current(struct wpa_sm *sm) { } static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, int try_opportunistic, const u8 *fils_cache_id, int akmp) { return -1; } static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *pmk, size_t pmk_len) + const u8 *pmk, size_t pmk_len, + bool external_only) { } #endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 78e2380b15e8..e01cd52177d2 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,5232 +1,5248 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2018, Jouni Malinen * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/aes.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/aes_siv.h" #include "crypto/sha256.h" #include "crypto/sha384.h" #include "crypto/sha512.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/ocv.h" #include "common/dpp.h" #include "common/wpa_ctrl.h" #include "eap_common/eap_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "drivers/driver.h" #include "wpa.h" #include "eloop.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ptk: PTK for Key Confirmation/Encryption Key * @ver: Version field from Key Info * @dest: Destination address for the frame * @proto: Ethertype (usually ETH_P_EAPOL) * @msg: EAPOL-Key message * @msg_len: Length of message * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written * Returns: >= 0 on success, < 0 on failure */ int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) { int ret = -1; size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR " ver=%d mic_len=%d key_mgmt=0x%x", MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt); if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { /* * Association event was not yet received; try to fetch * BSSID from the driver. */ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Failed to read BSSID for " "EAPOL-Key destination address"); } else { dest = sm->bssid; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Use BSSID (" MACSTR ") as the destination for EAPOL-Key", MAC2STR(dest)); } } if (mic_len) { if (key_mic && (!ptk || !ptk->kck_len)) goto out; if (key_mic && wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver, msg, msg_len, key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", ver, sm->key_mgmt); goto out; } if (ptk) wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); } else { #ifdef CONFIG_FILS /* AEAD cipher - Key MIC field not used */ struct ieee802_1x_hdr *s_hdr, *hdr; struct wpa_eapol_key *s_key, *key; u8 *buf, *s_key_data, *key_data; size_t buf_len = msg_len + AES_BLOCK_SIZE; size_t key_data_len; u16 eapol_len; const u8 *aad[1]; size_t aad_len[1]; if (!ptk || !ptk->kek_len) goto out; key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) - sizeof(struct wpa_eapol_key) - 2; buf = os_malloc(buf_len); if (!buf) goto out; os_memcpy(buf, msg, msg_len); hdr = (struct ieee802_1x_hdr *) buf; key = (struct wpa_eapol_key *) (hdr + 1); key_data = ((u8 *) (key + 1)) + 2; /* Update EAPOL header to include AES-SIV overhead */ eapol_len = be_to_host16(hdr->length); eapol_len += AES_BLOCK_SIZE; hdr->length = host_to_be16(eapol_len); /* Update Key Data Length field to include AES-SIV overhead */ WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len); s_hdr = (struct ieee802_1x_hdr *) msg; s_key = (struct wpa_eapol_key *) (s_hdr + 1); s_key_data = ((u8 *) (s_key + 1)) + 2; wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data", s_key_data, key_data_len); wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len); /* AES-SIV AAD from EAPOL protocol version field (inclusive) to * to Key Data (exclusive). */ aad[0] = buf; aad_len[0] = key_data - buf; if (aes_siv_encrypt(ptk->kek, ptk->kek_len, s_key_data, key_data_len, 1, aad, aad_len, key_data) < 0) { os_free(buf); goto out; } wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", key_data, AES_BLOCK_SIZE + key_data_len); os_free(msg); msg = buf; msg_len = buf_len; #else /* CONFIG_FILS */ goto out; #endif /* CONFIG_FILS */ } wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); out: os_free(msg); return ret; } /** * wpa_sm_key_request - Send EAPOL-Key Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @error: Indicate whether this is an Michael MIC error report * @pairwise: 1 = error report for pairwise packet, 0 = for group packet * * Send an EAPOL-Key Request to the current authenticator. This function is * used to request rekeying and it is usually called when a local Michael MIC * failure is detected. */ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic; if (pairwise && sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id && wpa_sm_get_state(sm) == WPA_COMPLETED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: PTK0 rekey not allowed, reconnecting"); wpa_sm_reconnect(sm); return; } if (wpa_use_akm_defined(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "Failed to read BSSID for EAPOL-Key request"); return; } mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) key_info |= WPA_KEY_INFO_SECURE; if (sm->ptk_set && mic_len) key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) key_info |= WPA_KEY_INFO_KEY_TYPE; WPA_PUT_BE16(reply->key_info, key_info); WPA_PUT_BE16(reply->key_length, 0); os_memcpy(reply->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); mic = (u8 *) (reply + 1); WPA_PUT_BE16(mic + mic_len, 0); if (!(key_info & WPA_KEY_INFO_MIC)) key_mic = NULL; else key_mic = mic; wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " "pairwise=%d ptk_set=%d len=%lu)", error, pairwise, sm->ptk_set, (unsigned long) rlen); wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); } static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm) { #ifdef CONFIG_IEEE80211R if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len)) wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cannot set low order 256 bits of MSK for key management offload"); } else { #endif /* CONFIG_IEEE80211R */ if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len)) wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cannot set PMK for key management offload"); #ifdef CONFIG_IEEE80211R } #endif /* CONFIG_IEEE80211R */ } static int wpa_supplicant_get_pmk(struct wpa_sm *sm, const unsigned char *src_addr, const u8 *pmkid) { int abort_cached = 0; if (pmkid && !sm->cur_pmksa) { /* When using drivers that generate RSN IE, wpa_supplicant may * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL, 0); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); } else { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no matching PMKID found"); abort_cached = 1; } } if (pmkid && sm->cur_pmksa && os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); wpa_sm_set_pmk_from_pmksa(sm); wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", sm->pmk, sm->pmk_len); eapol_sm_notify_cached(sm->eapol); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; #ifdef CONFIG_SAE if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE && sm->pmk_len == PMK_LEN) { /* Need to allow FT key derivation to proceed with * PMK from SAE being used as the XXKey in cases where * the PMKID in msg 1/4 matches the PMKSA entry that was * just added based on SAE authentication for the * initial mobility domain association. */ os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len); sm->xxkey_len = sm->pmk_len; } #endif /* CONFIG_SAE */ #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; #ifdef CONFIG_IEEE80211R u8 buf[2 * PMK_LEN]; #endif /* CONFIG_IEEE80211R */ if (wpa_key_mgmt_sha384(sm->key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len); if (res) { if (pmk_len == PMK_LEN) { /* * EAP-LEAP is an exception from other EAP * methods: it uses only 16-byte PMK. */ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); pmk_len = 16; } } #ifdef CONFIG_IEEE80211R if (res == 0 && eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) { if (wpa_key_mgmt_sha384(sm->key_mgmt)) { os_memcpy(sm->xxkey, buf, SHA384_MAC_LEN); sm->xxkey_len = SHA384_MAC_LEN; } else { os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); sm->xxkey_len = PMK_LEN; } forced_memzero(buf, sizeof(buf)); if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_ft(sm->key_mgmt)) { struct rsn_pmksa_cache_entry *sa = NULL; const u8 *fils_cache_id = NULL; #ifdef CONFIG_FILS if (sm->fils_cache_id_set) fils_cache_id = sm->fils_cache_id; #endif /* CONFIG_FILS */ wpa_hexdump_key(MSG_DEBUG, "FT: Cache XXKey/MPMK", sm->xxkey, sm->xxkey_len); sa = pmksa_cache_add(sm->pmksa, sm->xxkey, sm->xxkey_len, NULL, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt, fils_cache_id); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } } #endif /* CONFIG_IEEE80211R */ if (res == 0) { struct rsn_pmksa_cache_entry *sa = NULL; const u8 *fils_cache_id = NULL; #ifdef CONFIG_FILS if (sm->fils_cache_id_set) fils_cache_id = sm->fils_cache_id; #endif /* CONFIG_FILS */ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; wpa_supplicant_key_mgmt_set_pmk(sm); if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, NULL, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt, fils_cache_id); } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL, 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; } else if (sa && !sm->cur_pmksa && pmkid) { /* * It looks like the authentication server * derived mismatching MSK. This should not * really happen, but bugs happen.. There is not * much we can do here without knowing what * exactly caused the server to misbehave. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); return -1; } if (!sm->cur_pmksa) sm->cur_pmksa = sa; #ifdef CONFIG_IEEE80211R } else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) { wpa_printf(MSG_DEBUG, "FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol"); #endif /* CONFIG_IEEE80211R */ } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " "EAPOL state machines - key handshake " "aborted"); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cancelled PMKSA caching " "attempt"); sm->cur_pmksa = NULL; abort_cached = 1; } else if (!abort_cached) { return -1; } } } if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) { /* Send EAPOL-Start to trigger full EAP authentication. */ u8 *buf; size_t buflen; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no PMKSA entry found - trigger " "full EAP authentication"); buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, NULL, 0, &buflen, NULL); if (buf) { /* Set and reset eapFail to allow EAP state machine to * proceed with new authentication. */ eapol_sm_notify_eap_fail(sm->eapol, true); eapol_sm_notify_eap_fail(sm->eapol, false); wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, buf, buflen); os_free(buf); return -2; } return -1; } return 0; } /** * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Destination address for the frame * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @nonce: Nonce value for the EAPOL-Key frame * @wpa_ie: WPA/RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @ptk: PTK to use for keyed hash and encryption * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; u8 *rbuf, *key_mic; u8 *rsn_ie_buf = NULL; u16 key_info; if (wpa_ie == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " "cannot generate msg 2/4"); return -1; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { int res; wpa_hexdump(MSG_DEBUG, "WPA: WPA IE before FT processing", wpa_ie, wpa_ie_len); /* * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and * FTIE from (Re)Association Response. */ rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN + sm->assoc_resp_ies_len); if (rsn_ie_buf == NULL) return -1; os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len, sm->pmk_r1_name); if (res < 0) { os_free(rsn_ie_buf); return -1; } wpa_hexdump(MSG_DEBUG, "WPA: WPA IE after PMKID[PMKR1Name] addition into RSNE", rsn_ie_buf, wpa_ie_len); if (sm->assoc_resp_ies) { wpa_hexdump(MSG_DEBUG, "WPA: Add assoc_resp_ies", sm->assoc_resp_ies, sm->assoc_resp_ies_len); os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, sm->assoc_resp_ies_len); wpa_ie_len += sm->assoc_resp_ies_len; } wpa_ie = rsn_ie_buf; } #endif /* CONFIG_IEEE80211R */ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + wpa_ie_len, &rlen, (void *) &reply); if (rbuf == NULL) { os_free(rsn_ie_buf); return -1; } reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = ver | WPA_KEY_INFO_KEY_TYPE; if (mic_len) key_info |= WPA_KEY_INFO_MIC; else key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); key_mic = (u8 *) (reply + 1); WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */ os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */ os_free(rsn_ie_buf); os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen, key_mic); } static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { const u8 *z = NULL; size_t z_len = 0, kdk_len; int akmp; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_DPP2 if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { z = wpabuf_head(sm->dpp_z); z_len = wpabuf_len(sm->dpp_z); } #endif /* CONFIG_DPP2 */ akmp = sm->key_mgmt; #ifdef CONFIG_OWE if (sm->owe_ptk_workaround && akmp == WPA_KEY_MGMT_OWE && sm->pmk_len > 32) { wpa_printf(MSG_DEBUG, "OWE: Force SHA256 for PTK derivation"); akmp |= WPA_KEY_MGMT_PSK_SHA256; } #endif /* CONFIG_OWE */ if (sm->force_kdk_derivation || (sm->secure_ltf && ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))) kdk_len = WPA_KDK_MAX_LEN; else kdk_len = 0; return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, ptk, akmp, sm->pairwise_cipher, z, z_len, kdk_len); } static int wpa_handle_ext_key_id(struct wpa_sm *sm, struct wpa_eapol_ie_parse *kde) { if (sm->ext_key_id) { u16 key_id; if (!kde->key_id) { wpa_msg(sm->ctx->msg_ctx, sm->use_ext_key_id ? MSG_INFO : MSG_DEBUG, "RSN: No Key ID in Extended Key ID handshake"); sm->keyidx_active = 0; return sm->use_ext_key_id ? -1 : 0; } key_id = kde->key_id[0] & 0x03; if (key_id > 1) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Invalid Extended Key ID: %d", key_id); return -1; } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Using Extended Key ID %d", key_id); sm->keyidx_active = key_id; sm->use_ext_key_id = 1; } else { if (kde->key_id && (kde->key_id[0] & 0x03)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Non-zero Extended Key ID Key ID in PTK0 handshake"); return -1; } if (kde->key_id) { /* This is not supposed to be included here, but ignore * the case of matching Key ID 0 just in case. */ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Extended Key ID Key ID 0 in PTK0 handshake"); } sm->keyidx_active = 0; sm->use_ext_key_id = 0; } return 0; } static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; int res; u8 *kde, *kde_buf = NULL; size_t kde_len; if (wpa_sm_get_network_ctx(sm) == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " "found (msg 1 of 4)"); return; } if (sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id && wpa_sm_get_state(sm) == WPA_COMPLETED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: PTK0 rekey not allowed, reconnecting"); wpa_sm_reconnect(sm); return; } wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len); if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); } } res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " "msg 1/4 - requesting full EAP authentication"); return; } if (res) goto failed; if (sm->renew_snonce) { if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; } sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", sm->snonce, WPA_NONCE_LEN); } /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0) goto failed; if (sm->pairwise_cipher == WPA_CIPHER_TKIP) { u8 buf[8]; /* Supplicant: swap tx/rx Mic keys */ os_memcpy(buf, &ptk->tk[16], 8); os_memcpy(&ptk->tk[16], &ptk->tk[24], 8); os_memcpy(&ptk->tk[24], buf, 8); forced_memzero(buf, sizeof(buf)); } sm->tptk_set = 1; kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3 + sm->assoc_rsnxe_len + 2 + RSN_SELECTOR_LEN + 1 + 2 + RSN_SELECTOR_LEN + 2); if (!kde_buf) goto failed; os_memcpy(kde_buf, kde, kde_len); kde = kde_buf; #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; u8 *pos; pos = kde + kde_len; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info for OCI element in EAPOL-Key 2/4"); goto failed; } #ifdef CONFIG_TESTING_OPTIONS if (sm->oci_freq_override_eapol) { wpa_printf(MSG_INFO, "TEST: Override OCI KDE frequency %d -> %d MHz", ci.frequency, sm->oci_freq_override_eapol); ci.frequency = sm->oci_freq_override_eapol; } #endif /* CONFIG_TESTING_OPTIONS */ if (ocv_insert_oci_kde(&ci, &pos) < 0) goto failed; kde_len = pos - kde; } #endif /* CONFIG_OCV */ if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) { os_memcpy(kde + kde_len, sm->assoc_rsnxe, sm->assoc_rsnxe_len); kde_len += sm->assoc_rsnxe_len; } #ifdef CONFIG_P2P if (sm->p2p) { u8 *pos; wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE into EAPOL-Key 2/4"); pos = kde + kde_len; *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + 1; RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); pos += RSN_SELECTOR_LEN; *pos++ = 0x01; kde_len = pos - kde; } #endif /* CONFIG_P2P */ #ifdef CONFIG_DPP2 if (DPP_VERSION > 1 && sm->key_mgmt == WPA_KEY_MGMT_DPP) { u8 *pos; wpa_printf(MSG_DEBUG, "DPP: Add DPP KDE into EAPOL-Key 2/4"); pos = kde + kde_len; *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + 2; RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_DPP); pos += RSN_SELECTOR_LEN; *pos++ = DPP_VERSION; /* Protocol Version */ *pos = 0; /* Flags */ if (sm->dpp_pfs == 0) *pos |= DPP_KDE_PFS_ALLOWED; else if (sm->dpp_pfs == 1) *pos |= DPP_KDE_PFS_ALLOWED | DPP_KDE_PFS_REQUIRED; pos++; kde_len = pos - kde; } #endif /* CONFIG_DPP2 */ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, kde, kde_len, ptk) < 0) goto failed; os_free(kde_buf); os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: os_free(kde_buf); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; rsn_preauth_candidate_process(sm); } static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, const u8 *addr, int secure) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Key negotiation completed with " MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); if (secure) { wpa_sm_mlme_setprotection( sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, true); if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) || sm->key_mgmt == WPA_KEY_MGMT_DPP || sm->key_mgmt == WPA_KEY_MGMT_OWE) eapol_sm_notify_eap_success(sm->eapol, true); /* * Start preauthentication after a short wait to avoid a * possible race condition between the data receive and key * configuration after the 4-Way Handshake. This increases the * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ if (!dl_list_empty(&sm->pmksa_candidates)) eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Authenticator accepted " "opportunistic PMKSA entry - marking it valid"); sm->cur_pmksa->opportunistic = 0; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); } #endif /* CONFIG_IEEE80211R */ } static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); wpa_sm_key_request(sm, 0, 1); } static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_key *key, enum key_flag key_flag) { int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; if (sm->ptk.installed) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Do not re-install same PTK to the driver"); return 0; } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing PTK to the driver"); if (sm->pairwise_cipher == WPA_CIPHER_NONE) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " "Suite: NONE - do not use pairwise keys"); return 0; } if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) { wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu", keylen, (long unsigned int) sm->ptk.tk_len); return -1; } rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); } if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc, rsclen, sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set PTK to the driver (alg=%d keylen=%d bssid=" MACSTR " idx=%d key_flag=0x%x)", alg, keylen, MAC2STR(sm->bssid), sm->keyidx_active, key_flag); return -1; } wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, sm->dot11RSNAConfigPMKLifetime, &sm->ptk); /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); sm->ptk.tk_len = 0; sm->ptk.installed = 1; if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, sm, NULL); } return 0; } static int wpa_supplicant_activate_ptk(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Activate PTK (idx=%d bssid=" MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid)); if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to activate PTK for TX (idx=%d bssid=" MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid)); return -1; } return 0; } static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, int group_cipher, int keylen, int maxkeylen, int *key_rsc_len, enum wpa_alg *alg) { int klen; *alg = wpa_cipher_to_alg(group_cipher); if (*alg == WPA_ALG_NONE) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported Group Cipher %d", group_cipher); return -1; } *key_rsc_len = wpa_cipher_rsc_len(group_cipher); klen = wpa_cipher_key_len(group_cipher); if (keylen != klen || maxkeylen < klen) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported %s Group Cipher key length %d (%d)", wpa_cipher_txt(group_cipher), keylen, maxkeylen); return -1; } return 0; } struct wpa_gtk_data { enum wpa_alg alg; int tx, key_rsc_len, keyidx; u8 gtk[32]; int gtk_len; }; static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_data *gd, const u8 *key_rsc, int wnm_sleep) { const u8 *_gtk = gd->gtk; u8 gtk_buf[32]; /* Detect possible key reinstallation */ if ((sm->gtk.gtk_len == (size_t) gd->gtk_len && os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) || (sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len && os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk, sm->gtk_wnm_sleep.gtk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)", gd->keyidx, gd->tx, gd->gtk_len); return 0; } wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", gd->keyidx, gd->tx, gd->gtk_len); wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ os_memcpy(gtk_buf, gd->gtk, 16); os_memcpy(gtk_buf + 16, gd->gtk + 24, 8); os_memcpy(gtk_buf + 24, gd->gtk + 16, 8); _gtk = gtk_buf; } if (sm->pairwise_cipher == WPA_CIPHER_NONE) { if (wpa_sm_set_key(sm, gd->alg, NULL, gd->keyidx, 1, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len, KEY_FLAG_GROUP_RX_TX_DEFAULT) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to the driver " "(Group only)"); forced_memzero(gtk_buf, sizeof(gtk_buf)); return -1; } } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to " "the driver (alg=%d keylen=%d keyidx=%d)", gd->alg, gd->gtk_len, gd->keyidx); forced_memzero(gtk_buf, sizeof(gtk_buf)); return -1; } forced_memzero(gtk_buf, sizeof(gtk_buf)); if (wnm_sleep) { sm->gtk_wnm_sleep.gtk_len = gd->gtk_len; os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk, sm->gtk_wnm_sleep.gtk_len); } else { sm->gtk.gtk_len = gd->gtk_len; os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len); } return 0; } static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, int tx) { if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { /* Ignore Tx bit for GTK if a pairwise key is used. One AP * seemed to set this bit (incorrectly, since Tx is only when * doing Group Key only APs) and without this workaround, the * data connection does not work because wpa_supplicant * configured non-zero keyidx to be used for unicast. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Tx bit set for GTK, but pairwise " "keys are used - ignore Tx bit"); return 0; } return tx; } static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, const u8 *rsc) { int rsclen; if (!sm->wpa_rsc_relaxation) return 0; rsclen = wpa_cipher_rsc_len(sm->group_cipher); /* * Try to detect RSC (endian) corruption issue where the AP sends * the RSC bytes in EAPOL-Key message in the wrong order, both if * it's actually a 6-byte field (as it should be) and if it treats * it as an 8-byte field. * An AP model known to have this bug is the Sapido RB-1632. */ if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0", rsc[0], rsc[1], rsc[2], rsc[3], rsc[4], rsc[5], rsc[6], rsc[7]); return 1; } return 0; } static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; const u8 *key_rsc; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x * GTK KDE format: * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] * Reserved [bits 0-7] * GTK */ os_memset(&gd, 0, sizeof(gd)); wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", gtk, gtk_len); if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) return -1; gd.keyidx = gtk[0] & 0x3; gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(gtk[0] & BIT(2))); gtk += 2; gtk_len -= 2; os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; key_rsc = key->key_rsc; if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) key_rsc = null_rsc; if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); forced_memzero(&gd, sizeof(gd)); return -1; } forced_memzero(&gd, sizeof(gd)); return 0; } static int wpa_supplicant_install_igtk(struct wpa_sm *sm, const struct wpa_igtk_kde *igtk, int wnm_sleep) { size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); u16 keyidx = WPA_GET_LE16(igtk->keyid); /* Detect possible key reinstallation */ if ((sm->igtk.igtk_len == len && os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) || (sm->igtk_wnm_sleep.igtk_len == len && os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk, sm->igtk_wnm_sleep.igtk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)", keyidx); return 0; } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d pn " COMPACT_MACSTR, keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len); if (keyidx > 4095) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid IGTK KeyID %d", keyidx); return -1; } if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, len, KEY_FLAG_GROUP_RX) < 0) { if (keyidx == 0x0400 || keyidx == 0x0500) { /* Assume the AP has broken PMF implementation since it * seems to have swapped the KeyID bytes. The AP cannot * be trusted to implement BIP correctly or provide a * valid IGTK, so do not try to configure this key with * swapped KeyID bytes. Instead, continue without * configuring the IGTK so that the driver can drop any * received group-addressed robust management frames due * to missing keys. * * Normally, this error behavior would result in us * disconnecting, but there are number of deployed APs * with this broken behavior, so as an interoperability * workaround, allow the connection to proceed. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Ignore IGTK configuration error due to invalid IGTK KeyID byte order"); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to configure IGTK to the driver"); return -1; } } if (wnm_sleep) { sm->igtk_wnm_sleep.igtk_len = len; os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk, sm->igtk_wnm_sleep.igtk_len); } else { sm->igtk.igtk_len = len; os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len); } return 0; } static int wpa_supplicant_install_bigtk(struct wpa_sm *sm, const struct wpa_bigtk_kde *bigtk, int wnm_sleep) { size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher); u16 keyidx = WPA_GET_LE16(bigtk->keyid); /* Detect possible key reinstallation */ if ((sm->bigtk.bigtk_len == len && os_memcmp(sm->bigtk.bigtk, bigtk->bigtk, sm->bigtk.bigtk_len) == 0) || (sm->bigtk_wnm_sleep.bigtk_len == len && os_memcmp(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk, sm->bigtk_wnm_sleep.bigtk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Not reinstalling already in-use BIGTK to the driver (keyidx=%d)", keyidx); return 0; } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: BIGTK keyid %d pn " COMPACT_MACSTR, keyidx, MAC2STR(bigtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK", bigtk->bigtk, len); if (keyidx < 6 || keyidx > 7) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid BIGTK KeyID %d", keyidx); return -1; } if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), broadcast_ether_addr, keyidx, 0, bigtk->pn, sizeof(bigtk->pn), bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to configure BIGTK to the driver"); return -1; } if (wnm_sleep) { sm->bigtk_wnm_sleep.bigtk_len = len; os_memcpy(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk, sm->bigtk_wnm_sleep.bigtk_len); } else { sm->bigtk.bigtk_len = len; os_memcpy(sm->bigtk.bigtk, bigtk->bigtk, sm->bigtk.bigtk_len); } return 0; } static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { size_t len; if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) || sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED) return 0; if (ie->igtk) { const struct wpa_igtk_kde *igtk; len = wpa_cipher_key_len(sm->mgmt_group_cipher); if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len) return -1; igtk = (const struct wpa_igtk_kde *) ie->igtk; if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0) return -1; } if (ie->bigtk && sm->beacon_prot) { const struct wpa_bigtk_kde *bigtk; len = wpa_cipher_key_len(sm->mgmt_group_cipher); if (ie->bigtk_len != WPA_BIGTK_KDE_PREFIX_LEN + len) return -1; bigtk = (const struct wpa_bigtk_kde *) ie->bigtk; if (wpa_supplicant_install_bigtk(sm, bigtk, 0) < 0) return -1; } return 0; } static void wpa_report_ie_mismatch(struct wpa_sm *sm, const char *reason, const u8 *src_addr, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsn_ie, size_t rsn_ie_len) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", reason, MAC2STR(src_addr)); if (sm->ap_wpa_ie) { wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", sm->ap_wpa_ie, sm->ap_wpa_ie_len); } if (wpa_ie) { if (!sm->ap_wpa_ie) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No WPA IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", wpa_ie, wpa_ie_len); } if (sm->ap_rsn_ie) { wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", sm->ap_rsn_ie, sm->ap_rsn_ie_len); } if (rsn_ie) { if (!sm->ap_rsn_ie) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No RSN IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", rsn_ie, rsn_ie_len); } wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); } #ifdef CONFIG_IEEE80211R static int ft_validate_mdie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie, const u8 *assoc_resp_mdie) { struct rsn_mdie *mdie; mdie = (struct rsn_mdie *) (ie->mdie + 2); if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " "not match with the current mobility domain"); return -1; } if (assoc_resp_mdie && (assoc_resp_mdie[1] != ie->mdie[1] || os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", ie->mdie, 2 + ie->mdie[1]); wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", assoc_resp_mdie, 2 + assoc_resp_mdie[1]); return -1; } return 0; } static int ft_validate_ftie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie, const u8 *assoc_resp_ftie) { if (ie->ftie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4"); return -1; } if (assoc_resp_ftie == NULL) return 0; if (assoc_resp_ftie[1] != ie->ftie[1] || os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", ie->ftie, 2 + ie->ftie[1]); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", assoc_resp_ftie, 2 + assoc_resp_ftie[1]); return -1; } return 0; } static int ft_validate_rsnie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { struct wpa_ie_data rsn; if (!ie->rsn_ie) return 0; /* * Verify that PMKR1Name from EAPOL-Key message 3/4 * matches with the value we derived. */ if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || rsn.num_pmkid != 1 || rsn.pmkid == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " "FT 4-way handshake message 3/4"); return -1; } if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: PMKR1Name mismatch in " "FT 4-way handshake message 3/4"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", rsn.pmkid, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); return -1; } return 0; } static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { const u8 *pos, *end, *mdie = NULL, *ftie = NULL; if (sm->assoc_resp_ies) { pos = sm->assoc_resp_ies; end = pos + sm->assoc_resp_ies_len; while (end - pos > 2) { if (2 + pos[1] > end - pos) break; switch (*pos) { case WLAN_EID_MOBILITY_DOMAIN: mdie = pos; break; case WLAN_EID_FAST_BSS_TRANSITION: ftie = pos; break; } pos += 2 + pos[1]; } } if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 || ft_validate_ftie(sm, src_addr, ie, ftie) < 0 || ft_validate_rsnie(sm, src_addr, ie) < 0) return -1; return 0; } #endif /* CONFIG_IEEE80211R */ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) { if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " "Trying to get from scan results"); if (wpa_sm_get_beacon_ie(sm) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Could not find AP from " "the scan results"); return -1; } wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Found the current AP from updated scan results"); } if (ie->wpa_ie == NULL && ie->rsn_ie == NULL && (sm->ap_wpa_ie || sm->ap_rsn_ie)) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp (no IE?)", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if ((ie->wpa_ie && sm->ap_wpa_ie && (ie->wpa_ie_len != sm->ap_wpa_ie_len || os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || (ie->rsn_ie && sm->ap_rsn_ie && wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), sm->ap_rsn_ie, sm->ap_rsn_ie_len, ie->rsn_ie, ie->rsn_ie_len))) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if (sm->proto == WPA_PROTO_WPA && ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) { wpa_report_ie_mismatch(sm, "Possible downgrade attack " "detected - RSN was enabled and RSN IE " "was in msg 3/4, but not in " "Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, ie->rsn_ie, ie->rsn_ie_len); return -1; } if (sm->proto == WPA_PROTO_RSN && ((sm->ap_rsnxe && !ie->rsnxe) || (!sm->ap_rsnxe && ie->rsnxe) || (sm->ap_rsnxe && ie->rsnxe && (sm->ap_rsnxe_len != ie->rsnxe_len || os_memcmp(sm->ap_rsnxe, ie->rsnxe, sm->ap_rsnxe_len) != 0)))) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4"); wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp", sm->ap_rsnxe, sm->ap_rsnxe_len); wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4", ie->rsnxe, ie->rsnxe_len); wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); return -1; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt) && wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) return -1; #endif /* CONFIG_IEEE80211R */ return 0; } /** * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake * @sm: Pointer to WPA state machine data from wpa_sm_init() * @dst: Destination address for the frame * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info * @ptk: PTK to use for keyed hash and encryption * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, struct wpa_ptk *ptk) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; u8 *rbuf, *key_mic; mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_SECURE; key_info |= ver | WPA_KEY_INFO_KEY_TYPE; if (mic_len) key_info |= WPA_KEY_INFO_MIC; else key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); key_mic = (u8 *) (reply + 1); WPA_PUT_BE16(key_mic + mic_len, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen, key_mic); } static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, size_t key_data_len) { u16 key_info, keylen; struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way " "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len); if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) goto failed; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: GTK IE in unencrypted key data"); goto failed; } if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: IGTK KDE in unencrypted key data"); goto failed; } if (ie.igtk && sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED && wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) && ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN + (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", (unsigned long) ie.igtk_len); goto failed; } if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) goto failed; if (wpa_handle_ext_key_id(sm, &ie)) goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: ANonce from message 1 of 4-Way Handshake " "differs from 3 of 4-Way Handshake - drop packet (src=" MACSTR ")", MAC2STR(sm->bssid)); goto failed; } keylen = WPA_GET_BE16(key->key_length); if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid %s key length %d (src=" MACSTR ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, MAC2STR(sm->bssid)); goto failed; } #ifdef CONFIG_P2P if (ie.ip_addr_alloc) { os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); wpa_hexdump(MSG_DEBUG, "P2P: IP address info", sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); } #endif /* CONFIG_P2P */ #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "Failed to get channel info to validate received OCI in EAPOL-Key 3/4"); return; } if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci, channel_width_to_int(ci.chanwidth), ci.seg1_idx) != OCI_SUCCESS) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" MACSTR " frame=eapol-key-m3 error=%s", MAC2STR(sm->bssid), ocv_errorstr); return; } } #endif /* CONFIG_OCV */ #ifdef CONFIG_DPP2 if (DPP_VERSION > 1 && ie.dpp_kde) { wpa_printf(MSG_DEBUG, "DPP: peer Protocol Version %u Flags 0x%x", ie.dpp_kde[0], ie.dpp_kde[1]); if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_pfs != 2 && (ie.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) && !sm->dpp_z) { wpa_printf(MSG_INFO, "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association"); goto failed; } } #endif /* CONFIG_DPP2 */ if (sm->use_ext_key_id && wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX)) goto failed; if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, &sm->ptk) < 0) { goto failed; } /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old * SNonce will still be used to avoid changing PTK. */ sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { int res; if (sm->use_ext_key_id) res = wpa_supplicant_activate_ptk(sm); else res = wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX); if (res) goto failed; } if (key_info & WPA_KEY_INFO_SECURE) { wpa_sm_mlme_setprotection( sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, true); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { /* No GTK to be set to the driver */ } else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: No GTK KDE included in EAPOL-Key msg 3/4"); goto failed; } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure GTK"); goto failed; } if (ieee80211w_set_keys(sm, &ie) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure IGTK"); goto failed; } if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk) wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); if (ie.gtk) wpa_sm_set_rekey_offload(sm); /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be * calculated only after KCK has been derived. Though, do not replace an * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID) * to avoid unnecessary changes of PMKID while continuing to use the * same PMK. */ if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) && !sm->cur_pmksa) { struct rsn_pmksa_cache_entry *sa; sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, sm->ptk.kck, sm->ptk.kck_len, sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, NULL); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } if (ie.transition_disable) wpa_sm_transition_disable(sm, ie.transition_disable[0]); sm->msg_3_of_4_ok = 1; return; failed: wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, const u8 *keydata, size_t keydatalen, u16 key_info, struct wpa_gtk_data *gd) { int maxkeylen; struct wpa_eapol_ie_parse ie; u16 gtk_len; wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: GTK IE in unencrypted key data"); return -1; } if (ie.gtk == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); return -1; } gtk_len = ie.gtk_len; if (gtk_len < 2) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2", gtk_len); return -1; } gtk_len -= 2; if (gtk_len > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Too long GTK in GTK KDE (len=%u)", gtk_len); return -1; } maxkeylen = gd->gtk_len = gtk_len; #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2"); return -1; } if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci, channel_width_to_int(ci.chanwidth), ci.seg1_idx) != OCI_SUCCESS) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" MACSTR " frame=eapol-key-g1 error=%s", MAC2STR(sm->bssid), ocv_errorstr); return -1; } } #endif /* CONFIG_OCV */ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake", ie.gtk, 2 + gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); os_memcpy(gd->gtk, ie.gtk + 2, gtk_len); if (ieee80211w_set_keys(sm, &ie) < 0) wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Failed to configure IGTK"); return 0; } static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len, u16 key_info, u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; u16 gtk_len; gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = key_data_len; if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen < 8) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Too short maxkeylen (%lu)", (unsigned long) maxkeylen); return -1; } maxkeylen -= 8; } if (gtk_len > maxkeylen || wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; gd->gtk_len = gtk_len; gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { #ifdef CONFIG_NO_RC4 wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 not supported in the build"); return -1; #else /* CONFIG_NO_RC4 */ u8 ek[32]; if (key_data_len > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 key data too long (%lu)", (unsigned long) key_data_len); return -1; } os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); os_memcpy(gd->gtk, key_data, key_data_len); if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) { forced_memzero(ek, sizeof(ek)); wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } forced_memzero(ek, sizeof(ek)); #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %lu", (unsigned long) maxkeylen); return -1; } if (maxkeylen > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES-WRAP key data " "too long (keydatalen=%lu maxkeylen=%lu)", (unsigned long) key_data_len, (unsigned long) maxkeylen); return -1; } if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8, key_data, gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - could not decrypt " "GTK"); return -1; } } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); return -1; } gd->tx = wpa_supplicant_gtk_tx_bit_workaround( sm, !!(key_info & WPA_KEY_INFO_TXRX)); return 0; } static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, const struct wpa_eapol_key *key, int ver, u16 key_info) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; u8 *rbuf, *key_mic; size_t kde_len = 0; #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) kde_len = OCV_OCI_KDE_LEN; #endif /* CONFIG_OCV */ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; key_info |= ver | WPA_KEY_INFO_SECURE; if (mic_len) key_info |= WPA_KEY_INFO_MIC; else key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); key_mic = (u8 *) (reply + 1); WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data Length */ #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; u8 *pos; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info for OCI element in EAPOL-Key 2/2"); os_free(rbuf); return -1; } #ifdef CONFIG_TESTING_OPTIONS if (sm->oci_freq_override_eapol_g2) { wpa_printf(MSG_INFO, "TEST: Override OCI KDE frequency %d -> %d MHz", ci.frequency, sm->oci_freq_override_eapol_g2); ci.frequency = sm->oci_freq_override_eapol_g2; } #endif /* CONFIG_TESTING_OPTIONS */ pos = key_mic + mic_len + 2; /* Key Data */ if (ocv_insert_oci_kde(&ci, &pos) < 0) { os_free(rbuf); return -1; } } #endif /* CONFIG_OCV */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); } static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, const u8 *key_data, size_t key_data_len, u16 ver) { u16 key_info; int rekey, ret; struct wpa_gtk_data gd; const u8 *key_rsc; if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; } os_memset(&gd, 0, sizeof(gd)); rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key " "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data, key_data_len, key_info, &gd); } else { ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data, key_data_len, key_info, ver, &gd); } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (ret) goto failed; key_rsc = key->key_rsc; if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) key_rsc = null_rsc; if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0) goto failed; forced_memzero(&gd, sizeof(gd)); if (rekey) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying " "completed with " MACSTR " [GTK=%s]", MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } wpa_sm_set_rekey_offload(sm); return; failed: forced_memzero(&gd, sizeof(gd)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; int ok = 0; size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); os_memcpy(mic, key + 1, mic_len); if (sm->tptk_set) { os_memset(key + 1, 0, mic_len); if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt, ver, buf, len, (u8 *) (key + 1)) < 0 || os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); #ifdef TEST_FUZZ wpa_printf(MSG_INFO, "TEST: Ignore Key MIC failure for fuzz testing"); goto continue_fuzz; #endif /* TEST_FUZZ */ } else { #ifdef TEST_FUZZ continue_fuzz: #endif /* TEST_FUZZ */ ok = 1; sm->tptk_set = 0; sm->ptk_set = 1; os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); /* * This assures the same TPTK in sm->tptk can never be * copied twice to sm->ptk as the new PTK. In * combination with the installed flag in the wpa_ptk * struct, this assures the same PTK is only installed * once. */ sm->renew_snonce = 1; } } if (!ok && sm->ptk_set) { os_memset(key + 1, 0, mic_len); if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt, ver, buf, len, (u8 *) (key + 1)) < 0 || os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); #ifdef TEST_FUZZ wpa_printf(MSG_INFO, "TEST: Ignore Key MIC failure for fuzz testing"); goto continue_fuzz2; #endif /* TEST_FUZZ */ return -1; } #ifdef TEST_FUZZ continue_fuzz2: #endif /* TEST_FUZZ */ ok = 1; } if (!ok) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC - " "dropping packet"); return -1; } os_memcpy(sm->rx_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 1; return 0; } /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, struct wpa_eapol_key *key, size_t mic_len, u16 ver, u8 *key_data, size_t *key_data_len) { wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", key_data, *key_data_len); if (!sm->ptk_set) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: PTK not available, cannot decrypt EAPOL-Key Key " "Data"); return -1; } /* Decrypt key data here so that this operation does not need * to be implemented separately for each message type. */ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { #ifdef CONFIG_NO_RC4 wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: RC4 not supported in the build"); return -1; #else /* CONFIG_NO_RC4 */ u8 ek[32]; wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4"); os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { forced_memzero(ek, sizeof(ek)); wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } forced_memzero(ek, sizeof(ek)); #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || wpa_use_aes_key_wrap(sm->key_mgmt)) { u8 *buf; wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)", (unsigned int) sm->ptk.kek_len); if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %u", (unsigned int) *key_data_len); return -1; } *key_data_len -= 8; /* AES-WRAP adds 8 bytes */ buf = os_malloc(*key_data_len); if (buf == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No memory for AES-UNWRAP buffer"); return -1; } #ifdef TEST_FUZZ os_memset(buf, 0x11, *key_data_len); #endif /* TEST_FUZZ */ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { #ifdef TEST_FUZZ wpa_printf(MSG_INFO, "TEST: Ignore AES unwrap failure for fuzz testing"); goto continue_fuzz; #endif /* TEST_FUZZ */ bin_clear_free(buf, *key_data_len); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - " "could not decrypt EAPOL-Key key data"); return -1; } #ifdef TEST_FUZZ continue_fuzz: #endif /* TEST_FUZZ */ os_memcpy(key_data, buf, *key_data_len); bin_clear_free(buf, *key_data_len); WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", key_data, *key_data_len); return 0; } /** * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void wpa_sm_aborted_cached(struct wpa_sm *sm) { if (sm && sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); sm->cur_pmksa = NULL; } } +void wpa_sm_aborted_external_cached(struct wpa_sm *sm) +{ + if (sm && sm->cur_pmksa && sm->cur_pmksa->external) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelling external PMKSA caching attempt"); + sm->cur_pmksa = NULL; + } +} + + static void wpa_eapol_key_dump(struct wpa_sm *sm, const struct wpa_eapol_key *key, unsigned int key_data_len, const u8 *mic, unsigned int mic_len) { #ifndef CONFIG_NO_STDOUT_DEBUG u16 key_info = WPA_GET_BE16(key->key_info); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)", key_info, key_info & WPA_KEY_INFO_TYPE_MASK, (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT, (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", key_info & WPA_KEY_INFO_ACK ? " Ack" : "", key_info & WPA_KEY_INFO_MIC ? " MIC" : "", key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", key_info & WPA_KEY_INFO_ERROR ? " Error" : "", key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " key_length=%u key_data_length=%u", WPA_GET_BE16(key->key_length), key_data_len); wpa_hexdump(MSG_DEBUG, " replay_counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16); wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8); wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8); wpa_hexdump(MSG_DEBUG, " key_mic", mic, mic_len); #endif /* CONFIG_NO_STDOUT_DEBUG */ } #ifdef CONFIG_FILS static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len, size_t *key_data_len) { struct wpa_ptk *ptk; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u8 *pos, *tmp; const u8 *aad[1]; size_t aad_len[1]; if (*key_data_len < AES_BLOCK_SIZE) { wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame"); return -1; } if (sm->tptk_set) ptk = &sm->tptk; else if (sm->ptk_set) ptk = &sm->ptk; else return -1; hdr = (struct ieee802_1x_hdr *) buf; key = (struct wpa_eapol_key *) (hdr + 1); pos = (u8 *) (key + 1); pos += 2; /* Pointing at the Encrypted Key Data field */ tmp = os_malloc(*key_data_len); if (!tmp) return -1; /* AES-SIV AAD from EAPOL protocol version field (inclusive) to * to Key Data (exclusive). */ aad[0] = buf; aad_len[0] = pos - buf; if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len, 1, aad, aad_len, tmp) < 0) { wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame"); bin_clear_free(tmp, *key_data_len); return -1; } /* AEAD decryption and validation completed successfully */ (*key_data_len) -= AES_BLOCK_SIZE; wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", tmp, *key_data_len); /* Replace Key Data field with the decrypted version */ os_memcpy(pos, tmp, *key_data_len); pos -= 2; /* Key Data Length field */ WPA_PUT_BE16(pos, *key_data_len); bin_clear_free(tmp, *key_data_len); if (sm->tptk_set) { sm->tptk_set = 0; sm->ptk_set = 1; os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); } os_memcpy(sm->rx_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 1; return 0; } #endif /* CONFIG_FILS */ /** * wpa_sm_rx_eapol - Process received WPA EAPOL frames * @sm: Pointer to WPA state machine data from wpa_sm_init() * @src_addr: Source MAC address of the EAPOL packet * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure * * This function is called for each received EAPOL frame. Other than EAPOL-Key * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is * only processing WPA and WPA2 EAPOL-Key frames. * * The received EAPOL-Key packets are validated and valid packets are replied * to. In addition, key material (PTK, GTK) is configured at the end of a * successful key handshake. */ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { size_t plen, data_len, key_data_len; const struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, ver; u8 *tmp = NULL; int ret = -1; u8 *mic, *key_data; size_t mic_len, keyhdrlen, pmk_len; #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ pmk_len = sm->pmk_len; if (!pmk_len && sm->cur_pmksa) pmk_len = sm->cur_pmksa->pmk_len; mic_len = wpa_mic_len(sm->key_mgmt, pmk_len); keyhdrlen = sizeof(*key) + mic_len + 2; if (len < sizeof(*hdr) + keyhdrlen) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " "EAPOL-Key (len %lu, expecting at least %lu)", (unsigned long) len, (unsigned long) sizeof(*hdr) + keyhdrlen); return 0; } hdr = (const struct ieee802_1x_hdr *) buf; plen = be_to_host16(hdr->length); data_len = plen + sizeof(*hdr); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", hdr->version, hdr->type, (unsigned long) plen); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " "not a Key frame", hdr->type); ret = 0; goto out; } wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len); if (plen > len - sizeof(*hdr) || plen < keyhdrlen) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL frame payload size %lu " "invalid (frame size %lu)", (unsigned long) plen, (unsigned long) len); ret = 0; goto out; } if (data_len < len) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE 802.1X data", (unsigned long) len - data_len); } /* * Make a copy of the frame since we need to modify the buffer during * MAC validation and Key Data decryption. */ tmp = os_memdup(buf, data_len); if (tmp == NULL) goto out; key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr)); mic = (u8 *) (key + 1); key_data = mic + mic_len + 2; if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, discarded", key->type); ret = 0; goto out; } key_data_len = WPA_GET_BE16(mic + mic_len); wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len); if (key_data_len > plen - keyhdrlen) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " "frame - key_data overflow (%u > %u)", (unsigned int) key_data_len, (unsigned int) (plen - keyhdrlen)); goto out; } eapol_sm_notify_lower_layer_success(sm->eapol, 0); key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } if (wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", ver); goto out; } #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FT: AP did not use AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211R */ if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); goto out; } } else if (sm->pairwise_cipher == WPA_CIPHER_CCMP && !wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2", ver); if (sm->group_cipher != WPA_CIPHER_CCMP && !(key_info & WPA_KEY_INFO_KEY_TYPE)) { /* Earlier versions of IEEE 802.11i did not explicitly * require version 2 descriptor for all EAPOL-Key * packets, so allow group keys to use version 1 if * CCMP is not used for them. */ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Backwards compatibility: allow invalid " "version for non-CCMP group keys"); } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used"); } else goto out; } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && !wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " "descriptor version (%d) is not 2", ver); goto out; } if (sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not increase - " "dropping packet"); goto out; } if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported SMK bit in key_info"); goto out; } if (!(key_info & WPA_KEY_INFO_ACK)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No Ack bit in key_info"); goto out; } if (key_info & WPA_KEY_INFO_REQUEST) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: EAPOL-Key with Request bit - dropped"); goto out; } if ((key_info & WPA_KEY_INFO_MIC) && wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) goto out; #ifdef CONFIG_FILS if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len)) goto out; } #endif /* CONFIG_FILS */ if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) { /* * Only decrypt the Key Data field if the frame's authenticity * was verified. When using AES-SIV (FILS), the MIC flag is not * set, so this check should only be performed if mic_len != 0 * which is the case in this code branch. */ if (!(key_info & WPA_KEY_INFO_MIC)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Ignore EAPOL-Key with encrypted but unauthenticated data"); goto out; } if (wpa_supplicant_decrypt_key_data(sm, key, mic_len, ver, key_data, &key_data_len)) goto out; } if (key_info & WPA_KEY_INFO_KEY_TYPE) { if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Ignored EAPOL-Key (Pairwise) with " "non-zero key index"); goto out; } if (key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ENCR_KEY_DATA)) { /* 3/4 4-Way Handshake */ wpa_supplicant_process_3_of_4(sm, key, ver, key_data, key_data_len); } else { /* 1/4 4-Way Handshake */ wpa_supplicant_process_1_of_4(sm, src_addr, key, ver, key_data, key_data_len); } } else { if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) || (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) { /* 1/2 Group Key Handshake */ wpa_supplicant_process_1_of_2(sm, src_addr, key, key_data, key_data_len, ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key (Group) without Mic/Encr bit - " "dropped"); } } ret = 1; out: bin_clear_free(tmp, data_len); return ret; } #ifdef CONFIG_CTRL_IFACE static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) { switch (sm->key_mgmt) { case WPA_KEY_MGMT_IEEE8021X: return ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); case WPA_KEY_MGMT_PSK: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); #ifdef CONFIG_IEEE80211R case WPA_KEY_MGMT_FT_IEEE8021X: return RSN_AUTH_KEY_MGMT_FT_802_1X; case WPA_KEY_MGMT_FT_PSK: return RSN_AUTH_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ case WPA_KEY_MGMT_IEEE8021X_SHA256: return RSN_AUTH_KEY_MGMT_802_1X_SHA256; case WPA_KEY_MGMT_PSK_SHA256: return RSN_AUTH_KEY_MGMT_PSK_SHA256; case WPA_KEY_MGMT_CCKM: return (sm->proto == WPA_PROTO_RSN ? RSN_AUTH_KEY_MGMT_CCKM: WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; default: return 0; } } #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff /** * wpa_sm_get_mib - Dump text list of MIB entries * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for the list * @buflen: Length of the buffer * Returns: Number of bytes written to buffer * * This function is used fetch dot11 MIB variables. */ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { char pmkid_txt[PMKID_LEN * 2 + 1]; bool rsna; int ret; size_t len; if (sm->cur_pmksa) { wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), sm->cur_pmksa->pmkid, PMKID_LEN); } else pmkid_txt[0] = '\0'; rsna = (wpa_key_mgmt_wpa_psk(sm->key_mgmt) || wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && sm->proto == WPA_PROTO_RSN; ret = os_snprintf(buf, buflen, "dot11RSNAOptionImplemented=TRUE\n" "dot11RSNAPreauthenticationImplemented=TRUE\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n" "dot11RSNAConfigVersion=%d\n" "dot11RSNAConfigPairwiseKeysSupported=5\n" "dot11RSNAConfigGroupCipherSize=%d\n" "dot11RSNAConfigPMKLifetime=%d\n" "dot11RSNAConfigPMKReauthThreshold=%d\n" "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" "dot11RSNAConfigSATimeout=%d\n", rsna ? "TRUE" : "FALSE", rsna ? "TRUE" : "FALSE", RSN_VERSION, wpa_cipher_key_len(sm->group_cipher) * 8, sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); if (os_snprintf_error(buflen, ret)) return 0; len = ret; ret = os_snprintf( buf + len, buflen - len, "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" "dot11RSNAPMKIDUsed=%s\n" "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" "dot11RSNA4WayHandshakeFailures=%u\n", RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->pairwise_cipher)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), pmkid_txt, RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->pairwise_cipher)), RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); if (!os_snprintf_error(buflen - len, ret)) len += ret; return (int) len; } #endif /* CONFIG_CTRL_IFACE */ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx, enum pmksa_free_reason reason) { struct wpa_sm *sm = ctx; int deauth = 0; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " MACSTR " reason=%d", MAC2STR(entry->aa), reason); if (sm->cur_pmksa == entry) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: %s current PMKSA entry", reason == PMKSA_REPLACE ? "replaced" : "removed"); pmksa_cache_clear_current(sm); /* * If an entry is simply being replaced, there's no need to * deauthenticate because it will be immediately re-added. * This happens when EAP authentication is completed again * (reauth or failed PMKSA caching attempt). */ if (reason != PMKSA_REPLACE) deauth = 1; } if (reason == PMKSA_EXPIRE && (sm->pmk_len == entry->pmk_len && os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: deauthenticating due to expired PMK"); pmksa_cache_clear_current(sm); deauth = 1; } if (deauth) { sm->pmk_len = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } } /** * wpa_sm_init - Initialize WPA state machine * @ctx: Context pointer for callbacks; this needs to be an allocated buffer * Returns: Pointer to the allocated WPA state machine data * * This function is used to allocate a new WPA state machine and the returned * value is passed to all WPA state machine calls. */ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) { struct wpa_sm *sm; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; dl_list_init(&sm->pmksa_candidates); sm->renew_snonce = 1; sm->ctx = ctx; sm->dot11RSNAConfigPMKLifetime = 43200; sm->dot11RSNAConfigPMKReauthThreshold = 70; sm->dot11RSNAConfigSATimeout = 60; sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); if (sm->pmksa == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "RSN: PMKSA cache initialization failed"); os_free(sm); return NULL; } return sm; } /** * wpa_sm_deinit - Deinitialize WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() */ void wpa_sm_deinit(struct wpa_sm *sm) { if (sm == NULL) return; pmksa_cache_deinit(sm->pmksa); eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); os_free(sm->assoc_wpa_ie); os_free(sm->assoc_rsnxe); os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); os_free(sm->ap_rsnxe); wpa_sm_drop_sa(sm); os_free(sm->ctx); #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(sm->test_assoc_ie); #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_FILS_SK_PFS crypto_ecdh_deinit(sm->fils_ecdh); #endif /* CONFIG_FILS_SK_PFS */ #ifdef CONFIG_FILS wpabuf_free(sm->fils_ft_ies); #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE crypto_ecdh_deinit(sm->owe_ecdh); #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 wpabuf_clear_free(sm->dpp_z); #endif /* CONFIG_DPP2 */ os_free(sm); } /** * wpa_sm_notify_assoc - Notify WPA state machine about association * @sm: Pointer to WPA state machine data from wpa_sm_init() * @bssid: The BSSID of the new association * * This function is called to let WPA state machine know that the connection * was established. */ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { int clear_keys = 1; if (sm == NULL) return; wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Association event - clear replay counter"); os_memcpy(sm->bssid, bssid, ETH_ALEN); os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 0; sm->renew_snonce = 1; if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0) rsn_preauth_deinit(sm); #ifdef CONFIG_IEEE80211R if (wpa_ft_is_completed(sm)) { /* * Clear portValid to kick EAPOL state machine to re-enter * AUTHENTICATED state to get the EAPOL port Authorized. */ eapol_sm_notify_portValid(sm->eapol, false); wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); /* Prepare for the next transition */ wpa_ft_prepare_auth_request(sm, NULL); clear_keys = 0; sm->ft_protocol = 1; } else { sm->ft_protocol = 0; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_FILS if (sm->fils_completed) { /* * Clear portValid to kick EAPOL state machine to re-enter * AUTHENTICATED state to get the EAPOL port Authorized. */ wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); clear_keys = 0; } #endif /* CONFIG_FILS */ if (clear_keys) { /* * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * this is not part of a Fast BSS Transition. */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); sm->ptk_set = 0; os_memset(&sm->ptk, 0, sizeof(sm->ptk)); sm->tptk_set = 0; os_memset(&sm->tptk, 0, sizeof(sm->tptk)); os_memset(&sm->gtk, 0, sizeof(sm->gtk)); os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); os_memset(&sm->igtk, 0, sizeof(sm->igtk)); os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); } #ifdef CONFIG_TDLS wpa_tdls_assoc(sm); #endif /* CONFIG_TDLS */ #ifdef CONFIG_P2P os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); #endif /* CONFIG_P2P */ sm->keyidx_active = 0; } /** * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation * @sm: Pointer to WPA state machine data from wpa_sm_init() * * This function is called to let WPA state machine know that the connection * was lost. This will abort any existing pre-authentication session. */ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); rsn_preauth_deinit(sm); pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ #ifdef CONFIG_FILS sm->fils_completed = 0; #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R sm->ft_reassoc_completed = 0; sm->ft_protocol = 0; #endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ wpa_sm_drop_sa(sm); sm->keyidx_active = 0; sm->msg_3_of_4_ok = 0; os_memset(sm->bssid, 0, ETH_ALEN); } /** * wpa_sm_set_pmk - Set PMK * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmk: The new PMK * @pmk_len: The length of the new PMK in bytes * @pmkid: Calculated PMKID * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK * * Configure the PMK for WPA state machine. */ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid) { if (sm == NULL) return; wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data", pmk, pmk_len); sm->pmk_len = pmk_len; os_memcpy(sm->pmk, pmk, pmk_len); #ifdef CONFIG_IEEE80211R /* Set XXKey to be PSK for FT key derivation */ sm->xxkey_len = pmk_len; os_memcpy(sm->xxkey, pmk, pmk_len); #endif /* CONFIG_IEEE80211R */ if (bssid) { pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, NULL); } } /** * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA * @sm: Pointer to WPA state machine data from wpa_sm_init() * * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK * will be cleared. */ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) { if (sm == NULL) return; if (sm->cur_pmksa) { wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on current PMKSA", sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len); sm->pmk_len = sm->cur_pmksa->pmk_len; os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); } else { wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK"); sm->pmk_len = 0; os_memset(sm->pmk, 0, PMK_LEN_MAX); } } /** * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled * @sm: Pointer to WPA state machine data from wpa_sm_init() * @fast_reauth: Whether fast reauthentication (EAP) is allowed */ void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) { if (sm) sm->fast_reauth = fast_reauth; } /** * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks * @sm: Pointer to WPA state machine data from wpa_sm_init() * @scard_ctx: Context pointer for smartcard related callback functions */ void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) { if (sm == NULL) return; sm->scard_ctx = scard_ctx; if (sm->preauth_eapol) eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx); } /** * wpa_sm_set_config - Notification of current configuration change * @sm: Pointer to WPA state machine data from wpa_sm_init() * @config: Pointer to current network configuration * * Notify WPA state machine that configuration has changed. config will be * stored as a backpointer to network configuration. This can be %NULL to clear * the stored pointed. */ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) { if (!sm) return; if (config) { sm->network_ctx = config->network_ctx; sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; sm->proactive_key_caching = config->proactive_key_caching; sm->eap_workaround = config->eap_workaround; sm->eap_conf_ctx = config->eap_conf_ctx; if (config->ssid) { os_memcpy(sm->ssid, config->ssid, config->ssid_len); sm->ssid_len = config->ssid_len; } else sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; sm->owe_ptk_workaround = config->owe_ptk_workaround; sm->force_kdk_derivation = config->force_kdk_derivation; #ifdef CONFIG_FILS if (config->fils_cache_id) { sm->fils_cache_id_set = 1; os_memcpy(sm->fils_cache_id, config->fils_cache_id, FILS_CACHE_ID_LEN); } else { sm->fils_cache_id_set = 0; } #endif /* CONFIG_FILS */ sm->beacon_prot = config->beacon_prot; } else { sm->network_ctx = NULL; sm->allowed_pairwise_cipher = 0; sm->proactive_key_caching = 0; sm->eap_workaround = 0; sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; sm->wpa_rsc_relaxation = 0; sm->owe_ptk_workaround = 0; sm->beacon_prot = 0; sm->force_kdk_derivation = false; } } /** * wpa_sm_set_own_addr - Set own MAC address * @sm: Pointer to WPA state machine data from wpa_sm_init() * @addr: Own MAC address */ void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) { if (sm) os_memcpy(sm->own_addr, addr, ETH_ALEN); } /** * wpa_sm_set_ifname - Set network interface name * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ifname: Interface name * @bridge_ifname: Optional bridge interface name (for pre-auth) */ void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname) { if (sm) { sm->ifname = ifname; sm->bridge_ifname = bridge_ifname; } } /** * wpa_sm_set_eapol - Set EAPOL state machine pointer * @sm: Pointer to WPA state machine data from wpa_sm_init() * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() */ void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) { if (sm) sm->eapol = eapol; } /** * wpa_sm_set_param - Set WPA state machine parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() * @param: Parameter field * @value: Parameter value * Returns: 0 on success, -1 on failure */ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value) { int ret = 0; if (sm == NULL) return -1; switch (param) { case RSNA_PMK_LIFETIME: if (value > 0) sm->dot11RSNAConfigPMKLifetime = value; else ret = -1; break; case RSNA_PMK_REAUTH_THRESHOLD: if (value > 0 && value <= 100) sm->dot11RSNAConfigPMKReauthThreshold = value; else ret = -1; break; case RSNA_SA_TIMEOUT: if (value > 0) sm->dot11RSNAConfigSATimeout = value; else ret = -1; break; case WPA_PARAM_PROTO: sm->proto = value; break; case WPA_PARAM_PAIRWISE: sm->pairwise_cipher = value; break; case WPA_PARAM_GROUP: sm->group_cipher = value; break; case WPA_PARAM_KEY_MGMT: sm->key_mgmt = value; break; case WPA_PARAM_MGMT_GROUP: sm->mgmt_group_cipher = value; break; case WPA_PARAM_RSN_ENABLED: sm->rsn_enabled = value; break; case WPA_PARAM_MFP: sm->mfp = value; break; case WPA_PARAM_OCV: sm->ocv = value; break; case WPA_PARAM_SAE_PWE: sm->sae_pwe = value; break; case WPA_PARAM_SAE_PK: sm->sae_pk = value; break; case WPA_PARAM_DENY_PTK0_REKEY: sm->wpa_deny_ptk0_rekey = value; break; case WPA_PARAM_EXT_KEY_ID: sm->ext_key_id = value; break; case WPA_PARAM_USE_EXT_KEY_ID: sm->use_ext_key_id = value; break; #ifdef CONFIG_TESTING_OPTIONS case WPA_PARAM_FT_RSNXE_USED: sm->ft_rsnxe_used = value; break; case WPA_PARAM_OCI_FREQ_EAPOL: sm->oci_freq_override_eapol = value; break; case WPA_PARAM_OCI_FREQ_EAPOL_G2: sm->oci_freq_override_eapol_g2 = value; break; case WPA_PARAM_OCI_FREQ_FT_ASSOC: sm->oci_freq_override_ft_assoc = value; break; case WPA_PARAM_OCI_FREQ_FILS_ASSOC: sm->oci_freq_override_fils_assoc = value; break; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_DPP2 case WPA_PARAM_DPP_PFS: sm->dpp_pfs = value; break; #endif /* CONFIG_DPP2 */ default: break; } return ret; } /** * wpa_sm_get_status - Get WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query WPA state machine for status information. This function fills in * a text area with current status information. If the buffer (buf) is not * large enough, status information will be truncated to fit the buffer. */ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { char *pos = buf, *end = buf + buflen; int ret; ret = os_snprintf(pos, end - pos, "pairwise_cipher=%s\n" "group_cipher=%s\n" "key_mgmt=%s\n", wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher), wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #ifdef CONFIG_DPP2 if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { ret = os_snprintf(pos, end - pos, "dpp_pfs=1\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_DPP2 */ if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { struct wpa_ie_data rsn; if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) { ret = os_snprintf(pos, end - pos, "pmf=%d\n" "mgmt_group_cipher=%s\n", (rsn.capabilities & WPA_CAPABILITY_MFPR) ? 2 : 1, wpa_cipher_txt( sm->mgmt_group_cipher)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } int wpa_sm_pmf_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) return 0; if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) return 1; return 0; } int wpa_sm_ext_key_id(struct wpa_sm *sm) { return sm ? sm->ext_key_id : 0; } int wpa_sm_ext_key_id_active(struct wpa_sm *sm) { return sm ? sm->use_ext_key_id : 0; } int wpa_sm_ocv_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; if (!sm->ocv || !sm->ap_rsn_ie) return 0; return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && (rsn.capabilities & WPA_CAPABILITY_OCVC); } /** * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() * @wpa_ie: Pointer to buffer for WPA/RSN IE * @wpa_ie_len: Pointer to the length of the wpa_ie buffer * Returns: 0 on success, -1 on failure */ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len) { int res; if (sm == NULL) return -1; #ifdef CONFIG_TESTING_OPTIONS if (sm->test_assoc_ie) { wpa_printf(MSG_DEBUG, "TESTING: Replace association WPA/RSN IE"); if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie)) return -1; os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie), wpabuf_len(sm->test_assoc_ie)); res = wpabuf_len(sm->test_assoc_ie); } else #endif /* CONFIG_TESTING_OPTIONS */ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); if (res < 0) return -1; *wpa_ie_len = res; wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", wpa_ie, *wpa_ie_len); if (sm->assoc_wpa_ie == NULL) { /* * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets * the correct version of the IE even if PMKSA caching is * aborted (which would remove PMKID from IE generation). */ sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len); if (sm->assoc_wpa_ie == NULL) return -1; sm->assoc_wpa_ie_len = *wpa_ie_len; } else { wpa_hexdump(MSG_DEBUG, "WPA: Leave previously set WPA IE default", sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); } return 0; } /** * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the WPA/RSN IE used in (Re)Association * Request frame. The IE will be used to override the default value generated * with wpa_sm_set_assoc_wpa_ie_default(). */ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->assoc_wpa_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); sm->assoc_wpa_ie = NULL; sm->assoc_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); sm->assoc_wpa_ie = os_memdup(ie, len); if (sm->assoc_wpa_ie == NULL) return -1; sm->assoc_wpa_ie_len = len; } return 0; } /** * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() * @rsnxe: Pointer to buffer for RSNXE * @rsnxe_len: Pointer to the length of the rsne buffer * Returns: 0 on success, -1 on failure */ int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe, size_t *rsnxe_len) { int res; if (!sm) return -1; res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len); if (res < 0) return -1; *rsnxe_len = res; wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len); if (sm->assoc_rsnxe) { wpa_hexdump(MSG_DEBUG, "RSN: Leave previously set RSNXE default", sm->assoc_rsnxe, sm->assoc_rsnxe_len); } else if (*rsnxe_len > 0) { /* * Make a copy of the RSNXE so that 4-Way Handshake gets the * correct version of the IE even if it gets changed. */ sm->assoc_rsnxe = os_memdup(rsnxe, *rsnxe_len); if (!sm->assoc_rsnxe) return -1; sm->assoc_rsnxe_len = *rsnxe_len; } return 0; } /** * wpa_sm_set_assoc_rsnxe - Set own RSNXE from (Re)AssocReq * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the RSNXE used in (Re)Association Request * frame. The IE will be used to override the default value generated * with wpa_sm_set_assoc_rsnxe_default(). */ int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len) { if (!sm) return -1; os_free(sm->assoc_rsnxe); if (!ie || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: clearing own RSNXE"); sm->assoc_rsnxe = NULL; sm->assoc_rsnxe_len = 0; } else { wpa_hexdump(MSG_DEBUG, "RSN: set own RSNXE", ie, len); sm->assoc_rsnxe = os_memdup(ie, len); if (!sm->assoc_rsnxe) return -1; sm->assoc_rsnxe_len = len; } return 0; } /** * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the WPA IE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->ap_wpa_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP WPA IE"); sm->ap_wpa_ie = NULL; sm->ap_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); sm->ap_wpa_ie = os_memdup(ie, len); if (sm->ap_wpa_ie == NULL) return -1; sm->ap_wpa_ie_len = len; } return 0; } /** * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the RSN IE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { if (sm == NULL) return -1; os_free(sm->ap_rsn_ie); if (ie == NULL || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSN IE"); sm->ap_rsn_ie = NULL; sm->ap_rsn_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); sm->ap_rsn_ie = os_memdup(ie, len); if (sm->ap_rsn_ie == NULL) return -1; sm->ap_rsn_ie_len = len; } return 0; } /** * wpa_sm_set_ap_rsnxe - Set AP RSNXE from Beacon/ProbeResp * @sm: Pointer to WPA state machine data from wpa_sm_init() * @ie: Pointer to IE data (starting from id) * @len: IE length * Returns: 0 on success, -1 on failure * * Inform WPA state machine about the RSNXE used in Beacon / Probe Response * frame. */ int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len) { if (!sm) return -1; os_free(sm->ap_rsnxe); if (!ie || len == 0) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSNXE"); sm->ap_rsnxe = NULL; sm->ap_rsnxe_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len); sm->ap_rsnxe = os_memdup(ie, len); if (!sm->ap_rsnxe) return -1; sm->ap_rsnxe_len = len; } return 0; } /** * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE * @sm: Pointer to WPA state machine data from wpa_sm_init() * @data: Pointer to data area for parsing results * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure * * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the * parsed data into data. */ int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { if (sm == NULL) return -1; if (sm->assoc_wpa_ie == NULL) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: No WPA/RSN IE available from association info"); return -1; } if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) return -2; return 0; } int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) { return pmksa_cache_list(sm->pmksa, buf, len); } struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm) { return pmksa_cache_head(sm->pmksa); } struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm, struct rsn_pmksa_cache_entry * entry) { return pmksa_cache_add_entry(sm->pmksa, entry); } void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid, const u8 *fils_cache_id) { sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, fils_cache_id); } int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const void *network_ctx) { return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL; } struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *aa, const u8 *pmkid, const void *network_ctx, int akmp) { return pmksa_cache_get(sm->pmksa, aa, pmkid, network_ctx, akmp); } void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; sm->pmk_len = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); os_memset(&sm->gtk, 0, sizeof(sm->gtk)); os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep)); os_memset(&sm->igtk, 0, sizeof(sm->igtk)); os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep)); #ifdef CONFIG_IEEE80211R os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); sm->xxkey_len = 0; os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); sm->pmk_r0_len = 0; os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); sm->pmk_r1_len = 0; #ifdef CONFIG_PASN os_free(sm->pasn_r1kh); sm->pasn_r1kh = NULL; sm->n_pasn_r1kh = 0; #endif /* CONFIG_PASN */ #endif /* CONFIG_IEEE80211R */ } int wpa_sm_has_ptk(struct wpa_sm *sm) { if (sm == NULL) return 0; return sm->ptk_set; } int wpa_sm_has_ptk_installed(struct wpa_sm *sm) { if (!sm) return 0; return sm->ptk.installed; } void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) { os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); } void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { - pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, false); +} + + +void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) +{ + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, true); } #ifdef CONFIG_WNM int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) { u16 keyinfo; u8 keylen; /* plaintext key len */ u8 *key_rsc; if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { struct wpa_gtk_data gd; os_memset(&gd, 0, sizeof(gd)); keylen = wpa_cipher_key_len(sm->group_cipher); gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); gd.alg = wpa_cipher_to_alg(sm->group_cipher); if (gd.alg == WPA_ALG_NONE) { wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); return -1; } key_rsc = buf + 5; keyinfo = WPA_GET_LE16(buf + 2); gd.gtk_len = keylen; if (gd.gtk_len != buf[4]) { wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", gd.gtk_len, buf[4]); return -1; } gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ gd.tx = wpa_supplicant_gtk_tx_bit_workaround( sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); os_memcpy(gd.gtk, buf + 13, gd.gtk_len); wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", gd.gtk, gd.gtk_len); if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) { forced_memzero(&gd, sizeof(gd)); wpa_printf(MSG_DEBUG, "Failed to install the GTK in " "WNM mode"); return -1; } forced_memzero(&gd, sizeof(gd)); } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { const struct wpa_igtk_kde *igtk; igtk = (const struct wpa_igtk_kde *) (buf + 2); if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0) return -1; } else if (subelem_id == WNM_SLEEP_SUBELEM_BIGTK) { const struct wpa_bigtk_kde *bigtk; bigtk = (const struct wpa_bigtk_kde *) (buf + 2); if (sm->beacon_prot && wpa_supplicant_install_bigtk(sm, bigtk, 1) < 0) return -1; } else { wpa_printf(MSG_DEBUG, "Unknown element id"); return -1; } return 0; } #endif /* CONFIG_WNM */ #ifdef CONFIG_P2P int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) { if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) return -1; os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); return 0; } #endif /* CONFIG_P2P */ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) { if (rx_replay_counter == NULL) return; os_memcpy(sm->rx_replay_counter, rx_replay_counter, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 1; wpa_printf(MSG_DEBUG, "Updated key replay counter"); } void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len) { if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) { os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len); sm->ptk.kck_len = ptk_kck_len; wpa_printf(MSG_DEBUG, "Updated PTK KCK"); } if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) { os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len); sm->ptk.kek_len = ptk_kek_len; wpa_printf(MSG_DEBUG, "Updated PTK KEK"); } sm->ptk_set = 1; } #ifdef CONFIG_TESTING_OPTIONS void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) { wpabuf_free(sm->test_assoc_ie); sm->test_assoc_ie = buf; } const u8 * wpa_sm_get_anonce(struct wpa_sm *sm) { return sm->anonce; } #endif /* CONFIG_TESTING_OPTIONS */ unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm) { return sm->key_mgmt; } #ifdef CONFIG_FILS struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) { struct wpabuf *buf = NULL; struct wpabuf *erp_msg; struct wpabuf *pub = NULL; erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol); if (!erp_msg && !sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS"); goto fail; } wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)", erp_msg != NULL, sm->cur_pmksa != NULL); sm->fils_completed = 0; if (!sm->assoc_wpa_ie) { wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS"); goto fail; } if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 || random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0) goto fail; wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce", sm->fils_nonce, FILS_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session", sm->fils_session, FILS_SESSION_LEN); #ifdef CONFIG_FILS_SK_PFS sm->fils_dh_group = dh_group; if (dh_group) { crypto_ecdh_deinit(sm->fils_ecdh); sm->fils_ecdh = crypto_ecdh_init(dh_group); if (!sm->fils_ecdh) { wpa_printf(MSG_INFO, "FILS: Could not initialize ECDH with group %d", dh_group); goto fail; } pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1); if (!pub) goto fail; wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)", pub); sm->fils_dh_elem_len = wpabuf_len(pub); } #endif /* CONFIG_FILS_SK_PFS */ buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len + (pub ? wpabuf_len(pub) : 0)); if (!buf) goto fail; /* Fields following the Authentication algorithm number field */ /* Authentication Transaction seq# */ wpabuf_put_le16(buf, 1); /* Status Code */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); /* TODO: FILS PK */ #ifdef CONFIG_FILS_SK_PFS if (dh_group) { /* Finite Cyclic Group */ wpabuf_put_le16(buf, dh_group); /* Element */ wpabuf_put_buf(buf, pub); } #endif /* CONFIG_FILS_SK_PFS */ /* RSNE */ wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame", sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); if (md) { /* MDE when using FILS for FT initial association */ struct rsn_mdie *mdie; wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN); wpabuf_put_u8(buf, sizeof(*mdie)); mdie = wpabuf_put(buf, sizeof(*mdie)); os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); mdie->ft_capab = 0; } /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */ /* Element ID Extension */ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ /* Element ID Extension */ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); /* Wrapped Data */ sm->fils_erp_pmkid_set = 0; if (erp_msg) { wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */ /* Element ID Extension */ wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); wpabuf_put_buf(buf, erp_msg); /* Calculate pending PMKID here so that we do not need to * maintain a copy of the EAP-Initiate/Reauth message. */ if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg), wpabuf_len(erp_msg), sm->fils_erp_pmkid) == 0) sm->fils_erp_pmkid_set = 1; } wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame", buf); fail: wpabuf_free(erp_msg); wpabuf_free(pub); return buf; } int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, size_t len) { const u8 *pos, *end; struct ieee802_11_elems elems; struct wpa_ie_data rsn; int pmkid_match = 0; u8 ick[FILS_ICK_MAX_LEN]; size_t ick_len; int res; struct wpabuf *dh_ss = NULL; const u8 *g_sta = NULL; size_t g_sta_len = 0; const u8 *g_ap = NULL; size_t g_ap_len = 0, kdk_len; struct wpabuf *pub = NULL; os_memcpy(sm->bssid, bssid, ETH_ALEN); wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", data, len); pos = data; end = data + len; /* TODO: FILS PK */ #ifdef CONFIG_FILS_SK_PFS if (sm->fils_dh_group) { u16 group; /* Using FILS PFS */ /* Finite Cyclic Group */ if (end - pos < 2) { wpa_printf(MSG_DEBUG, "FILS: No room for Finite Cyclic Group"); goto fail; } group = WPA_GET_LE16(pos); pos += 2; if (group != sm->fils_dh_group) { wpa_printf(MSG_DEBUG, "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)", group, sm->fils_dh_group); goto fail; } /* Element */ if ((size_t) (end - pos) < sm->fils_dh_elem_len) { wpa_printf(MSG_DEBUG, "FILS: No room for Element"); goto fail; } if (!sm->fils_ecdh) { wpa_printf(MSG_DEBUG, "FILS: No ECDH state available"); goto fail; } dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos, sm->fils_dh_elem_len); if (!dh_ss) { wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); goto fail; } wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss); g_ap = pos; g_ap_len = sm->fils_dh_elem_len; pos += sm->fils_dh_elem_len; } #endif /* CONFIG_FILS_SK_PFS */ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); goto fail; } /* RSNE */ wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie, elems.rsn_ie_len); if (!elems.rsn_ie || wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &rsn) < 0) { wpa_printf(MSG_DEBUG, "FILS: No RSN element"); goto fail; } if (!elems.fils_nonce) { wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); goto fail; } os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { struct wpa_ft_ies parse; if (!elems.mdie || !elems.ftie) { wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE"); goto fail; } if (wpa_ft_parse_ies(pos, end - pos, &parse, wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) { wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs"); goto fail; } if (!parse.r0kh_id) { wpa_printf(MSG_DEBUG, "FILS+FT: No R0KH-ID subelem in FTE"); goto fail; } os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); sm->r0kh_id_len = parse.r0kh_id_len; wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); if (!parse.r1kh_id) { wpa_printf(MSG_DEBUG, "FILS+FT: No R1KH-ID subelem in FTE"); goto fail; } os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); /* TODO: Check MDE and FTE payload */ wpabuf_free(sm->fils_ft_ies); sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len + 2 + elems.ftie_len); if (!sm->fils_ft_ies) goto fail; wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2, 2 + elems.mdie_len); wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2, 2 + elems.ftie_len); } else { wpabuf_free(sm->fils_ft_ies); sm->fils_ft_ies = NULL; } #endif /* CONFIG_IEEE80211R */ /* PMKID List */ if (rsn.pmkid && rsn.num_pmkid > 0) { wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", rsn.pmkid, rsn.num_pmkid * PMKID_LEN); if (rsn.num_pmkid != 1) { wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection"); goto fail; } wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN); if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch"); wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); goto fail; } wpa_printf(MSG_DEBUG, "FILS: Matching PMKID - continue using PMKSA caching"); pmkid_match = 1; } if (!pmkid_match && sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "FILS: No PMKID match - cannot use cached PMKSA entry"); sm->cur_pmksa = NULL; } /* FILS Session */ if (!elems.fils_session) { wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); goto fail; } wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, FILS_SESSION_LEN); if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN) != 0) { wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", sm->fils_session, FILS_SESSION_LEN); goto fail; } /* Wrapped Data */ if (!sm->cur_pmksa && elems.wrapped_data) { u8 rmsk[ERP_MAX_KEY_LEN]; size_t rmsk_len; wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", elems.wrapped_data, elems.wrapped_data_len); eapol_sm_process_erp_finish(sm->eapol, elems.wrapped_data, elems.wrapped_data_len); if (eapol_sm_failed(sm->eapol)) goto fail; rmsk_len = ERP_MAX_KEY_LEN; res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); if (res == PMK_LEN) { rmsk_len = PMK_LEN; res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); } if (res) goto fail; res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len, sm->fils_nonce, sm->fils_anonce, dh_ss ? wpabuf_head(dh_ss) : NULL, dh_ss ? wpabuf_len(dh_ss) : 0, sm->pmk, &sm->pmk_len); forced_memzero(rmsk, sizeof(rmsk)); /* Don't use DHss in PTK derivation if PMKSA caching is not * used. */ wpabuf_clear_free(dh_ss); dh_ss = NULL; if (res) goto fail; if (!sm->fils_erp_pmkid_set) { wpa_printf(MSG_DEBUG, "FILS: PMKID not available"); goto fail; } wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid, PMKID_LEN); wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result"); sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, sm->fils_erp_pmkid, NULL, 0, sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, NULL); } if (!sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "FILS: No remaining options to continue FILS authentication"); goto fail; } if (sm->force_kdk_derivation || (sm->secure_ltf && ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))) kdk_len = WPA_KDK_MAX_LEN; else kdk_len = 0; if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid, sm->fils_nonce, sm->fils_anonce, dh_ss ? wpabuf_head(dh_ss) : NULL, dh_ss ? wpabuf_len(dh_ss) : 0, &sm->ptk, ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher, sm->fils_ft, &sm->fils_ft_len, kdk_len) < 0) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK"); goto fail; } wpabuf_clear_free(dh_ss); dh_ss = NULL; sm->ptk_set = 1; sm->tptk_set = 0; os_memset(&sm->tptk, 0, sizeof(sm->tptk)); #ifdef CONFIG_FILS_SK_PFS if (sm->fils_dh_group) { if (!sm->fils_ecdh) { wpa_printf(MSG_INFO, "FILS: ECDH not initialized"); goto fail; } pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1); if (!pub) goto fail; wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub); g_sta = wpabuf_head(pub); g_sta_len = wpabuf_len(pub); if (!g_ap) { wpa_printf(MSG_INFO, "FILS: gAP not available"); goto fail; } wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); } #endif /* CONFIG_FILS_SK_PFS */ res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce, sm->fils_anonce, sm->own_addr, sm->bssid, g_sta, g_sta_len, g_ap, g_ap_len, sm->key_mgmt, sm->fils_key_auth_sta, sm->fils_key_auth_ap, &sm->fils_key_auth_len); wpabuf_free(pub); forced_memzero(ick, sizeof(ick)); return res; fail: wpabuf_free(pub); wpabuf_clear_free(dh_ss); return -1; } #ifdef CONFIG_IEEE80211R static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) { struct rsn_ie_hdr *rsnie; u16 capab; u8 *pos; int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); /* RSNIE[PMKR0Name/PMKR1Name] */ rsnie = wpabuf_put(buf, sizeof(*rsnie)); rsnie->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(rsnie->version, RSN_VERSION); /* Group Suite Selector */ if (!wpa_cipher_valid_group(sm->group_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); return -1; } pos = wpabuf_put(buf, RSN_SELECTOR_LEN); RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, sm->group_cipher)); /* Pairwise Suite Count */ wpabuf_put_le16(buf, 1); /* Pairwise Suite List */ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", sm->pairwise_cipher); return -1; } pos = wpabuf_put(buf, RSN_SELECTOR_LEN); RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, sm->pairwise_cipher)); /* Authenticated Key Management Suite Count */ wpabuf_put_le16(buf, 1); /* Authenticated Key Management Suite List */ pos = wpabuf_put(buf, RSN_SELECTOR_LEN); if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); else { wpa_printf(MSG_WARNING, "FILS+FT: Invalid key management type (%d)", sm->key_mgmt); return -1; } /* RSN Capabilities */ capab = 0; if (sm->mfp) capab |= WPA_CAPABILITY_MFPC; if (sm->mfp == 2) capab |= WPA_CAPABILITY_MFPR; if (sm->ocv) capab |= WPA_CAPABILITY_OCVC; if (sm->ext_key_id) capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST; wpabuf_put_le16(buf, capab); /* PMKID Count */ wpabuf_put_le16(buf, 1); /* PMKID List [PMKR1Name] */ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)", sm->fils_ft, sm->fils_ft_len); wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len); wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID", sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid, sm->ssid_len, sm->mobility_domain, sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) { wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0"); return -1; } sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR, MAC2STR(sm->r1kh_id)); pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1_name, use_sha384) < 0) { wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); return -1; } os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN); if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { /* Management Group Cipher Suite */ pos = wpabuf_put(buf, RSN_SELECTOR_LEN); RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); } rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2; return 0; } #endif /* CONFIG_IEEE80211R */ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, size_t *kek_len, const u8 **snonce, const u8 **anonce, const struct wpabuf **hlp, unsigned int num_hlp) { struct wpabuf *buf; size_t len; unsigned int i; len = 1000; #ifdef CONFIG_IEEE80211R if (sm->fils_ft_ies) len += wpabuf_len(sm->fils_ft_ies); if (wpa_key_mgmt_ft(sm->key_mgmt)) len += 256; #endif /* CONFIG_IEEE80211R */ for (i = 0; hlp && i < num_hlp; i++) len += 10 + wpabuf_len(hlp[i]); buf = wpabuf_alloc(len); if (!buf) return NULL; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { /* MDE and FTE when using FILS+FT */ wpabuf_put_buf(buf, sm->fils_ft_ies); /* RSNE with PMKR1Name in PMKID field */ if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) { wpabuf_free(buf); return NULL; } } #endif /* CONFIG_IEEE80211R */ /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ /* Element ID Extension */ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); /* Everything after FILS Session element gets encrypted in the driver * with KEK. The buffer returned from here is the plaintext version. */ /* TODO: FILS Public Key */ /* FILS Key Confirm */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */ /* Element ID Extension */ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM); wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len); /* FILS HLP Container */ for (i = 0; hlp && i < num_hlp; i++) { const u8 *pos = wpabuf_head(hlp[i]); size_t left = wpabuf_len(hlp[i]); wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ if (left <= 254) len = 1 + left; else len = 255; wpabuf_put_u8(buf, len); /* Length */ /* Element ID Extension */ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_HLP_CONTAINER); /* Destination MAC Address, Source MAC Address, HLP Packet. * HLP Packet is in MSDU format (i.e., included the LLC/SNAP * header when LPD is used). */ wpabuf_put_data(buf, pos, len - 1); pos += len - 1; left -= len - 1; while (left) { wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); len = left > 255 ? 255 : left; wpabuf_put_u8(buf, len); wpabuf_put_data(buf, pos, len); pos += len; left -= len; } } /* TODO: FILS IP Address Assignment */ #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; u8 *pos; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_printf(MSG_WARNING, "FILS: Failed to get channel info for OCI element"); wpabuf_free(buf); return NULL; } #ifdef CONFIG_TESTING_OPTIONS if (sm->oci_freq_override_fils_assoc) { wpa_printf(MSG_INFO, "TEST: Override OCI KDE frequency %d -> %d MHz", ci.frequency, sm->oci_freq_override_fils_assoc); ci.frequency = sm->oci_freq_override_fils_assoc; } #endif /* CONFIG_TESTING_OPTIONS */ pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN); if (ocv_insert_extended_oci(&ci, pos) < 0) { wpabuf_free(buf); return NULL; } } #endif /* CONFIG_OCV */ wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf); *kek = sm->ptk.kek; *kek_len = sm->ptk.kek_len; wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len); *snonce = sm->fils_nonce; wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD", *snonce, FILS_NONCE_LEN); *anonce = sm->fils_anonce; wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD", *anonce, FILS_NONCE_LEN); return buf; } static void fils_process_hlp_resp(struct wpa_sm *sm, const u8 *resp, size_t len) { const u8 *pos, *end; wpa_hexdump(MSG_MSGDUMP, "FILS: HLP response", resp, len); if (len < 2 * ETH_ALEN) return; pos = resp + 2 * ETH_ALEN; end = resp + len; if (end - pos >= 6 && os_memcmp(pos, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) pos += 6; /* Remove SNAP/LLC header */ wpa_sm_fils_hlp_rx(sm, resp, resp + ETH_ALEN, pos, end - pos); } static void fils_process_hlp_container(struct wpa_sm *sm, const u8 *pos, size_t len) { const u8 *end = pos + len; u8 *tmp, *tmp_pos; /* Check if there are any FILS HLP Container elements */ while (end - pos >= 2) { if (2 + pos[1] > end - pos) return; if (pos[0] == WLAN_EID_EXTENSION && pos[1] >= 1 + 2 * ETH_ALEN && pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) break; pos += 2 + pos[1]; } if (end - pos < 2) return; /* No FILS HLP Container elements */ tmp = os_malloc(end - pos); if (!tmp) return; while (end - pos >= 2) { if (2 + pos[1] > end - pos || pos[0] != WLAN_EID_EXTENSION || pos[1] < 1 + 2 * ETH_ALEN || pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) break; tmp_pos = tmp; os_memcpy(tmp_pos, pos + 3, pos[1] - 1); tmp_pos += pos[1] - 1; pos += 2 + pos[1]; /* Add possible fragments */ while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && 2 + pos[1] <= end - pos) { os_memcpy(tmp_pos, pos + 2, pos[1]); tmp_pos += pos[1]; pos += 2 + pos[1]; } fils_process_hlp_resp(sm, tmp, tmp_pos - tmp); } os_free(tmp); } int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) { const struct ieee80211_mgmt *mgmt; const u8 *end, *ie_start; struct ieee802_11_elems elems; int keylen, rsclen; enum wpa_alg alg; struct wpa_gtk_data gd; int maxkeylen; struct wpa_eapol_ie_parse kde; if (!sm || !sm->ptk_set) { wpa_printf(MSG_DEBUG, "FILS: No KEK available"); return -1; } if (!wpa_key_mgmt_fils(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM"); return -1; } if (sm->fils_completed) { wpa_printf(MSG_DEBUG, "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission"); return -1; } wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame", resp, len); mgmt = (const struct ieee80211_mgmt *) resp; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) return -1; end = resp + len; /* Same offset for Association Response and Reassociation Response */ ie_start = mgmt->u.assoc_resp.variable; if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "FILS: Failed to parse decrypted elements"); goto fail; } if (!elems.fils_session) { wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); return -1; } if (os_memcmp(elems.fils_session, sm->fils_session, FILS_SESSION_LEN) != 0) { wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch"); wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", elems.fils_session, FILS_SESSION_LEN); wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", sm->fils_session, FILS_SESSION_LEN); } if (!elems.rsn_ie) { wpa_printf(MSG_DEBUG, "FILS: No RSNE in (Re)Association Response"); /* As an interop workaround, allow this for now since IEEE Std * 802.11ai-2016 did not include all the needed changes to make * a FILS AP include RSNE in the frame. This workaround might * eventually be removed and replaced with rejection (goto fail) * to follow a strict interpretation of the standard. */ } else if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), sm->ap_rsn_ie, sm->ap_rsn_ie_len, elems.rsn_ie - 2, elems.rsn_ie_len + 2)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FILS: RSNE mismatch between Beacon/Probe Response and (Re)Association Response"); wpa_hexdump(MSG_DEBUG, "FILS: RSNE in Beacon/Probe Response", sm->ap_rsn_ie, sm->ap_rsn_ie_len); wpa_hexdump(MSG_DEBUG, "FILS: RSNE in (Re)Association Response", elems.rsn_ie, elems.rsn_ie_len); goto fail; } /* TODO: FILS Public Key */ if (!elems.fils_key_confirm) { wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); goto fail; } if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { wpa_printf(MSG_DEBUG, "FILS: Unexpected Key-Auth length %d (expected %d)", elems.fils_key_confirm_len, (int) sm->fils_key_auth_len); goto fail; } if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap, sm->fils_key_auth_len) != 0) { wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", elems.fils_key_confirm, elems.fils_key_confirm_len); wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", sm->fils_key_auth_ap, sm->fils_key_auth_len); goto fail; } #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(sm)) { struct wpa_channel_info ci; if (wpa_sm_channel_info(sm, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info to validate received OCI in FILS (Re)Association Response frame"); goto fail; } if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, channel_width_to_int(ci.chanwidth), ci.seg1_idx) != OCI_SUCCESS) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" MACSTR " frame=fils-assoc error=%s", MAC2STR(sm->bssid), ocv_errorstr); goto fail; } } #endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { struct wpa_ie_data rsn; /* Check that PMKR1Name derived by the AP matches */ if (!elems.rsn_ie || wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &rsn) < 0 || !rsn.pmkid || rsn.num_pmkid != 1 || os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { wpa_printf(MSG_DEBUG, "FILS+FT: No RSNE[PMKR1Name] match in AssocResp"); goto fail; } } #endif /* CONFIG_IEEE80211R */ /* Key Delivery */ if (!elems.key_delivery) { wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); goto fail; } /* Parse GTK and set the key to the driver */ os_memset(&gd, 0, sizeof(gd)); if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN, elems.key_delivery_len - WPA_KEY_RSC_LEN, &kde) < 0) { wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs"); goto fail; } if (!kde.gtk) { wpa_printf(MSG_DEBUG, "FILS: No GTK KDE"); goto fail; } maxkeylen = gd.gtk_len = kde.gtk_len - 2; if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd.gtk_len, maxkeylen, &gd.key_rsc_len, &gd.alg)) goto fail; wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len); gd.keyidx = kde.gtk[0] & 0x3; gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(kde.gtk[0] & BIT(2))); if (kde.gtk_len - 2 > sizeof(gd.gtk)) { wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)", (unsigned long) kde.gtk_len - 2); goto fail; } os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2); wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver"); if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) { wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK"); goto fail; } if (ieee80211w_set_keys(sm, &kde) < 0) { wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK"); goto fail; } alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) { wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu", keylen, (long unsigned int) sm->ptk.tk_len); goto fail; } rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver", sm->ptk.tk, keylen); if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen, sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid=" MACSTR ")", alg, keylen, MAC2STR(sm->bssid)); goto fail; } wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, sm->dot11RSNAConfigPMKLifetime, &sm->ptk); /* TODO: TK could be cleared after auth frame exchange now that driver * takes care of association frame encryption/decryption. */ /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); sm->ptk.tk_len = 0; sm->ptk.installed = 1; /* FILS HLP Container */ fils_process_hlp_container(sm, ie_start, end - ie_start); /* TODO: FILS IP Address Assignment */ wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully"); sm->fils_completed = 1; forced_memzero(&gd, sizeof(gd)); if (kde.transition_disable) wpa_sm_transition_disable(sm, kde.transition_disable[0]); return 0; fail: forced_memzero(&gd, sizeof(gd)); return -1; } void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set) { if (sm) sm->fils_completed = !!set; } #endif /* CONFIG_FILS */ int wpa_fils_is_completed(struct wpa_sm *sm) { #ifdef CONFIG_FILS return sm && sm->fils_completed; #else /* CONFIG_FILS */ return 0; #endif /* CONFIG_FILS */ } #ifdef CONFIG_OWE struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group) { struct wpabuf *ie = NULL, *pub = NULL; size_t prime_len; if (group == 19) prime_len = 32; else if (group == 20) prime_len = 48; else if (group == 21) prime_len = 66; else return NULL; crypto_ecdh_deinit(sm->owe_ecdh); sm->owe_ecdh = crypto_ecdh_init(group); if (!sm->owe_ecdh) goto fail; sm->owe_group = group; pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); pub = wpabuf_zeropad(pub, prime_len); if (!pub) goto fail; ie = wpabuf_alloc(5 + wpabuf_len(pub)); if (!ie) goto fail; wpabuf_put_u8(ie, WLAN_EID_EXTENSION); wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub)); wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM); wpabuf_put_le16(ie, group); wpabuf_put_buf(ie, pub); wpabuf_free(pub); wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element", ie); return ie; fail: wpabuf_free(pub); crypto_ecdh_deinit(sm->owe_ecdh); sm->owe_ecdh = NULL; return NULL; } int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, const u8 *resp_ies, size_t resp_ies_len) { struct ieee802_11_elems elems; u16 group; struct wpabuf *secret, *pub, *hkey; int res; u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; const char *info = "OWE Key Generation"; const u8 *addr[2]; size_t len[2]; size_t hash_len, prime_len; struct wpa_ie_data data; if (!resp_ies || ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) == ParseFailed) { wpa_printf(MSG_INFO, "OWE: Could not parse Association Response frame elements"); return -1; } if (sm->cur_pmksa && elems.rsn_ie && wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len, &data) == 0 && data.num_pmkid == 1 && data.pmkid && os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) { wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching"); wpa_sm_set_pmk_from_pmksa(sm); return 0; } if (!elems.owe_dh) { wpa_printf(MSG_INFO, "OWE: No Diffie-Hellman Parameter element found in Association Response frame"); return -1; } group = WPA_GET_LE16(elems.owe_dh); if (group != sm->owe_group) { wpa_printf(MSG_INFO, "OWE: Unexpected Diffie-Hellman group in response: %u", group); return -1; } if (!sm->owe_ecdh) { wpa_printf(MSG_INFO, "OWE: No ECDH state available"); return -1; } if (group == 19) prime_len = 32; else if (group == 20) prime_len = 48; else if (group == 21) prime_len = 66; else return -1; secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0, elems.owe_dh + 2, elems.owe_dh_len - 2); secret = wpabuf_zeropad(secret, prime_len); if (!secret) { wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); return -1; } wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); /* prk = HKDF-extract(C | A | group, z) */ pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); if (!pub) { wpabuf_clear_free(secret); return -1; } /* PMKID = Truncate-128(Hash(C | A)) */ addr[0] = wpabuf_head(pub); len[0] = wpabuf_len(pub); addr[1] = elems.owe_dh + 2; len[1] = elems.owe_dh_len - 2; if (group == 19) { res = sha256_vector(2, addr, len, pmkid); hash_len = SHA256_MAC_LEN; } else if (group == 20) { res = sha384_vector(2, addr, len, pmkid); hash_len = SHA384_MAC_LEN; } else if (group == 21) { res = sha512_vector(2, addr, len, pmkid); hash_len = SHA512_MAC_LEN; } else { res = -1; hash_len = 0; } pub = wpabuf_zeropad(pub, prime_len); if (res < 0 || !pub) { wpabuf_free(pub); wpabuf_clear_free(secret); return -1; } hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2); if (!hkey) { wpabuf_free(pub); wpabuf_clear_free(secret); return -1; } wpabuf_put_buf(hkey, pub); /* C */ wpabuf_free(pub); wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */ wpabuf_put_le16(hkey, sm->owe_group); /* group */ if (group == 19) res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), wpabuf_head(secret), wpabuf_len(secret), prk); else if (group == 20) res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), wpabuf_head(secret), wpabuf_len(secret), prk); else if (group == 21) res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), wpabuf_head(secret), wpabuf_len(secret), prk); wpabuf_clear_free(hkey); wpabuf_clear_free(secret); if (res < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ if (group == 19) res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sm->pmk, hash_len); else if (group == 20) res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sm->pmk, hash_len); else if (group == 21) res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sm->pmk, hash_len); forced_memzero(prk, SHA512_MAC_LEN); if (res < 0) { sm->pmk_len = 0; return -1; } sm->pmk_len = hash_len; wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len); wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, NULL); return 0; } #endif /* CONFIG_OWE */ void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id) { #ifdef CONFIG_FILS if (sm && fils_cache_id) { sm->fils_cache_id_set = 1; os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN); } #endif /* CONFIG_FILS */ } #ifdef CONFIG_DPP2 void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z) { if (sm) { wpabuf_clear_free(sm->dpp_z); sm->dpp_z = z ? wpabuf_dup(z) : NULL; } } #endif /* CONFIG_DPP2 */ #ifdef CONFIG_PASN void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid, int key_mgmt) { sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, NULL, key_mgmt, 0); } #endif /* CONFIG_PASN */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index f377acba2f23..ff8a85b6e29b 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -1,546 +1,557 @@ /* * wpa_supplicant - WPA definitions * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_H #define WPA_H #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" struct wpa_sm; struct eapol_sm; struct wpa_config_blob; struct hostapd_freq_params; struct wpa_channel_info; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ void *msg_ctx; /* upper level context for wpa_msg() calls */ void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); void (*deauthenticate)(void * ctx, u16 reason_code); void (*reconnect)(void *ctx); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len, enum key_flag key_flag); void * (*get_network_ctx)(void *ctx); int (*get_bssid)(void *ctx, u8 *bssid); int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf, size_t len); int (*get_beacon_ie)(void *ctx); void (*cancel_auth_timeout)(void *ctx); u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos); int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid, const u8 *pmkid, const u8 *fils_cache_id, const u8 *pmk, size_t pmk_len, u32 pmk_lifetime, u8 pmk_reauth_threshold, int akmp); int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid, const u8 *pmkid, const u8 *fils_cache_id); void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); const struct wpa_config_blob * (*get_config_blob)(void *ctx, const char *name); int (*mlme_setprotection)(void *ctx, const u8 *addr, int protection_type, int key_type); int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies, size_t ies_len); int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len); int (*mark_authenticated)(void *ctx, const u8 *target_ap); #ifdef CONFIG_TDLS int (*tdls_get_capa)(void *ctx, int *tdls_supported, int *tdls_ext_setup, int *tdls_chan_switch); int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, int initiator, const u8 *buf, size_t len); int (*tdls_oper)(void *ctx, int oper, const u8 *peer); int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, const struct ieee80211_he_capabilities *he_capab, size_t he_capab_len, u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, const u8 *supp_channels, size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len); int (*tdls_enable_channel_switch)( void *ctx, const u8 *addr, u8 oper_class, const struct hostapd_freq_params *params); int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr); #endif /* CONFIG_TDLS */ void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len, const u8 *kck, size_t kck_len, const u8 *replay_ctr); int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src, const u8 *pkt, size_t pkt_len); int (*channel_info)(void *ctx, struct wpa_channel_info *ci); void (*transition_disable)(void *ctx, u8 bitmap); void (*store_ptk)(void *ctx, u8 *addr, int cipher, u32 life_time, const struct wpa_ptk *ptk); }; enum wpa_sm_conf_params { RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */, RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */, RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */, WPA_PARAM_PROTO, WPA_PARAM_PAIRWISE, WPA_PARAM_GROUP, WPA_PARAM_KEY_MGMT, WPA_PARAM_MGMT_GROUP, WPA_PARAM_RSN_ENABLED, WPA_PARAM_MFP, WPA_PARAM_OCV, WPA_PARAM_SAE_PWE, WPA_PARAM_SAE_PK, WPA_PARAM_DENY_PTK0_REKEY, WPA_PARAM_EXT_KEY_ID, WPA_PARAM_USE_EXT_KEY_ID, WPA_PARAM_FT_RSNXE_USED, WPA_PARAM_DPP_PFS, WPA_PARAM_OCI_FREQ_EAPOL, WPA_PARAM_OCI_FREQ_EAPOL_G2, WPA_PARAM_OCI_FREQ_FT_ASSOC, WPA_PARAM_OCI_FREQ_FILS_ASSOC, }; struct rsn_supp_config { void *network_ctx; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; void *eap_conf_ctx; const u8 *ssid; size_t ssid_len; int wpa_ptk_rekey; int wpa_deny_ptk0_rekey; int p2p; int wpa_rsc_relaxation; int owe_ptk_workaround; const u8 *fils_cache_id; int beacon_prot; bool force_kdk_derivation; }; #ifndef CONFIG_NO_WPA struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); void wpa_sm_deinit(struct wpa_sm *sm); void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); void wpa_sm_notify_disassoc(struct wpa_sm *sm); void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid); void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config); void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr); void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname); void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol); int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len); int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe, size_t *rsnxe_len); int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value); int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int wpa_sm_pmf_enabled(struct wpa_sm *sm); int wpa_sm_ext_key_id(struct wpa_sm *sm); int wpa_sm_ext_key_id_active(struct wpa_sm *sm); int wpa_sm_ocv_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); void wpa_sm_aborted_cached(struct wpa_sm *sm); +void wpa_sm_aborted_external_cached(struct wpa_sm *sm); int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len); int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm); struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm, struct rsn_pmksa_cache_entry * entry); void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid, const u8 *fils_cache_id); int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const void *network_ctx); void wpa_sm_drop_sa(struct wpa_sm *sm); struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *aa, const u8 *pmkid, const void *network_ctx, int akmp); int wpa_sm_has_ptk(struct wpa_sm *sm); int wpa_sm_has_ptk_installed(struct wpa_sm *sm); void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); +void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len); int wpa_fils_is_completed(struct wpa_sm *sm); #else /* CONFIG_NO_WPA */ static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) { return (struct wpa_sm *) 1; } static inline void wpa_sm_deinit(struct wpa_sm *sm) { } static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) { } static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) { } static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid) { } static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) { } static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) { } static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) { } static inline void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) { } static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) { } static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, const char *bridge_ifname) { } static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) { } static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, size_t *wpa_ie_len) { return -1; } static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len) { return -1; } static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { return 0; } static inline int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value) { return -1; } static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose) { return 0; } static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) { return 0; } static inline int wpa_sm_ext_key_id(struct wpa_sm *sm) { return 0; } static inline int wpa_sm_ext_key_id_active(struct wpa_sm *sm) { return 0; } static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm) { return 0; } static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { } static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data) { return -1; } static inline void wpa_sm_aborted_cached(struct wpa_sm *sm) { } +static inline void wpa_sm_aborted_external_cached(struct wpa_sm *sm) +{ +} + static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { return -1; } static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { return -1; } static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) { return -1; } static inline void wpa_sm_drop_sa(struct wpa_sm *sm) { } static inline struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *aa, const u8 *pmkid, const void *network_ctx, int akmp) { return NULL; } static inline int wpa_sm_has_ptk(struct wpa_sm *sm) { return 0; } static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) { } +static inline void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, + void *network_ctx) +{ +} + static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { } static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) { } static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len) { } static inline int wpa_fils_is_completed(struct wpa_sm *sm) { return 0; } #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_IEEE80211R int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len, const u8 *mdie); const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm); int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len); int wpa_ft_is_completed(struct wpa_sm *sm); void wpa_reset_ft_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, const u8 *mdie); #ifdef CONFIG_PASN int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id, u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name); #endif /* CONFIG_PASN */ #else /* CONFIG_IEEE80211R */ static inline int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { return 0; } static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) { return 0; } static inline int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len, const u8 *mdie) { return 0; } static inline int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap) { return 0; } static inline int wpa_ft_is_completed(struct wpa_sm *sm) { return 0; } static inline void wpa_reset_ft_completed(struct wpa_sm *sm) { } static inline int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) { return -1; } #ifdef CONFIG_PASN int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id, u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name) { return -1; } #endif /* CONFIG_PASN */ #endif /* CONFIG_IEEE80211R */ /* tdls.c */ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_init(struct wpa_sm *sm); void wpa_tdls_teardown_peers(struct wpa_sm *sm); void wpa_tdls_deinit(struct wpa_sm *sm); void wpa_tdls_enable(struct wpa_sm *sm, int enabled); void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr); const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_is_external_setup(struct wpa_sm *sm); int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, u8 oper_class, struct hostapd_freq_params *freq_params); int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); #ifdef CONFIG_TDLS_TESTING extern unsigned int tdls_testing; #endif /* CONFIG_TDLS_TESTING */ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); const u8 * wpa_sm_get_anonce(struct wpa_sm *sm); unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm); struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md); int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, size_t len); struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, size_t *kek_len, const u8 **snonce, const u8 **anonce, const struct wpabuf **hlp, unsigned int num_hlp); int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group); int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, const u8 *resp_ies, size_t resp_ies_len); void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z); void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *bssid, int key_mgmt); #endif /* WPA_H */ diff --git a/src/wps/wps.h b/src/wps/wps.h index 6a12255c8a68..fed3e284895f 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -1,1098 +1,1102 @@ /* * Wi-Fi Protected Setup * Copyright (c) 2007-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPS_H #define WPS_H #include "common/ieee802_11_defs.h" #include "wps_defs.h" /** * enum wsc_op_code - EAP-WSC OP-Code values */ enum wsc_op_code { WSC_UPnP = 0 /* No OP Code in UPnP transport */, WSC_Start = 0x01, WSC_ACK = 0x02, WSC_NACK = 0x03, WSC_MSG = 0x04, WSC_Done = 0x05, WSC_FRAG_ACK = 0x06 }; struct wps_registrar; struct upnp_wps_device_sm; struct wps_er; struct wps_parse_attr; /** * struct wps_credential - WPS Credential * @ssid: SSID * @ssid_len: Length of SSID * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags) * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags) * @key_idx: Key index * @key: Key * @key_len: Key length in octets * @mac_addr: MAC address of the Credential receiver * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets */ struct wps_credential { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u16 auth_type; u16 encr_type; u8 key_idx; u8 key[64]; size_t key_len; u8 mac_addr[ETH_ALEN]; const u8 *cred_attr; size_t cred_attr_len; }; #define WPS_DEV_TYPE_LEN 8 #define WPS_DEV_TYPE_BUFSIZE 21 #define WPS_SEC_DEV_TYPE_MAX_LEN 128 /* maximum number of advertised WPS vendor extension attributes */ #define MAX_WPS_VENDOR_EXTENSIONS 10 /* maximum size of WPS Vendor extension attribute */ #define WPS_MAX_VENDOR_EXT_LEN 1024 /* maximum number of parsed WPS vendor extension attributes */ #define MAX_WPS_PARSE_VENDOR_EXT 10 /** * struct wps_device_data - WPS Device Data * @mac_addr: Device MAC address * @device_name: Device Name (0..32 octets encoded in UTF-8) * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8) * @model_name: Model Name (0..32 octets encoded in UTF-8) * @model_number: Model Number (0..32 octets encoded in UTF-8) * @serial_number: Serial Number (0..32 octets encoded in UTF-8) * @pri_dev_type: Primary Device Type * @sec_dev_type: Array of secondary device types * @num_sec_dev_type: Number of secondary device types * @os_version: OS Version * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags) * @p2p: Whether the device is a P2P device */ struct wps_device_data { u8 mac_addr[ETH_ALEN]; char *device_name; char *manufacturer; char *model_name; char *model_number; char *serial_number; u8 pri_dev_type[WPS_DEV_TYPE_LEN]; #define WPS_SEC_DEVICE_TYPES 5 u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN]; u8 num_sec_dev_types; u32 os_version; u8 rf_bands; u16 config_methods; struct wpabuf *vendor_ext_m1; struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; struct wpabuf *application_ext; int p2p; u8 multi_ap_ext; }; /** * struct wps_config - WPS configuration for a single registration protocol run */ struct wps_config { /** * wps - Pointer to long term WPS context */ struct wps_context *wps; /** * registrar - Whether this end is a Registrar */ int registrar; /** * pin - Enrollee Device Password (%NULL for Registrar or PBC) */ const u8 *pin; /** * pin_len - Length on pin in octets */ size_t pin_len; /** * pbc - Whether this is protocol run uses PBC */ int pbc; /** * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP) */ const struct wpabuf *assoc_wps_ie; /** * new_ap_settings - New AP settings (%NULL if not used) * * This parameter provides new AP settings when using a wireless * stations as a Registrar to configure the AP. %NULL means that AP * will not be reconfigured, i.e., the station will only learn the * current AP settings by using AP PIN. */ const struct wps_credential *new_ap_settings; /** * peer_addr: MAC address of the peer in AP; %NULL if not AP */ const u8 *peer_addr; /** * use_psk_key - Use PSK format key in Credential * * Force PSK format to be used instead of ASCII passphrase when * building Credential for an Enrollee. The PSK value is set in * struct wpa_context::psk. */ int use_psk_key; /** * dev_pw_id - Device Password ID for Enrollee when PIN is used */ u16 dev_pw_id; /** * p2p_dev_addr - P2P Device Address from (Re)Association Request * * On AP/GO, this is set to the P2P Device Address of the associating * P2P client if a P2P IE is included in the (Re)Association Request * frame and the P2P Device Address is included. Otherwise, this is set * to %NULL to indicate the station does not have a P2P Device Address. */ const u8 *p2p_dev_addr; /** * pbc_in_m1 - Do not remove PushButton config method in M1 (AP) * * This can be used to enable a workaround to allow Windows 7 to use * PBC with the AP. */ int pbc_in_m1; /** * peer_pubkey_hash - Peer public key hash or %NULL if not known */ const u8 *peer_pubkey_hash; /** * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA * enrollee */ int multi_ap_backhaul_sta; }; struct wps_data * wps_init(const struct wps_config *cfg); void wps_deinit(struct wps_data *data); /** * enum wps_process_res - WPS message processing result */ enum wps_process_res { /** * WPS_DONE - Processing done */ WPS_DONE, /** * WPS_CONTINUE - Processing continues */ WPS_CONTINUE, /** * WPS_FAILURE - Processing failed */ WPS_FAILURE, /** * WPS_PENDING - Processing continues, but waiting for an external * event (e.g., UPnP message from an external Registrar) */ WPS_PENDING }; enum wps_process_res wps_process_msg(struct wps_data *wps, enum wsc_op_code op_code, const struct wpabuf *msg); struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code); int wps_is_selected_pbc_registrar(const struct wpabuf *msg); int wps_is_selected_pin_registrar(const struct wpabuf *msg); int wps_ap_priority_compar(const struct wpabuf *wps_a, const struct wpabuf *wps_b); int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, int ver1_compat); const u8 * wps_get_uuid_e(const struct wpabuf *msg); int wps_is_20(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); struct wpabuf * wps_build_assoc_resp_ie(void); struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, enum wps_request_type req_type, unsigned int num_req_dev_types, const u8 *req_dev_types); /** * struct wps_registrar_config - WPS Registrar configuration */ struct wps_registrar_config { /** * new_psk_cb - Callback for new PSK * @ctx: Higher layer context data (cb_ctx) * @mac_addr: MAC address of the Enrollee * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not * @psk: The new PSK * @psk_len: The length of psk in octets * Returns: 0 on success, -1 on failure * * This callback is called when a new per-device PSK is provisioned. */ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len); /** * set_ie_cb - Callback for WPS IE changes * @ctx: Higher layer context data (cb_ctx) * @beacon_ie: WPS IE for Beacon * @probe_resp_ie: WPS IE for Probe Response * Returns: 0 on success, -1 on failure * * This callback is called whenever the WPS IE in Beacon or Probe * Response frames needs to be changed (AP only). Callee is responsible * for freeing the buffers. */ int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie, struct wpabuf *probe_resp_ie); /** * pin_needed_cb - Callback for requesting a PIN * @ctx: Higher layer context data (cb_ctx) * @uuid_e: UUID-E of the unknown Enrollee * @dev: Device Data from the unknown Enrollee * * This callback is called whenever an unknown Enrollee requests to use * PIN method and a matching PIN (Device Password) is not found in * Registrar data. */ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev); /** * reg_success_cb - Callback for reporting successful registration * @ctx: Higher layer context data (cb_ctx) * @mac_addr: MAC address of the Enrollee * @uuid_e: UUID-E of the Enrollee * @dev_pw: Device Password (PIN) used during registration * @dev_pw_len: Length of dev_pw in octets * * This callback is called whenever an Enrollee completes registration * successfully. */ void (*reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len); /** * set_sel_reg_cb - Callback for reporting selected registrar changes * @ctx: Higher layer context data (cb_ctx) * @sel_reg: Whether the Registrar is selected * @dev_passwd_id: Device Password ID to indicate with method or * specific password the Registrar intends to use * @sel_reg_config_methods: Bit field of active config methods * * This callback is called whenever the Selected Registrar state * changes (e.g., a new PIN becomes available or PBC is invoked). This * callback is only used by External Registrar implementation; * set_ie_cb() is used by AP implementation in similar caes, but it * provides the full WPS IE data instead of just the minimal Registrar * state information. */ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); /** * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq * @ctx: Higher layer context data (cb_ctx) * @addr: MAC address of the Enrollee * @uuid_e: UUID of the Enrollee * @pri_dev_type: Primary device type * @config_methods: Config Methods * @dev_password_id: Device Password ID * @request_type: Request Type * @dev_name: Device Name (if available) */ void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, const u8 *pri_dev_type, u16 config_methods, u16 dev_password_id, u8 request_type, const char *dev_name); /** * lookup_pskfile_cb - Callback for searching for PSK in wpa_psk_file * @ctx: Higher layer context data (cb_ctx) * @addr: Enrollee's MAC address * @psk: Pointer to found PSK (output arg) */ int (*lookup_pskfile_cb)(void *ctx, const u8 *mac_addr, const u8 **psk); /** * cb_ctx: Higher layer context data for Registrar callbacks */ void *cb_ctx; /** * skip_cred_build: Do not build credential * * This option can be used to disable internal code that builds * Credential attribute into M8 based on the current network * configuration and Enrollee capabilities. The extra_cred data will * then be used as the Credential(s). */ int skip_cred_build; /** * extra_cred: Additional Credential attribute(s) * * This optional data (set to %NULL to disable) can be used to add * Credential attribute(s) for other networks into M8. If * skip_cred_build is set, this will also override the automatically * generated Credential attribute. */ const u8 *extra_cred; /** * extra_cred_len: Length of extra_cred in octets */ size_t extra_cred_len; /** * disable_auto_conf - Disable auto-configuration on first registration * * By default, the AP that is started in not configured state will * generate a random PSK and move to configured state when the first * registration protocol run is completed successfully. This option can * be used to disable this functionality and leave it up to an external * program to take care of configuration. This requires the extra_cred * to be set with a suitable Credential and skip_cred_build being used. */ int disable_auto_conf; /** * dualband - Whether this is a concurrent dualband AP */ int dualband; /** * force_per_enrollee_psk - Force per-Enrollee random PSK * * This forces per-Enrollee random PSK to be generated even if a default * PSK is set for a network. */ int force_per_enrollee_psk; /** * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul * enrollee * * This SSID is used by the Registrar to fill in information for * Credentials when the enrollee advertises it is a Multi-AP backhaul * STA. */ const u8 *multi_ap_backhaul_ssid; /** * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in * octets */ size_t multi_ap_backhaul_ssid_len; /** * multi_ap_backhaul_network_key - The Network Key (PSK) for the * Multi-AP backhaul enrollee. * * This key can be either the ASCII passphrase (8..63 characters) or the * 32-octet PSK (64 hex characters). */ const u8 *multi_ap_backhaul_network_key; /** * multi_ap_backhaul_network_key_len - Length of * multi_ap_backhaul_network_key in octets */ size_t multi_ap_backhaul_network_key_len; }; /** * enum wps_event - WPS event types */ enum wps_event { /** * WPS_EV_M2D - M2D received (Registrar did not know us) */ WPS_EV_M2D, /** * WPS_EV_FAIL - Registration failed */ WPS_EV_FAIL, /** * WPS_EV_SUCCESS - Registration succeeded */ WPS_EV_SUCCESS, /** * WPS_EV_PWD_AUTH_FAIL - Password authentication failed */ WPS_EV_PWD_AUTH_FAIL, /** * WPS_EV_PBC_OVERLAP - PBC session overlap detected */ WPS_EV_PBC_OVERLAP, /** * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start */ WPS_EV_PBC_TIMEOUT, /** * WPS_EV_PBC_ACTIVE - PBC mode was activated */ WPS_EV_PBC_ACTIVE, /** * WPS_EV_PBC_DISABLE - PBC mode was disabled */ WPS_EV_PBC_DISABLE, /** * WPS_EV_ER_AP_ADD - ER: AP added */ WPS_EV_ER_AP_ADD, /** * WPS_EV_ER_AP_REMOVE - ER: AP removed */ WPS_EV_ER_AP_REMOVE, /** * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added */ WPS_EV_ER_ENROLLEE_ADD, /** * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed */ WPS_EV_ER_ENROLLEE_REMOVE, /** * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned */ WPS_EV_ER_AP_SETTINGS, /** * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event */ WPS_EV_ER_SET_SELECTED_REGISTRAR, /** * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN */ WPS_EV_AP_PIN_SUCCESS }; /** * union wps_event_data - WPS event data */ union wps_event_data { /** * struct wps_event_m2d - M2D event data */ struct wps_event_m2d { u16 config_methods; const u8 *manufacturer; size_t manufacturer_len; const u8 *model_name; size_t model_name_len; const u8 *model_number; size_t model_number_len; const u8 *serial_number; size_t serial_number_len; const u8 *dev_name; size_t dev_name_len; const u8 *primary_dev_type; /* 8 octets */ u16 config_error; u16 dev_password_id; } m2d; /** * struct wps_event_fail - Registration failure information * @msg: enum wps_msg_type */ struct wps_event_fail { int msg; u16 config_error; u16 error_indication; u8 peer_macaddr[ETH_ALEN]; } fail; struct wps_event_success { u8 peer_macaddr[ETH_ALEN]; } success; struct wps_event_pwd_auth_fail { int enrollee; int part; u8 peer_macaddr[ETH_ALEN]; } pwd_auth_fail; struct wps_event_er_ap { const u8 *uuid; const u8 *mac_addr; const char *friendly_name; const char *manufacturer; const char *manufacturer_url; const char *model_description; const char *model_name; const char *model_number; const char *model_url; const char *serial_number; const char *upc; const u8 *pri_dev_type; u8 wps_state; } ap; struct wps_event_er_enrollee { const u8 *uuid; const u8 *mac_addr; int m1_received; u16 config_methods; u16 dev_passwd_id; const u8 *pri_dev_type; const char *dev_name; const char *manufacturer; const char *model_name; const char *model_number; const char *serial_number; } enrollee; struct wps_event_er_ap_settings { const u8 *uuid; const struct wps_credential *cred; } ap_settings; struct wps_event_er_set_selected_registrar { const u8 *uuid; int sel_reg; u16 dev_passwd_id; u16 sel_reg_config_methods; enum { WPS_ER_SET_SEL_REG_START, WPS_ER_SET_SEL_REG_DONE, WPS_ER_SET_SEL_REG_FAILED } state; } set_sel_reg; }; /** * struct upnp_pending_message - Pending PutWLANResponse messages * @next: Pointer to next pending message or %NULL * @addr: NewWLANEventMAC * @msg: NewMessage * @type: Message Type */ struct upnp_pending_message { struct upnp_pending_message *next; u8 addr[ETH_ALEN]; struct wpabuf *msg; enum wps_msg_type type; }; /** * struct wps_context - Long term WPS context data * * This data is stored at the higher layer Authenticator or Supplicant data * structures and it is maintained over multiple registration protocol runs. */ struct wps_context { /** * ap - Whether the local end is an access point */ int ap; /** * registrar - Pointer to WPS registrar data from wps_registrar_init() */ struct wps_registrar *registrar; /** * wps_state - Current WPS state */ enum wps_state wps_state; /** * ap_setup_locked - Whether AP setup is locked (only used at AP) */ int ap_setup_locked; /** * uuid - Own UUID */ u8 uuid[16]; /** * ssid - SSID * * This SSID is used by the Registrar to fill in information for * Credentials. In addition, AP uses it when acting as an Enrollee to * notify Registrar of the current configuration. */ u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of ssid in octets */ size_t ssid_len; /** * dev - Own WPS device data */ struct wps_device_data dev; /** * dh_ctx - Context data for Diffie-Hellman operation */ void *dh_ctx; /** * dh_privkey - Diffie-Hellman private key */ struct wpabuf *dh_privkey; /** * dh_pubkey_oob - Diffie-Hellman public key */ struct wpabuf *dh_pubkey; /** * config_methods - Enabled configuration methods * * Bit field of WPS_CONFIG_* */ u16 config_methods; /** * encr_types - Enabled encryption types (bit field of WPS_ENCR_*) */ u16 encr_types; /** * encr_types_rsn - Enabled encryption types for RSN (WPS_ENCR_*) */ u16 encr_types_rsn; /** * encr_types_wpa - Enabled encryption types for WPA (WPS_ENCR_*) */ u16 encr_types_wpa; /** * auth_types - Authentication types (bit field of WPS_AUTH_*) */ u16 auth_types; /** * encr_types - Current AP encryption type (WPS_ENCR_*) */ u16 ap_encr_type; /** * ap_auth_type - Current AP authentication types (WPS_AUTH_*) */ u16 ap_auth_type; /** * network_key - The current Network Key (PSK) or %NULL to generate new * * If %NULL, Registrar will generate per-device PSK. In addition, AP * uses this when acting as an Enrollee to notify Registrar of the * current configuration. * * When using WPA/WPA2-Personal, this key can be either the ASCII * passphrase (8..63 characters) or the 32-octet PSK (64 hex * characters). When this is set to the ASCII passphrase, the PSK can * be provided in the psk buffer and used per-Enrollee to control which * key type is included in the Credential (e.g., to reduce calculation * need on low-powered devices by provisioning PSK while still allowing * other devices to get the passphrase). */ u8 *network_key; /** * network_key_len - Length of network_key in octets */ size_t network_key_len; /** * psk - The current network PSK * * This optional value can be used to provide the current PSK if * network_key is set to the ASCII passphrase. */ u8 psk[32]; /** * psk_set - Whether psk value is set */ int psk_set; /** * ap_settings - AP Settings override for M7 (only used at AP) * * If %NULL, AP Settings attributes will be generated based on the * current network configuration. */ u8 *ap_settings; /** * ap_settings_len - Length of ap_settings in octets */ size_t ap_settings_len; /** * friendly_name - Friendly Name (required for UPnP) */ char *friendly_name; /** * manufacturer_url - Manufacturer URL (optional for UPnP) */ char *manufacturer_url; /** * model_description - Model Description (recommended for UPnP) */ char *model_description; /** * model_url - Model URL (optional for UPnP) */ char *model_url; /** * upc - Universal Product Code (optional for UPnP) */ char *upc; /** * cred_cb - Callback to notify that new Credentials were received * @ctx: Higher layer context data (cb_ctx) * @cred: The received Credential * Return: 0 on success, -1 on failure */ int (*cred_cb)(void *ctx, const struct wps_credential *cred); /** * event_cb - Event callback (state information about progress) * @ctx: Higher layer context data (cb_ctx) * @event: Event type * @data: Event data */ void (*event_cb)(void *ctx, enum wps_event event, union wps_event_data *data); /** * rf_band_cb - Fetch currently used RF band * @ctx: Higher layer context data (cb_ctx) * Return: Current used RF band or 0 if not known */ int (*rf_band_cb)(void *ctx); /** * cb_ctx: Higher layer context data for callbacks */ void *cb_ctx; struct upnp_wps_device_sm *wps_upnp; /* Pending messages from UPnP PutWLANResponse */ struct upnp_pending_message *upnp_msgs; u16 ap_nfc_dev_pw_id; struct wpabuf *ap_nfc_dh_pubkey; struct wpabuf *ap_nfc_dh_privkey; struct wpabuf *ap_nfc_dev_pw; + + /* Whether to send WPA2-PSK passphrase as a passphrase instead of PSK + * for WPA3-Personal transition mode needs. */ + bool use_passphrase; }; struct wps_registrar * wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg); void wps_registrar_deinit(struct wps_registrar *reg); int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, const u8 *uuid, const u8 *pin, size_t pin_len, int timeout); int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_wps_cancel(struct wps_registrar *reg); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_button_pushed(struct wps_registrar *reg, const u8 *p2p_dev_addr); void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data, int p2p_wildcard); int wps_registrar_update_ie(struct wps_registrar *reg); int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, char *buf, size_t buflen); int wps_registrar_config_ap(struct wps_registrar *reg, struct wps_credential *cred); int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, const u8 *pubkey_hash, u16 pw_id, const u8 *dev_pw, size_t dev_pw_len, int pk_hash_provided_oob); int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, const u8 *oob_dev_pw, size_t oob_dev_pw_len); void wps_registrar_flush(struct wps_registrar *reg); int wps_registrar_update_multi_ap(struct wps_registrar *reg, const u8 *multi_ap_backhaul_ssid, size_t multi_ap_backhaul_ssid_len, const u8 *multi_ap_backhaul_network_key, size_t multi_ap_backhaul_network_key_len); int wps_build_credential_wrap(struct wpabuf *msg, const struct wps_credential *cred); unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); int wps_generate_pin(unsigned int *pin); int wps_pin_str_valid(const char *pin); void wps_free_pending_msgs(struct upnp_pending_message *msgs); struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, int channel); int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr); int wps_attr_text(struct wpabuf *data, char *buf, char *end); const char * wps_ei_str(enum wps_error_indication ei); struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname, const char *filter); void wps_er_refresh(struct wps_er *er); void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx); void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr); const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr); int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, const u8 *pin, size_t pin_len); int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, const struct wps_credential *cred); int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, const u8 *pin, size_t pin_len, const struct wps_credential *cred); struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, struct wps_credential *cred); struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, const u8 *addr); struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, struct wps_context *wps, const u8 *uuid, const u8 *addr, struct wpabuf *pubkey); int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]); char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, size_t buf_len); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); u16 wps_config_methods_str2bin(const char *str); struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, const struct wpabuf *pubkey, const struct wpabuf *dev_pw); struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey, struct wpabuf *dev_pw); int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey); struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, struct wpabuf **privkey, struct wpabuf **dev_pw); struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, struct wpabuf *nfc_dh_pubkey); struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, struct wpabuf *nfc_dh_pubkey, const u8 *bssid, int freq); struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, struct wpabuf *nfc_dh_pubkey); struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, int nfc_dev_pw_id, struct wpabuf *nfc_dh_pubkey, struct wpabuf *nfc_dev_pw); /* ndef.c */ struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); struct wpabuf * ndef_build_wifi(const struct wpabuf *buf); struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf); struct wpabuf * ndef_build_p2p(const struct wpabuf *buf); #ifdef CONFIG_WPS_STRICT int wps_validate_beacon(const struct wpabuf *wps_ie); int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe, const u8 *addr); int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr); int wps_validate_assoc_req(const struct wpabuf *wps_ie); int wps_validate_assoc_resp(const struct wpabuf *wps_ie); int wps_validate_m1(const struct wpabuf *tlvs); int wps_validate_m2(const struct wpabuf *tlvs); int wps_validate_m2d(const struct wpabuf *tlvs); int wps_validate_m3(const struct wpabuf *tlvs); int wps_validate_m4(const struct wpabuf *tlvs); int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2); int wps_validate_m5(const struct wpabuf *tlvs); int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2); int wps_validate_m6(const struct wpabuf *tlvs); int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2); int wps_validate_m7(const struct wpabuf *tlvs); int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2); int wps_validate_m8(const struct wpabuf *tlvs); int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2); int wps_validate_wsc_ack(const struct wpabuf *tlvs); int wps_validate_wsc_nack(const struct wpabuf *tlvs); int wps_validate_wsc_done(const struct wpabuf *tlvs); int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs); #else /* CONFIG_WPS_STRICT */ static inline int wps_validate_beacon(const struct wpabuf *wps_ie){ return 0; } static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe, const u8 *addr) { return 0; } static inline int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr) { return 0; } static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie) { return 0; } static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie) { return 0; } static inline int wps_validate_m1(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m2(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m2d(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m3(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m4(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2) { return 0; } static inline int wps_validate_m5(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2) { return 0; } static inline int wps_validate_m6(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2) { return 0; } static inline int wps_validate_m7(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2) { return 0; } static inline int wps_validate_m8(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2) { return 0; } static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_wsc_done(const struct wpabuf *tlvs) { return 0; } static inline int wps_validate_upnp_set_selected_registrar( const struct wpabuf *tlvs) { return 0; } #endif /* CONFIG_WPS_STRICT */ #endif /* WPS_H */ diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 45f7e947e0fd..173fbbd68aa0 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1,3793 +1,3795 @@ /* * Wi-Fi Protected Setup - Registrar * Copyright (c) 2008-2016, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/base64.h" #include "utils/eloop.h" #include "utils/uuid.h" #include "utils/list.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" #include "wps_i.h" #include "wps_dev_attr.h" #include "wps_upnp.h" #include "wps_upnp_i.h" #ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS #endif /* CONFIG_WPS_STRICT */ #ifdef CONFIG_WPS_NFC struct wps_nfc_pw_token { struct dl_list list; u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; unsigned int peer_pk_hash_known:1; u16 pw_id; u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1]; size_t dev_pw_len; int pk_hash_provided_oob; /* whether own PK hash was provided OOB */ }; static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token) { dl_list_del(&token->list); bin_clear_free(token, sizeof(*token)); } static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id) { struct wps_nfc_pw_token *token, *prev; dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token, list) { if (pw_id == 0 || pw_id == token->pw_id) wps_remove_nfc_pw_token(token); } } static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens, u16 pw_id) { struct wps_nfc_pw_token *token; dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) { if (pw_id == token->pw_id) return token; } return NULL; } #else /* CONFIG_WPS_NFC */ #define wps_free_nfc_pw_tokens(t, p) do { } while (0) #endif /* CONFIG_WPS_NFC */ struct wps_uuid_pin { struct dl_list list; u8 uuid[WPS_UUID_LEN]; int wildcard_uuid; u8 *pin; size_t pin_len; #define PIN_LOCKED BIT(0) #define PIN_EXPIRES BIT(1) int flags; struct os_reltime expiration; u8 enrollee_addr[ETH_ALEN]; }; static void wps_free_pin(struct wps_uuid_pin *pin) { bin_clear_free(pin->pin, pin->pin_len); os_free(pin); } static void wps_remove_pin(struct wps_uuid_pin *pin) { dl_list_del(&pin->list); wps_free_pin(pin); } static void wps_free_pins(struct dl_list *pins) { struct wps_uuid_pin *pin, *prev; dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list) wps_remove_pin(pin); } struct wps_pbc_session { struct wps_pbc_session *next; u8 addr[ETH_ALEN]; u8 uuid_e[WPS_UUID_LEN]; struct os_reltime timestamp; }; static void wps_free_pbc_sessions(struct wps_pbc_session *pbc) { struct wps_pbc_session *prev; while (pbc) { prev = pbc; pbc = pbc->next; os_free(prev); } } struct wps_registrar_device { struct wps_registrar_device *next; struct wps_device_data dev; u8 uuid[WPS_UUID_LEN]; }; struct wps_registrar { struct wps_context *wps; int pbc; int selected_registrar; int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len); int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie, struct wpabuf *probe_resp_ie); void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev); void (*reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len); void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, const u8 *pri_dev_type, u16 config_methods, u16 dev_password_id, u8 request_type, const char *dev_name); int (*lookup_pskfile_cb)(void *ctx, const u8 *mac_addr, const u8 **psk); void *cb_ctx; struct dl_list pins; struct dl_list nfc_pw_tokens; struct wps_pbc_session *pbc_sessions; int skip_cred_build; struct wpabuf *extra_cred; int disable_auto_conf; int sel_reg_union; int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; int dualband; int force_per_enrollee_psk; struct wps_registrar_device *devices; int force_pbc_overlap; u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; u8 pbc_ignore_uuid[WPS_UUID_LEN]; #ifdef WPS_WORKAROUNDS struct os_reltime pbc_ignore_start; #endif /* WPS_WORKAROUNDS */ /** * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul * enrollee * * This SSID is used by the Registrar to fill in information for * Credentials when the enrollee advertises it is a Multi-AP backhaul * STA. */ u8 multi_ap_backhaul_ssid[SSID_MAX_LEN]; /** * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in * octets */ size_t multi_ap_backhaul_ssid_len; /** * multi_ap_backhaul_network_key - The Network Key (PSK) for the * Multi-AP backhaul enrollee. * * This key can be either the ASCII passphrase (8..63 characters) or the * 32-octet PSK (64 hex characters). */ u8 *multi_ap_backhaul_network_key; /** * multi_ap_backhaul_network_key_len - Length of * multi_ap_backhaul_network_key in octets */ size_t multi_ap_backhaul_network_key_len; }; static int wps_set_ie(struct wps_registrar *reg); static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_remove_pin(struct wps_registrar *reg, struct wps_uuid_pin *pin); static void wps_registrar_add_authorized_mac(struct wps_registrar *reg, const u8 *addr) { int i; wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR, MAC2STR(addr)); for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was " "already in the list"); return; /* already in list */ } for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--) os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1], ETH_ALEN); os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN); wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs", (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs)); } static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg, const u8 *addr) { int i; wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR, MAC2STR(addr)); for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) { if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0) break; } if (i == WPS_MAX_AUTHORIZED_MACS) { wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the " "list"); return; /* not in the list */ } for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++) os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1], ETH_ALEN); os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0, ETH_ALEN); wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs", (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs)); } static void wps_free_devices(struct wps_registrar_device *dev) { struct wps_registrar_device *prev; while (dev) { prev = dev; dev = dev->next; wps_device_data_free(&prev->dev); os_free(prev); } } static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg, const u8 *addr) { struct wps_registrar_device *dev; for (dev = reg->devices; dev; dev = dev->next) { if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0) return dev; } return NULL; } static void wps_device_clone_data(struct wps_device_data *dst, struct wps_device_data *src) { os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN); os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); #define WPS_STRDUP(n) \ os_free(dst->n); \ dst->n = src->n ? os_strdup(src->n) : NULL WPS_STRDUP(device_name); WPS_STRDUP(manufacturer); WPS_STRDUP(model_name); WPS_STRDUP(model_number); WPS_STRDUP(serial_number); #undef WPS_STRDUP } int wps_device_store(struct wps_registrar *reg, struct wps_device_data *dev, const u8 *uuid) { struct wps_registrar_device *d; d = wps_device_get(reg, dev->mac_addr); if (d == NULL) { d = os_zalloc(sizeof(*d)); if (d == NULL) return -1; d->next = reg->devices; reg->devices = d; } wps_device_clone_data(&d->dev, dev); os_memcpy(d->uuid, uuid, WPS_UUID_LEN); return 0; } static void wps_registrar_add_pbc_session(struct wps_registrar *reg, const u8 *addr, const u8 *uuid_e) { struct wps_pbc_session *pbc, *prev = NULL; struct os_reltime now; os_get_reltime(&now); pbc = reg->pbc_sessions; while (pbc) { if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 && os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) { if (prev) prev->next = pbc->next; else reg->pbc_sessions = pbc->next; break; } prev = pbc; pbc = pbc->next; } if (!pbc) { pbc = os_zalloc(sizeof(*pbc)); if (pbc == NULL) return; os_memcpy(pbc->addr, addr, ETH_ALEN); if (uuid_e) os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN); } pbc->next = reg->pbc_sessions; reg->pbc_sessions = pbc; pbc->timestamp = now; /* remove entries that have timed out */ prev = pbc; pbc = pbc->next; while (pbc) { if (os_reltime_expired(&now, &pbc->timestamp, WPS_PBC_WALK_TIME)) { prev->next = NULL; wps_free_pbc_sessions(pbc); break; } prev = pbc; pbc = pbc->next; } } static void wps_registrar_remove_pbc_session(struct wps_registrar *reg, const u8 *uuid_e, const u8 *p2p_dev_addr) { struct wps_pbc_session *pbc, *prev = NULL, *tmp; pbc = reg->pbc_sessions; while (pbc) { if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 || (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) && os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == 0)) { if (prev) prev->next = pbc->next; else reg->pbc_sessions = pbc->next; tmp = pbc; pbc = pbc->next; wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for " "addr=" MACSTR, MAC2STR(tmp->addr)); wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E", tmp->uuid_e, WPS_UUID_LEN); os_free(tmp); continue; } prev = pbc; pbc = pbc->next; } } int wps_registrar_pbc_overlap(struct wps_registrar *reg, const u8 *addr, const u8 *uuid_e) { int count = 0; struct wps_pbc_session *pbc; struct wps_pbc_session *first = NULL; struct os_reltime now; os_get_reltime(&now); wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap"); if (uuid_e) { wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID"); wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID", uuid_e, WPS_UUID_LEN); count++; } for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) { wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR, MAC2STR(pbc->addr)); wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", pbc->uuid_e, WPS_UUID_LEN); if (os_reltime_expired(&now, &pbc->timestamp, WPS_PBC_WALK_TIME)) { wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired"); break; } if (first && os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) { wpa_printf(MSG_DEBUG, "WPS: Same Enrollee"); continue; /* same Enrollee */ } if (uuid_e == NULL || os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) { wpa_printf(MSG_DEBUG, "WPS: New Enrollee"); count++; } if (first == NULL) first = pbc; } wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count); return count > 1 ? 1 : 0; } static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)", wps->wps_state); wpabuf_put_be16(msg, ATTR_WPS_STATE); wpabuf_put_be16(msg, 1); wpabuf_put_u8(msg, wps->wps_state); return 0; } #ifdef CONFIG_WPS_UPNP static void wps_registrar_free_pending_m2(struct wps_context *wps) { struct upnp_pending_message *p, *p2, *prev = NULL; p = wps->upnp_msgs; while (p) { if (p->type == WPS_M2 || p->type == WPS_M2D) { if (prev == NULL) wps->upnp_msgs = p->next; else prev->next = p->next; wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D"); p2 = p; p = p->next; wpabuf_free(p2->msg); os_free(p2); continue; } prev = p; p = p->next; } } #endif /* CONFIG_WPS_UPNP */ static int wps_build_ap_setup_locked(struct wps_context *wps, struct wpabuf *msg) { if (wps->ap_setup_locked && wps->ap_setup_locked != 2) { wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked"); wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED); wpabuf_put_be16(msg, 1); wpabuf_put_u8(msg, 1); } return 0; } static int wps_build_selected_registrar(struct wps_registrar *reg, struct wpabuf *msg) { if (!reg->sel_reg_union) return 0; wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar"); wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); wpabuf_put_be16(msg, 1); wpabuf_put_u8(msg, 1); return 0; } static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg, struct wpabuf *msg) { u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; if (!reg->sel_reg_union) return 0; if (reg->sel_reg_dev_password_id_override >= 0) id = reg->sel_reg_dev_password_id_override; wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id); wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, id); return 0; } static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg, struct wpabuf *msg) { u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; if (!reg->sel_reg_union) return 0; if (reg->sel_reg_dev_password_id_override >= 0) id = reg->sel_reg_dev_password_id_override; if (id != DEV_PW_PUSHBUTTON || !reg->dualband) return 0; return wps_build_uuid_e(msg, reg->wps->uuid); } static void wps_set_pushbutton(u16 *methods, u16 conf_methods) { *methods |= WPS_CONFIG_PUSHBUTTON; if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == WPS_CONFIG_VIRT_PUSHBUTTON) *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) == WPS_CONFIG_PHY_PUSHBUTTON) *methods |= WPS_CONFIG_PHY_PUSHBUTTON; if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) != WPS_CONFIG_VIRT_PUSHBUTTON && (*methods & WPS_CONFIG_PHY_PUSHBUTTON) != WPS_CONFIG_PHY_PUSHBUTTON) { /* * Required to include virtual/physical flag, but we were not * configured with push button type, so have to default to one * of them. */ *methods |= WPS_CONFIG_PHY_PUSHBUTTON; } } static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; if (!reg->sel_reg_union) return 0; methods = reg->wps->config_methods; methods &= ~WPS_CONFIG_PUSHBUTTON; methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); if (reg->pbc) wps_set_pushbutton(&methods, reg->wps->config_methods); if (reg->sel_reg_config_methods_override >= 0) methods = reg->sel_reg_config_methods_override; wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)", methods); wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, methods); return 0; } static int wps_build_probe_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; /* * These are the methods that the AP supports as an Enrollee for adding * external Registrars. */ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, methods); return 0; } static int wps_build_config_methods_r(struct wps_registrar *reg, struct wpabuf *msg) { return wps_build_config_methods(msg, reg->wps->config_methods); } const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count) { *count = 0; while (*count < WPS_MAX_AUTHORIZED_MACS) { if (is_zero_ether_addr(reg->authorized_macs_union[*count])) break; (*count)++; } return (const u8 *) reg->authorized_macs_union; } /** * wps_registrar_init - Initialize WPS Registrar data * @wps: Pointer to longterm WPS context * @cfg: Registrar configuration * Returns: Pointer to allocated Registrar data or %NULL on failure * * This function is used to initialize WPS Registrar functionality. It can be * used for a single Registrar run (e.g., when run in a supplicant) or multiple * runs (e.g., when run as an internal Registrar in an AP). Caller is * responsible for freeing the returned data with wps_registrar_deinit() when * Registrar functionality is not needed anymore. */ struct wps_registrar * wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg) { struct wps_registrar *reg = os_zalloc(sizeof(*reg)); if (reg == NULL) return NULL; dl_list_init(®->pins); dl_list_init(®->nfc_pw_tokens); reg->wps = wps; reg->new_psk_cb = cfg->new_psk_cb; reg->set_ie_cb = cfg->set_ie_cb; reg->pin_needed_cb = cfg->pin_needed_cb; reg->reg_success_cb = cfg->reg_success_cb; reg->set_sel_reg_cb = cfg->set_sel_reg_cb; reg->enrollee_seen_cb = cfg->enrollee_seen_cb; reg->lookup_pskfile_cb = cfg->lookup_pskfile_cb; reg->cb_ctx = cfg->cb_ctx; reg->skip_cred_build = cfg->skip_cred_build; if (cfg->extra_cred) { reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred, cfg->extra_cred_len); if (reg->extra_cred == NULL) { os_free(reg); return NULL; } } reg->disable_auto_conf = cfg->disable_auto_conf; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; reg->dualband = cfg->dualband; reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; if (cfg->multi_ap_backhaul_ssid) { os_memcpy(reg->multi_ap_backhaul_ssid, cfg->multi_ap_backhaul_ssid, cfg->multi_ap_backhaul_ssid_len); reg->multi_ap_backhaul_ssid_len = cfg->multi_ap_backhaul_ssid_len; } if (cfg->multi_ap_backhaul_network_key) { reg->multi_ap_backhaul_network_key = os_memdup(cfg->multi_ap_backhaul_network_key, cfg->multi_ap_backhaul_network_key_len); if (reg->multi_ap_backhaul_network_key) reg->multi_ap_backhaul_network_key_len = cfg->multi_ap_backhaul_network_key_len; } if (wps_set_ie(reg)) { wps_registrar_deinit(reg); return NULL; } return reg; } void wps_registrar_flush(struct wps_registrar *reg) { if (reg == NULL) return; wps_free_pins(®->pins); wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); wps_free_pbc_sessions(reg->pbc_sessions); reg->pbc_sessions = NULL; wps_free_devices(reg->devices); reg->devices = NULL; #ifdef WPS_WORKAROUNDS reg->pbc_ignore_start.sec = 0; #endif /* WPS_WORKAROUNDS */ } /** * wps_registrar_deinit - Deinitialize WPS Registrar data * @reg: Registrar data from wps_registrar_init() */ void wps_registrar_deinit(struct wps_registrar *reg) { if (reg == NULL) return; eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_registrar_flush(reg); wpabuf_clear_free(reg->extra_cred); bin_clear_free(reg->multi_ap_backhaul_network_key, reg->multi_ap_backhaul_network_key_len); os_free(reg); } static void wps_registrar_invalidate_unused(struct wps_registrar *reg) { struct wps_uuid_pin *pin; dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) { wpa_printf(MSG_DEBUG, "WPS: Invalidate previously " "configured wildcard PIN"); wps_registrar_remove_pin(reg, pin); break; } } } /** * wps_registrar_add_pin - Configure a new PIN for Registrar * @reg: Registrar data from wps_registrar_init() * @addr: Enrollee MAC address or %NULL if not known * @uuid: UUID-E or %NULL for wildcard (any UUID) * @pin: PIN (Device Password) * @pin_len: Length of pin in octets * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout * Returns: 0 on success, -1 on failure */ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, const u8 *uuid, const u8 *pin, size_t pin_len, int timeout) { struct wps_uuid_pin *p; p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; if (addr) os_memcpy(p->enrollee_addr, addr, ETH_ALEN); if (uuid == NULL) p->wildcard_uuid = 1; else os_memcpy(p->uuid, uuid, WPS_UUID_LEN); p->pin = os_memdup(pin, pin_len); if (p->pin == NULL) { os_free(p); return -1; } p->pin_len = pin_len; if (timeout) { p->flags |= PIN_EXPIRES; os_get_reltime(&p->expiration); p->expiration.sec += timeout; } if (p->wildcard_uuid) wps_registrar_invalidate_unused(reg); dl_list_add(®->pins, &p->list); wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", timeout); wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN); wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; if (addr) wps_registrar_add_authorized_mac(reg, addr); else wps_registrar_add_authorized_mac( reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg, 0); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_set_selected_timeout, reg, NULL); return 0; } static void wps_registrar_remove_pin(struct wps_registrar *reg, struct wps_uuid_pin *pin) { u8 *addr; u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (is_zero_ether_addr(pin->enrollee_addr)) addr = bcast; else addr = pin->enrollee_addr; wps_registrar_remove_authorized_mac(reg, addr); wps_remove_pin(pin); wps_registrar_selected_registrar_changed(reg, 0); } static void wps_registrar_expire_pins(struct wps_registrar *reg) { struct wps_uuid_pin *pin, *prev; struct os_reltime now; os_get_reltime(&now); dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { if ((pin->flags & PIN_EXPIRES) && os_reltime_before(&pin->expiration, &now)) { wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", pin->uuid, WPS_UUID_LEN); wps_registrar_remove_pin(reg, pin); } } } /** * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN * @reg: Registrar data from wps_registrar_init() * @dev_pw: PIN to search for or %NULL to match any * @dev_pw_len: Length of dev_pw in octets * Returns: 0 on success, -1 if not wildcard PIN is enabled */ static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, const u8 *dev_pw, size_t dev_pw_len) { struct wps_uuid_pin *pin, *prev; dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { if (dev_pw && pin->pin && (dev_pw_len != pin->pin_len || os_memcmp_const(dev_pw, pin->pin, dev_pw_len) != 0)) continue; /* different PIN */ if (pin->wildcard_uuid) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); wps_registrar_remove_pin(reg, pin); return 0; } } return -1; } /** * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E * @reg: Registrar data from wps_registrar_init() * @uuid: UUID-E * Returns: 0 on success, -1 on failure (e.g., PIN not found) */ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) { struct wps_uuid_pin *pin, *prev; dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); wps_registrar_remove_pin(reg, pin); return 0; } } return -1; } static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, const u8 *uuid, size_t *pin_len) { struct wps_uuid_pin *pin, *found = NULL; int wildcard = 0; wps_registrar_expire_pins(reg); dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (!pin->wildcard_uuid && os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { found = pin; break; } } if (!found) { /* Check for wildcard UUIDs since none of the UUID-specific * PINs matched */ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (pin->wildcard_uuid == 1 || pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); wildcard = 1; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); found = pin; break; } } } if (!found) return NULL; /* * Lock the PIN to avoid attacks based on concurrent re-use of the PIN * that could otherwise avoid PIN invalidations. */ if (found->flags & PIN_LOCKED) { wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not " "allow concurrent re-use"); return NULL; } *pin_len = found->pin_len; found->flags |= PIN_LOCKED; if (wildcard) found->wildcard_uuid++; return found->pin; } /** * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E * @reg: Registrar data from wps_registrar_init() * @uuid: UUID-E * Returns: 0 on success, -1 on failure * * PINs are locked to enforce only one concurrent use. This function unlocks a * PIN to allow it to be used again. If the specified PIN was configured using * a wildcard UUID, it will be removed instead of allowing multiple uses. */ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) { struct wps_uuid_pin *pin; dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { if (pin->wildcard_uuid == 3) { wpa_printf(MSG_DEBUG, "WPS: Invalidating used " "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); } pin->flags &= ~PIN_LOCKED; return 0; } } return -1; } static void wps_registrar_stop_pbc(struct wps_registrar *reg) { reg->selected_registrar = 0; reg->pbc = 0; os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); wps_registrar_remove_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg, 0); } static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx) { struct wps_registrar *reg = eloop_ctx; wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode"); wps_pbc_timeout_event(reg->wps); wps_registrar_stop_pbc(reg); } /** * wps_registrar_button_pushed - Notify Registrar that AP button was pushed * @reg: Registrar data from wps_registrar_init() * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL * indicates no such filtering * Returns: 0 on success, -1 on failure, -2 on session overlap * * This function is called on an AP when a push button is pushed to activate * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout * or when a PBC registration is completed. If more than one Enrollee in active * PBC mode has been detected during the monitor time (previous 2 minutes), the * PBC mode is not activated and -2 is returned to indicate session overlap. * This is skipped if a specific Enrollee is selected. */ int wps_registrar_button_pushed(struct wps_registrar *reg, const u8 *p2p_dev_addr) { if (p2p_dev_addr == NULL && wps_registrar_pbc_overlap(reg, NULL, NULL)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC " "mode"); wps_pbc_overlap_event(reg->wps); return -2; } wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started"); reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; if (p2p_dev_addr) os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); else os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); wps_registrar_add_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg, 0); wps_pbc_active_event(reg->wps); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, reg, NULL); return 0; } static void wps_registrar_pbc_completed(struct wps_registrar *reg) { wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode"); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); wps_registrar_stop_pbc(reg); wps_pbc_disable_event(reg->wps); } static void wps_registrar_pin_completed(struct wps_registrar *reg) { wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar"); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); reg->selected_registrar = 0; wps_registrar_selected_registrar_changed(reg, 0); } void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len) { if (registrar->pbc) { wps_registrar_remove_pbc_session(registrar, uuid_e, NULL); wps_registrar_pbc_completed(registrar); #ifdef WPS_WORKAROUNDS os_get_reltime(®istrar->pbc_ignore_start); #endif /* WPS_WORKAROUNDS */ os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN); } else { wps_registrar_pin_completed(registrar); } if (dev_pw && wps_registrar_invalidate_wildcard_pin(registrar, dev_pw, dev_pw_len) == 0) { wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN", dev_pw, dev_pw_len); } } int wps_registrar_wps_cancel(struct wps_registrar *reg) { if (reg->pbc) { wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it"); wps_registrar_pbc_timeout(reg, NULL); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); return 1; } else if (reg->selected_registrar) { /* PIN Method */ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it"); wps_registrar_pin_completed(reg); wps_registrar_invalidate_wildcard_pin(reg, NULL, 0); return 1; } return 0; } /** * wps_registrar_probe_req_rx - Notify Registrar of Probe Request * @reg: Registrar data from wps_registrar_init() * @addr: MAC address of the Probe Request sender * @wps_data: WPS IE contents * * This function is called on an AP when a Probe Request with WPS IE is * received. This is used to track PBC mode use and to detect possible overlap * situation with other WPS APs. */ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data, int p2p_wildcard) { struct wps_parse_attr attr; int skip_add = 0; wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Probe Request with WPS data received", wps_data); if (wps_parse_msg(wps_data, &attr) < 0) return; if (attr.config_methods == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in " "Probe Request"); return; } if (attr.dev_password_id == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute " "in Probe Request"); return; } if (reg->enrollee_seen_cb && attr.uuid_e && attr.primary_dev_type && attr.request_type && !p2p_wildcard) { char *dev_name = NULL; if (attr.dev_name) { dev_name = os_zalloc(attr.dev_name_len + 1); if (dev_name) { os_memcpy(dev_name, attr.dev_name, attr.dev_name_len); } } reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e, attr.primary_dev_type, WPA_GET_BE16(attr.config_methods), WPA_GET_BE16(attr.dev_password_id), *attr.request_type, dev_name); os_free(dev_name); } if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON) return; /* Not PBC */ wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from " MACSTR, MAC2STR(addr)); if (attr.uuid_e == NULL) { wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No " "UUID-E included"); return; } wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e, WPS_UUID_LEN); #ifdef WPS_WORKAROUNDS if (reg->pbc_ignore_start.sec && os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) { struct os_reltime now, dur; os_get_reltime(&now); os_reltime_sub(&now, ®->pbc_ignore_start, &dur); if (dur.sec >= 0 && dur.sec < 5) { wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation " "based on Probe Request from the Enrollee " "that just completed PBC provisioning"); skip_add = 1; } else reg->pbc_ignore_start.sec = 0; } #endif /* WPS_WORKAROUNDS */ if (!skip_add) wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected"); reg->force_pbc_overlap = 1; wps_pbc_overlap_event(reg->wps); } } int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { if (reg->new_psk_cb == NULL) return 0; return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk, psk_len); } static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e, const struct wps_device_data *dev) { if (reg->pin_needed_cb == NULL) return; reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev); } static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr, const u8 *uuid_e, const u8 *dev_pw, size_t dev_pw_len) { if (reg->reg_success_cb == NULL) return; reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len); } static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie, struct wpabuf *probe_resp_ie) { return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie); } static void wps_cb_set_sel_reg(struct wps_registrar *reg) { u16 methods = 0; if (reg->set_sel_reg_cb == NULL) return; if (reg->selected_registrar) { methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); if (reg->pbc) wps_set_pushbutton(&methods, reg->wps->config_methods); } wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d " "config_methods=0x%x pbc=%d methods=0x%x", reg->selected_registrar, reg->wps->config_methods, reg->pbc, methods); reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar, reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT, methods); } static int wps_cp_lookup_pskfile(struct wps_registrar *reg, const u8 *mac_addr, const u8 **psk) { if (!reg->lookup_pskfile_cb) return 0; return reg->lookup_pskfile_cb(reg->cb_ctx, mac_addr, psk); } static int wps_set_ie(struct wps_registrar *reg) { struct wpabuf *beacon; struct wpabuf *probe; const u8 *auth_macs; size_t count; size_t vendor_len = 0; int i; if (reg->set_ie_cb == NULL) return 0; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { if (reg->wps->dev.vendor_ext[i]) { vendor_len += 2 + 2; vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]); } } beacon = wpabuf_alloc(400 + vendor_len); probe = wpabuf_alloc(500 + vendor_len); if (!beacon || !probe) goto fail; auth_macs = wps_authorized_macs(reg, &count); wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs"); if (wps_build_version(beacon) || wps_build_wps_state(reg->wps, beacon) || wps_build_ap_setup_locked(reg->wps, beacon) || wps_build_selected_registrar(reg, beacon) || wps_build_sel_reg_dev_password_id(reg, beacon) || wps_build_sel_reg_config_methods(reg, beacon) || wps_build_sel_pbc_reg_uuid_e(reg, beacon) || (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon, 0)) || wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, beacon) || wps_build_application_ext(®->wps->dev, beacon)) goto fail; #ifdef CONFIG_P2P if (wps_build_dev_name(®->wps->dev, beacon) || wps_build_primary_dev_type(®->wps->dev, beacon)) goto fail; #endif /* CONFIG_P2P */ wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs"); if (wps_build_version(probe) || wps_build_wps_state(reg->wps, probe) || wps_build_ap_setup_locked(reg->wps, probe) || wps_build_selected_registrar(reg, probe) || wps_build_sel_reg_dev_password_id(reg, probe) || wps_build_sel_reg_config_methods(reg, probe) || wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP : WPS_RESP_REGISTRAR) || wps_build_uuid_e(probe, reg->wps->uuid) || wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || wps_build_wfa_ext(probe, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, probe) || wps_build_application_ext(®->wps->dev, probe)) goto fail; beacon = wps_ie_encapsulate(beacon); probe = wps_ie_encapsulate(probe); if (!beacon || !probe) goto fail; return wps_cb_set_ie(reg, beacon, probe); fail: wpabuf_free(beacon); wpabuf_free(probe); return -1; } static int wps_get_dev_password(struct wps_data *wps) { const u8 *pin; size_t pin_len = 0; bin_clear_free(wps->dev_password, wps->dev_password_len); wps->dev_password = NULL; if (wps->pbc) { wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC"); pin = (const u8 *) "00000000"; pin_len = 8; #ifdef CONFIG_WPS_NFC } else if (wps->nfc_pw_token) { if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { wpa_printf(MSG_DEBUG, "WPS: Using NFC connection " "handover and abbreviated WPS handshake " "without Device Password"); return 0; } wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC " "Password Token"); pin = wps->nfc_pw_token->dev_pw; pin_len = wps->nfc_pw_token->dev_pw_len; } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id && wps->wps->ap_nfc_dev_pw) { wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token"); pin = wpabuf_head(wps->wps->ap_nfc_dev_pw); pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw); #endif /* CONFIG_WPS_NFC */ } else { pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e, &pin_len); if (pin && wps->dev_pw_id >= 0x10) { wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device " "Password ID, but PIN found"); /* * See whether Enrollee is willing to use PIN instead. */ wps->dev_pw_id = DEV_PW_DEFAULT; } } if (pin == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Device Password available for " "the Enrollee (context %p registrar %p)", wps->wps, wps->wps->registrar); wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e, &wps->peer_dev); return -1; } wps->dev_password = os_memdup(pin, pin_len); if (wps->dev_password == NULL) return -1; wps->dev_password_len = pin_len; return 0; } static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * UUID-R"); wpabuf_put_be16(msg, ATTR_UUID_R); wpabuf_put_be16(msg, WPS_UUID_LEN); wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN); return 0; } static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg) { u8 *hash; const u8 *addr[4]; size_t len[4]; if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) return -1; wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: R-S2", wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN); if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) { wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for " "R-Hash derivation"); return -1; } wpa_printf(MSG_DEBUG, "WPS: * R-Hash1"); wpabuf_put_be16(msg, ATTR_R_HASH1); wpabuf_put_be16(msg, SHA256_MAC_LEN); hash = wpabuf_put(msg, SHA256_MAC_LEN); /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */ addr[0] = wps->snonce; len[0] = WPS_SECRET_NONCE_LEN; addr[1] = wps->psk1; len[1] = WPS_PSK_LEN; addr[2] = wpabuf_head(wps->dh_pubkey_e); len[2] = wpabuf_len(wps->dh_pubkey_e); addr[3] = wpabuf_head(wps->dh_pubkey_r); len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN); wpa_printf(MSG_DEBUG, "WPS: * R-Hash2"); wpabuf_put_be16(msg, ATTR_R_HASH2); wpabuf_put_be16(msg, SHA256_MAC_LEN); hash = wpabuf_put(msg, SHA256_MAC_LEN); /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN; addr[1] = wps->psk2; hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN); return 0; } static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * R-SNonce1"); wpabuf_put_be16(msg, ATTR_R_SNONCE1); wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN); return 0; } static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * R-SNonce2"); wpabuf_put_be16(msg, ATTR_R_SNONCE2); wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN); wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN); return 0; } static int wps_build_cred_network_idx(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)"); wpabuf_put_be16(msg, ATTR_NETWORK_INDEX); wpabuf_put_be16(msg, 1); wpabuf_put_u8(msg, 1); return 0; } static int wps_build_cred_ssid(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * SSID"); wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential", cred->ssid, cred->ssid_len); wpabuf_put_be16(msg, ATTR_SSID); wpabuf_put_be16(msg, cred->ssid_len); wpabuf_put_data(msg, cred->ssid, cred->ssid_len); return 0; } static int wps_build_cred_auth_type(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", cred->auth_type); wpabuf_put_be16(msg, ATTR_AUTH_TYPE); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, cred->auth_type); return 0; } static int wps_build_cred_encr_type(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", cred->encr_type); wpabuf_put_be16(msg, ATTR_ENCR_TYPE); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, cred->encr_type); return 0; } static int wps_build_cred_network_key(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)", (int) cred->key_len); wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", cred->key, cred->key_len); wpabuf_put_be16(msg, ATTR_NETWORK_KEY); wpabuf_put_be16(msg, cred->key_len); wpabuf_put_data(msg, cred->key, cred->key_len); return 0; } static int wps_build_credential(struct wpabuf *msg, const struct wps_credential *cred) { if (wps_build_cred_network_idx(msg, cred) || wps_build_cred_ssid(msg, cred) || wps_build_cred_auth_type(msg, cred) || wps_build_cred_encr_type(msg, cred) || wps_build_cred_network_key(msg, cred) || wps_build_mac_addr(msg, cred->mac_addr)) return -1; return 0; } int wps_build_credential_wrap(struct wpabuf *msg, const struct wps_credential *cred) { struct wpabuf *wbuf; wbuf = wpabuf_alloc(200); if (wbuf == NULL) return -1; if (wps_build_credential(wbuf, cred)) { wpabuf_clear_free(wbuf); return -1; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(wbuf)); wpabuf_put_buf(msg, wbuf); wpabuf_clear_free(wbuf); return 0; } int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; struct wps_registrar *reg = wps->wps->registrar; const u8 *pskfile_psk; char hex[65]; if (wps->wps->registrar->skip_cred_build) goto skip_cred_build; wpa_printf(MSG_DEBUG, "WPS: * Credential"); if (wps->use_cred) { os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred)); goto use_provided; } os_memset(&wps->cred, 0, sizeof(wps->cred)); if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA && reg->multi_ap_backhaul_ssid_len) { wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials"); os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid, reg->multi_ap_backhaul_ssid_len); wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len; /* Backhaul is always WPA2PSK */ wps->cred.auth_type = WPS_AUTH_WPA2PSK; wps->cred.encr_type = WPS_ENCR_AES; /* Set MAC address in the Credential to be the Enrollee's MAC * address */ os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); if (reg->multi_ap_backhaul_network_key) { os_memcpy(wps->cred.key, reg->multi_ap_backhaul_network_key, reg->multi_ap_backhaul_network_key_len); wps->cred.key_len = reg->multi_ap_backhaul_network_key_len; } goto use_provided; } os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len); wps->cred.ssid_len = wps->wps->ssid_len; /* Select the best authentication and encryption type */ wpa_printf(MSG_DEBUG, "WPS: Own auth types 0x%x - masked Enrollee auth types 0x%x", wps->wps->auth_types, wps->auth_type); if (wps->auth_type & WPS_AUTH_WPA2PSK) wps->auth_type = WPS_AUTH_WPA2PSK; #ifndef CONFIG_NO_TKIP else if (wps->auth_type & WPS_AUTH_WPAPSK) wps->auth_type = WPS_AUTH_WPAPSK; #endif /* CONFIG_NO_TKIP */ else if (wps->auth_type & WPS_AUTH_OPEN) wps->auth_type = WPS_AUTH_OPEN; else { wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x", wps->auth_type); return -1; } wps->cred.auth_type = wps->auth_type; wpa_printf(MSG_DEBUG, "WPS: Own encr types 0x%x (rsn: 0x%x, wpa: 0x%x) - masked Enrollee encr types 0x%x", wps->wps->encr_types, wps->wps->encr_types_rsn, wps->wps->encr_types_wpa, wps->encr_type); if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPA2PSK) wps->encr_type &= wps->wps->encr_types_rsn; else if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPAPSK) wps->encr_type &= wps->wps->encr_types_wpa; if (wps->auth_type == WPS_AUTH_WPA2PSK || wps->auth_type == WPS_AUTH_WPAPSK) { if (wps->encr_type & WPS_ENCR_AES) wps->encr_type = WPS_ENCR_AES; #ifndef CONFIG_NO_TKIP else if (wps->encr_type & WPS_ENCR_TKIP) wps->encr_type = WPS_ENCR_TKIP; #endif /* CONFIG_NO_TKIP */ else { wpa_printf(MSG_DEBUG, "WPS: No suitable encryption " "type for WPA/WPA2"); return -1; } } else { if (wps->encr_type & WPS_ENCR_NONE) wps->encr_type = WPS_ENCR_NONE; #ifdef CONFIG_TESTING_OPTIONS else if (wps->encr_type & WPS_ENCR_WEP) wps->encr_type = WPS_ENCR_WEP; #endif /* CONFIG_TESTING_OPTIONS */ else { wpa_printf(MSG_DEBUG, "WPS: No suitable encryption " "type for non-WPA/WPA2 mode"); return -1; } } wps->cred.encr_type = wps->encr_type; /* * Set MAC address in the Credential to be the Enrollee's MAC address */ os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap && !wps->wps->registrar->disable_auto_conf) { u8 r[16]; /* Generate a random passphrase */ if (random_pool_ready() != 1 || random_get_bytes(r, sizeof(r)) < 0) { wpa_printf(MSG_INFO, "WPS: Could not generate random PSK"); return -1; } os_free(wps->new_psk); wps->new_psk = (u8 *) base64_encode(r, sizeof(r), &wps->new_psk_len); if (wps->new_psk == NULL) return -1; wps->new_psk_len--; /* remove newline */ while (wps->new_psk_len && wps->new_psk[wps->new_psk_len - 1] == '=') wps->new_psk_len--; wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase", wps->new_psk, wps->new_psk_len); os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len); wps->cred.key_len = wps->new_psk_len; } else if (wps_cp_lookup_pskfile(reg, wps->mac_addr_e, &pskfile_psk)) { wpa_hexdump_key(MSG_DEBUG, "WPS: Use PSK from wpa_psk_file", pskfile_psk, PMK_LEN); wpa_snprintf_hex(hex, sizeof(hex), pskfile_psk, PMK_LEN); os_memcpy(wps->cred.key, hex, PMK_LEN * 2); wps->cred.key_len = PMK_LEN * 2; } else if (!wps->wps->registrar->force_per_enrollee_psk && wps->use_psk_key && wps->wps->psk_set) { wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key"); wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, PMK_LEN); os_memcpy(wps->cred.key, hex, PMK_LEN * 2); wps->cred.key_len = PMK_LEN * 2; - } else if (!wps->wps->registrar->force_per_enrollee_psk && - wps->wps->network_key) { + } else if ((!wps->wps->registrar->force_per_enrollee_psk || + wps->wps->use_passphrase) && wps->wps->network_key) { + wpa_printf(MSG_DEBUG, + "WPS: Use passphrase format for Network key"); os_memcpy(wps->cred.key, wps->wps->network_key, wps->wps->network_key_len); wps->cred.key_len = wps->wps->network_key_len; } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { /* Generate a random per-device PSK */ os_free(wps->new_psk); wps->new_psk_len = PMK_LEN; wps->new_psk = os_malloc(wps->new_psk_len); if (wps->new_psk == NULL) return -1; if (random_pool_ready() != 1 || random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { wpa_printf(MSG_INFO, "WPS: Could not generate random PSK"); os_free(wps->new_psk); wps->new_psk = NULL; return -1; } wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK", wps->new_psk, wps->new_psk_len); wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk, wps->new_psk_len); os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2); wps->cred.key_len = wps->new_psk_len * 2; } use_provided: #ifdef CONFIG_WPS_TESTING if (wps_testing_dummy_cred) cred = wpabuf_alloc(200); else cred = NULL; if (cred) { struct wps_credential dummy; wpa_printf(MSG_DEBUG, "WPS: Add dummy credential"); os_memset(&dummy, 0, sizeof(dummy)); os_memcpy(dummy.ssid, "dummy", 5); dummy.ssid_len = 5; dummy.auth_type = WPS_AUTH_WPA2PSK; dummy.encr_type = WPS_ENCR_AES; os_memcpy(dummy.key, "dummy psk", 9); dummy.key_len = 9; os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN); wps_build_credential(cred, &dummy); wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred); wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(cred)); wpabuf_put_buf(msg, cred); wpabuf_free(cred); } #endif /* CONFIG_WPS_TESTING */ cred = wpabuf_alloc(200); if (cred == NULL) return -1; if (wps_build_credential(cred, &wps->cred)) { wpabuf_clear_free(cred); return -1; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(cred)); wpabuf_put_buf(msg, cred); wpabuf_clear_free(cred); skip_cred_build: if (wps->wps->registrar->extra_cred) { wpa_printf(MSG_DEBUG, "WPS: * Credential (pre-configured)"); wpabuf_put_buf(msg, wps->wps->registrar->extra_cred); } return 0; } static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * AP Settings"); if (wps_build_credential(msg, &wps->cred)) return -1; return 0; } static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) { struct wpabuf *msg, *plain; msg = wpabuf_alloc(1000); if (msg == NULL) return NULL; plain = wpabuf_alloc(200); if (plain == NULL) { wpabuf_free(msg); return NULL; } if (wps_build_ap_settings(wps, plain)) { wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(plain)); wpabuf_put_buf(msg, plain); wpabuf_clear_free(plain); return msg; } static struct wpabuf * wps_build_m2(struct wps_data *wps) { struct wpabuf *msg; int config_in_m2 = 0; if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce", wps->nonce_r, WPS_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN); wpa_printf(MSG_DEBUG, "WPS: Building Message M2"); msg = wpabuf_alloc(1000); if (msg == NULL) return NULL; if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M2) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || wps_build_uuid_r(wps, msg) || wps_build_public_key(wps, msg) || wps_derive_keys(wps) || wps_build_auth_type_flags(wps, msg) || wps_build_encr_type_flags(wps, msg) || wps_build_conn_type_flags(wps, msg) || wps_build_config_methods_r(wps->wps->registrar, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || wps_build_rf_bands(&wps->wps->dev, msg, wps->wps->rf_band_cb(wps->wps->cb_ctx)) || wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_os_version(&wps->wps->dev, msg) || wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } #ifdef CONFIG_WPS_NFC if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob && wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { /* * Use abbreviated handshake since public key hash allowed * Enrollee to validate our public key similarly to how Enrollee * public key was validated. There is no need to validate Device * Password in this case. */ struct wpabuf *plain = wpabuf_alloc(500); if (plain == NULL || wps_build_cred(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain)) { wpabuf_free(msg); wpabuf_clear_free(plain); return NULL; } wpabuf_clear_free(plain); config_in_m2 = 1; } #endif /* CONFIG_WPS_NFC */ if (wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; } wps->int_reg = 1; wps->state = config_in_m2 ? RECV_DONE : RECV_M3; return msg; } static struct wpabuf * wps_build_m2d(struct wps_data *wps) { struct wpabuf *msg; u16 err = wps->config_error; wpa_printf(MSG_DEBUG, "WPS: Building Message M2D"); msg = wpabuf_alloc(1000); if (msg == NULL) return NULL; if (wps->wps->ap && wps->wps->ap_setup_locked && err == WPS_CFG_NO_ERROR) err = WPS_CFG_SETUP_LOCKED; if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M2D) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || wps_build_uuid_r(wps, msg) || wps_build_auth_type_flags(wps, msg) || wps_build_encr_type_flags(wps, msg) || wps_build_conn_type_flags(wps, msg) || wps_build_config_methods_r(wps->wps->registrar, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || wps_build_rf_bands(&wps->wps->dev, msg, wps->wps->rf_band_cb(wps->wps->cb_ctx)) || wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || wps_build_os_version(&wps->wps->dev, msg) || wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } wps->state = RECV_M2D_ACK; return msg; } static struct wpabuf * wps_build_m4(struct wps_data *wps) { struct wpabuf *msg, *plain; wpa_printf(MSG_DEBUG, "WPS: Building Message M4"); if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0) return NULL; plain = wpabuf_alloc(200); if (plain == NULL) return NULL; msg = wpabuf_alloc(1000); if (msg == NULL) { wpabuf_free(plain); return NULL; } if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M4) || wps_build_enrollee_nonce(wps, msg) || wps_build_r_hash(wps, msg) || wps_build_r_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } wpabuf_clear_free(plain); wps->state = RECV_M5; return msg; } static struct wpabuf * wps_build_m6(struct wps_data *wps) { struct wpabuf *msg, *plain; wpa_printf(MSG_DEBUG, "WPS: Building Message M6"); plain = wpabuf_alloc(200); if (plain == NULL) return NULL; msg = wpabuf_alloc(1000); if (msg == NULL) { wpabuf_free(plain); return NULL; } if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M6) || wps_build_enrollee_nonce(wps, msg) || wps_build_r_snonce2(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } wpabuf_clear_free(plain); wps->wps_pin_revealed = 1; wps->state = RECV_M7; return msg; } static struct wpabuf * wps_build_m8(struct wps_data *wps) { struct wpabuf *msg, *plain; wpa_printf(MSG_DEBUG, "WPS: Building Message M8"); plain = wpabuf_alloc(500); if (plain == NULL) return NULL; msg = wpabuf_alloc(1000); if (msg == NULL) { wpabuf_free(plain); return NULL; } if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M8) || wps_build_enrollee_nonce(wps, msg) || ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) || (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_clear_free(msg); return NULL; } wpabuf_clear_free(plain); wps->state = RECV_DONE; return msg; } struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { struct wpabuf *msg; #ifdef CONFIG_WPS_UPNP if (!wps->int_reg && wps->wps->wps_upnp) { struct upnp_pending_message *p, *prev = NULL; if (wps->ext_reg > 1) wps_registrar_free_pending_m2(wps->wps); p = wps->wps->upnp_msgs; /* TODO: check pending message MAC address */ while (p && p->next) { prev = p; p = p->next; } if (p) { wpa_printf(MSG_DEBUG, "WPS: Use pending message from " "UPnP"); if (prev) prev->next = NULL; else wps->wps->upnp_msgs = NULL; msg = p->msg; switch (p->type) { case WPS_WSC_ACK: *op_code = WSC_ACK; break; case WPS_WSC_NACK: *op_code = WSC_NACK; break; default: *op_code = WSC_MSG; break; } os_free(p); if (wps->ext_reg == 0) wps->ext_reg = 1; return msg; } } if (wps->ext_reg) { wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no " "pending message available"); return NULL; } #endif /* CONFIG_WPS_UPNP */ switch (wps->state) { case SEND_M2: if (wps_get_dev_password(wps) < 0) msg = wps_build_m2d(wps); else msg = wps_build_m2(wps); *op_code = WSC_MSG; break; case SEND_M2D: msg = wps_build_m2d(wps); *op_code = WSC_MSG; break; case SEND_M4: msg = wps_build_m4(wps); *op_code = WSC_MSG; break; case SEND_M6: msg = wps_build_m6(wps); *op_code = WSC_MSG; break; case SEND_M8: msg = wps_build_m8(wps); *op_code = WSC_MSG; break; case RECV_DONE: msg = wps_build_wsc_ack(wps); *op_code = WSC_ACK; break; case SEND_WSC_NACK: msg = wps_build_wsc_nack(wps); *op_code = WSC_NACK; break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building " "a message", wps->state); msg = NULL; break; } if (*op_code == WSC_MSG && msg) { /* Save a copy of the last message for Authenticator derivation */ wpabuf_free(wps->last_msg); wps->last_msg = wpabuf_dup(msg); } return msg; } static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce) { if (e_nonce == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received"); return -1; } os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", wps->nonce_e, WPS_NONCE_LEN); return 0; } static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce) { if (r_nonce == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received"); return -1; } if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received"); return -1; } return 0; } static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e) { if (uuid_e == NULL) { wpa_printf(MSG_DEBUG, "WPS: No UUID-E received"); return -1; } os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN); return 0; } static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id) { if (pw_id == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received"); return -1; } wps->dev_pw_id = WPA_GET_BE16(pw_id); wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id); return 0; } static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1) { if (e_hash1 == NULL) { wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received"); return -1; } os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN); wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN); return 0; } static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2) { if (e_hash2 == NULL) { wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received"); return -1; } os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN); wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN); return 0; } static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1) { u8 hash[SHA256_MAC_LEN]; const u8 *addr[4]; size_t len[4]; if (e_snonce1 == NULL) { wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received"); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1, WPS_SECRET_NONCE_LEN); /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */ addr[0] = e_snonce1; len[0] = WPS_SECRET_NONCE_LEN; addr[1] = wps->psk1; len[1] = WPS_PSK_LEN; addr[2] = wpabuf_head(wps->dh_pubkey_e); len[2] = wpabuf_len(wps->dh_pubkey_e); addr[3] = wpabuf_head(wps->dh_pubkey_r); len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does " "not match with the pre-committed value"); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; wps_pwd_auth_fail_event(wps->wps, 0, 1, wps->mac_addr_e); return -1; } wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first " "half of the device password"); return 0; } static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2) { u8 hash[SHA256_MAC_LEN]; const u8 *addr[4]; size_t len[4]; if (e_snonce2 == NULL) { wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received"); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2, WPS_SECRET_NONCE_LEN); /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */ addr[0] = e_snonce2; len[0] = WPS_SECRET_NONCE_LEN; addr[1] = wps->psk2; len[1] = WPS_PSK_LEN; addr[2] = wpabuf_head(wps->dh_pubkey_e); len[2] = wpabuf_len(wps->dh_pubkey_e); addr[3] = wpabuf_head(wps->dh_pubkey_r); len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does " "not match with the pre-committed value"); wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; wps_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e); return -1; } wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second " "half of the device password"); wps->wps_pin_revealed = 0; wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e); /* * In case wildcard PIN is used and WPS handshake succeeds in the first * attempt, wps_registrar_unlock_pin() would not free the PIN, so make * sure the PIN gets invalidated here. */ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); return 0; } static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr) { if (mac_addr == NULL) { wpa_printf(MSG_DEBUG, "WPS: No MAC Address received"); return -1; } wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR, MAC2STR(mac_addr)); os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN); os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN); return 0; } static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, size_t pk_len) { if (pk == NULL || pk_len == 0) { wpa_printf(MSG_DEBUG, "WPS: No Public Key received"); return -1; } wpabuf_free(wps->dh_pubkey_e); wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_e == NULL) return -1; return 0; } static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth) { u16 auth_types; if (auth == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags " "received"); return -1; } auth_types = WPA_GET_BE16(auth); wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x", auth_types); #ifdef WPS_WORKAROUNDS /* * Some deployed implementations seem to advertise incorrect information * in this attribute. A value of 0x1b (WPA2 + WPA + WPAPSK + OPEN, but * no WPA2PSK) has been reported to be used. Add WPA2PSK to the list to * avoid issues with building Credentials that do not use the strongest * actually supported authentication option (that device does support * WPA2PSK even when it does not claim it here). */ if ((auth_types & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) == (WPS_AUTH_WPA2 | WPS_AUTH_WPAPSK)) { wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee supports WPA2PSK based on claimed WPA2 support"); auth_types |= WPS_AUTH_WPA2PSK; } #endif /* WPS_WORKAROUNDS */ wps->auth_type = wps->wps->auth_types & auth_types; if (wps->auth_type == 0) { wpa_printf(MSG_DEBUG, "WPS: No match in supported " "authentication types (own 0x%x Enrollee 0x%x)", wps->wps->auth_types, auth_types); #ifdef WPS_WORKAROUNDS /* * Some deployed implementations seem to advertise incorrect * information in this attribute. For example, Linksys WRT350N * seems to have a byteorder bug that breaks this negotiation. * In order to interoperate with existing implementations, * assume that the Enrollee supports everything we do. */ wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee " "does not advertise supported authentication types " "correctly"); wps->auth_type = wps->wps->auth_types; #else /* WPS_WORKAROUNDS */ return -1; #endif /* WPS_WORKAROUNDS */ } return 0; } static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr) { u16 encr_types; if (encr == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags " "received"); return -1; } encr_types = WPA_GET_BE16(encr); wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x", encr_types); wps->encr_type = wps->wps->encr_types & encr_types; if (wps->encr_type == 0) { wpa_printf(MSG_DEBUG, "WPS: No match in supported " "encryption types (own 0x%x Enrollee 0x%x)", wps->wps->encr_types, encr_types); #ifdef WPS_WORKAROUNDS /* * Some deployed implementations seem to advertise incorrect * information in this attribute. For example, Linksys WRT350N * seems to have a byteorder bug that breaks this negotiation. * In order to interoperate with existing implementations, * assume that the Enrollee supports everything we do. */ wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee " "does not advertise supported encryption types " "correctly"); wps->encr_type = wps->wps->encr_types; #else /* WPS_WORKAROUNDS */ return -1; #endif /* WPS_WORKAROUNDS */ } return 0; } static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn) { if (conn == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags " "received"); return -1; } wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x", *conn); return 0; } static int wps_process_config_methods(struct wps_data *wps, const u8 *methods) { u16 m; if (methods == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Config Methods received"); return -1; } m = WPA_GET_BE16(methods); wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x" "%s%s%s%s%s%s%s%s%s", m, m & WPS_CONFIG_USBA ? " [USBA]" : "", m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "", m & WPS_CONFIG_LABEL ? " [Label]" : "", m & WPS_CONFIG_DISPLAY ? " [Display]" : "", m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "", m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "", m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "", m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "", m & WPS_CONFIG_KEYPAD ? " [Keypad]" : ""); if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) { /* * The Enrollee does not have a display so it is unlikely to be * able to show the passphrase to a user and as such, could * benefit from receiving PSK to reduce key derivation time. */ wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to " "Enrollee not supporting display"); wps->use_psk_key = 1; } return 0; } static int wps_process_wps_state(struct wps_data *wps, const u8 *state) { if (state == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State " "received"); return -1; } wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d", *state); return 0; } static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc) { u16 a; if (assoc == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Association State received"); return -1; } a = WPA_GET_BE16(assoc); wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a); return 0; } static int wps_process_config_error(struct wps_data *wps, const u8 *err) { u16 e; if (err == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received"); return -1; } e = WPA_GET_BE16(err); wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e); return 0; } static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps) { #ifdef CONFIG_P2P struct wps_registrar *reg = wps->wps->registrar; if (is_zero_ether_addr(reg->p2p_dev_addr)) return 1; /* no filtering in use */ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address " "filtering for PBC: expected " MACSTR " was " MACSTR " - indicate PBC session overlap", MAC2STR(reg->p2p_dev_addr), MAC2STR(wps->p2p_dev_addr)); return 0; } #endif /* CONFIG_P2P */ return 1; } static int wps_registrar_skip_overlap(struct wps_data *wps) { #ifdef CONFIG_P2P struct wps_registrar *reg = wps->wps->registrar; if (is_zero_ether_addr(reg->p2p_dev_addr)) return 0; /* no specific Enrollee selected */ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected " "Enrollee match"); return 1; } #endif /* CONFIG_P2P */ return 0; } static enum wps_process_res wps_process_m1(struct wps_data *wps, struct wps_parse_attr *attr) { wpa_printf(MSG_DEBUG, "WPS: Received M1"); if (wps->state != RECV_M1) { wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " "receiving M1", wps->state); return WPS_FAILURE; } if (wps_process_uuid_e(wps, attr->uuid_e) || wps_process_mac_addr(wps, attr->mac_addr) || wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || wps_process_auth_type_flags(wps, attr->auth_type_flags) || wps_process_encr_type_flags(wps, attr->encr_type_flags) || wps_process_conn_type_flags(wps, attr->conn_type_flags) || wps_process_config_methods(wps, attr->config_methods) || wps_process_wps_state(wps, attr->wps_state) || wps_process_device_attrs(&wps->peer_dev, attr) || wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) || wps_process_assoc_state(wps, attr->assoc_state) || wps_process_dev_password_id(wps, attr->dev_password_id) || wps_process_config_error(wps, attr->config_error) || wps_process_os_version(&wps->peer_dev, attr->os_version)) return WPS_FAILURE; if (wps->dev_pw_id < 0x10 && wps->dev_pw_id != DEV_PW_DEFAULT && wps->dev_pw_id != DEV_PW_P2PS_DEFAULT && wps->dev_pw_id != DEV_PW_USER_SPECIFIED && wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED && wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED && #ifdef CONFIG_WPS_NFC wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && #endif /* CONFIG_WPS_NFC */ (wps->dev_pw_id != DEV_PW_PUSHBUTTON || !wps->wps->registrar->pbc)) { wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d", wps->dev_pw_id); wps->state = SEND_M2D; return WPS_CONTINUE; } #ifdef CONFIG_WPS_NFC if (wps->dev_pw_id >= 0x10 || wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { struct wps_nfc_pw_token *token; const u8 *addr[1]; u8 hash[WPS_HASH_LEN]; wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)", wps->dev_pw_id, wps->wps, wps->wps->registrar); token = wps_get_nfc_pw_token( &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); if (token && token->peer_pk_hash_known) { size_t len; wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token"); dl_list_del(&token->list); wps->nfc_pw_token = token; addr[0] = attr->public_key; len = attr->public_key_len; sha256_vector(1, addr, &len, hash); if (os_memcmp_const(hash, wps->nfc_pw_token->pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN) != 0) { wpa_printf(MSG_ERROR, "WPS: Public Key hash " "mismatch"); wps->state = SEND_M2D; wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; return WPS_CONTINUE; } } else if (token) { wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token (no peer PK hash)"); wps->nfc_pw_token = token; } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id && wps->wps->ap_nfc_dev_pw) { wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token"); } } #endif /* CONFIG_WPS_NFC */ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { if ((wps->wps->registrar->force_pbc_overlap || wps_registrar_pbc_overlap(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e) || !wps_registrar_p2p_dev_addr_match(wps)) && !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC " "negotiation"); wps->state = SEND_M2D; wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; wps_pbc_overlap_event(wps->wps); wps_fail_event(wps->wps, WPS_M1, WPS_CFG_MULTIPLE_PBC_DETECTED, WPS_EI_NO_ERROR, wps->mac_addr_e); wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } wps_registrar_add_pbc_session(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); wps->pbc = 1; } #ifdef WPS_WORKAROUNDS /* * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in * passphrase format. To avoid interop issues, force PSK format to be * used. */ if (!wps->use_psk_key && wps->peer_dev.manufacturer && os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 && wps->peer_dev.model_name && os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) { wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in " "PSK format"); wps->use_psk_key = 1; } #endif /* WPS_WORKAROUNDS */ wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext); wps->state = SEND_M2; return WPS_CONTINUE; } static enum wps_process_res wps_process_m3(struct wps_data *wps, const struct wpabuf *msg, struct wps_parse_attr *attr) { wpa_printf(MSG_DEBUG, "WPS: Received M3"); if (wps->state != RECV_M3) { wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " "receiving M3", wps->state); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } if (wps->pbc && wps->wps->registrar->force_pbc_overlap && !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; return WPS_CONTINUE; } if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg) || wps_process_e_hash1(wps, attr->e_hash1) || wps_process_e_hash2(wps, attr->e_hash2)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } wps->state = SEND_M4; return WPS_CONTINUE; } static enum wps_process_res wps_process_m5(struct wps_data *wps, const struct wpabuf *msg, struct wps_parse_attr *attr) { struct wpabuf *decrypted; struct wps_parse_attr eattr; wpa_printf(MSG_DEBUG, "WPS: Received M5"); if (wps->state != RECV_M5) { wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " "receiving M5", wps->state); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } if (wps->pbc && wps->wps->registrar->force_pbc_overlap && !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; return WPS_CONTINUE; } if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, attr->encr_settings_len); if (decrypted == NULL) { wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " "Settings attribute"); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) { wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_e_snonce1(wps, eattr.e_snonce1)) { wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } wpabuf_clear_free(decrypted); wps->state = SEND_M6; return WPS_CONTINUE; } static void wps_sta_cred_cb(struct wps_data *wps) { /* * Update credential to only include a single authentication and * encryption type in case the AP configuration includes more than one * option. */ if (wps->cred.auth_type & WPS_AUTH_WPA2PSK) wps->cred.auth_type = WPS_AUTH_WPA2PSK; else if (wps->cred.auth_type & WPS_AUTH_WPAPSK) wps->cred.auth_type = WPS_AUTH_WPAPSK; if (wps->cred.encr_type & WPS_ENCR_AES) wps->cred.encr_type = WPS_ENCR_AES; else if (wps->cred.encr_type & WPS_ENCR_TKIP) wps->cred.encr_type = WPS_ENCR_TKIP; wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the " "AP configuration"); if (wps->wps->cred_cb) wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); } static void wps_cred_update(struct wps_credential *dst, struct wps_credential *src) { os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid)); dst->ssid_len = src->ssid_len; dst->auth_type = src->auth_type; dst->encr_type = src->encr_type; dst->key_idx = src->key_idx; os_memcpy(dst->key, src->key, sizeof(dst->key)); dst->key_len = src->key_len; } static int wps_process_ap_settings_r(struct wps_data *wps, struct wps_parse_attr *attr) { struct wpabuf *msg; if (wps->wps->ap || wps->er) return 0; /* AP Settings Attributes in M7 when Enrollee is an AP */ if (wps_process_ap_settings(attr, &wps->cred) < 0) return -1; wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP"); if (wps->new_ap_settings) { wpa_printf(MSG_INFO, "WPS: Update AP configuration based on " "new settings"); wps_cred_update(&wps->cred, wps->new_ap_settings); return 0; } else { /* * Use the AP PIN only to receive the current AP settings, not * to reconfigure the AP. */ /* * Clear selected registrar here since we do not get to * WSC_Done in this protocol run. */ wps_registrar_pin_completed(wps->wps->registrar); msg = wps_build_ap_cred(wps); if (msg == NULL) return -1; wps->cred.cred_attr = wpabuf_head(msg); wps->cred.cred_attr_len = wpabuf_len(msg); if (wps->ap_settings_cb) { wps->ap_settings_cb(wps->ap_settings_cb_ctx, &wps->cred); wpabuf_free(msg); return 1; } wps_sta_cred_cb(wps); wps->cred.cred_attr = NULL; wps->cred.cred_attr_len = 0; wpabuf_free(msg); return 1; } } static enum wps_process_res wps_process_m7(struct wps_data *wps, const struct wpabuf *msg, struct wps_parse_attr *attr) { struct wpabuf *decrypted; struct wps_parse_attr eattr; wpa_printf(MSG_DEBUG, "WPS: Received M7"); if (wps->state != RECV_M7) { wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " "receiving M7", wps->state); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } if (wps->pbc && wps->wps->registrar->force_pbc_overlap && !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; return WPS_CONTINUE; } if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, attr->encr_settings_len); if (decrypted == NULL) { wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt Encrypted " "Settings attribute"); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er, attr->version2 != NULL) < 0) { wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_e_snonce2(wps, eattr.e_snonce2) || wps_process_ap_settings_r(wps, &eattr)) { wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } wpabuf_clear_free(decrypted); wps->state = SEND_M8; return WPS_CONTINUE; } static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; enum wps_process_res ret = WPS_CONTINUE; wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG"); if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } if (*attr.msg_type != WPS_M1 && (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } switch (*attr.msg_type) { case WPS_M1: if (wps_validate_m1(msg) < 0) return WPS_FAILURE; #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && attr.mac_addr) { /* Remove old pending messages when starting new run */ wps_free_pending_msgs(wps->wps->upnp_msgs); wps->wps->upnp_msgs = NULL; upnp_wps_device_send_wlan_event( wps->wps->wps_upnp, attr.mac_addr, UPNP_WPS_WLANEVENT_TYPE_EAP, msg); } #endif /* CONFIG_WPS_UPNP */ ret = wps_process_m1(wps, &attr); break; case WPS_M3: if (wps_validate_m3(msg) < 0) return WPS_FAILURE; ret = wps_process_m3(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M3, wps->config_error, wps->error_indication, wps->mac_addr_e); break; case WPS_M5: if (wps_validate_m5(msg) < 0) return WPS_FAILURE; ret = wps_process_m5(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M5, wps->config_error, wps->error_indication, wps->mac_addr_e); break; case WPS_M7: if (wps_validate_m7(msg) < 0) return WPS_FAILURE; ret = wps_process_m7(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M7, wps->config_error, wps->error_indication, wps->mac_addr_e); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", *attr.msg_type); return WPS_FAILURE; } if (ret == WPS_CONTINUE) { /* Save a copy of the last message for Authenticator derivation */ wpabuf_free(wps->last_msg); wps->last_msg = wpabuf_dup(msg); } return ret; } static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK"); if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; } if (*attr.msg_type != WPS_WSC_ACK) { wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", *attr.msg_type); return WPS_FAILURE; } #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK && upnp_wps_subscribers(wps->wps->wps_upnp)) { if (wps->wps->upnp_msgs) return WPS_CONTINUE; wpa_printf(MSG_DEBUG, "WPS: Wait for response from an " "external Registrar"); return WPS_PENDING; } #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } if (wps->state == RECV_M2D_ACK) { #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && upnp_wps_subscribers(wps->wps->wps_upnp)) { if (wps->wps->upnp_msgs) return WPS_CONTINUE; if (wps->ext_reg == 0) wps->ext_reg = 1; wpa_printf(MSG_DEBUG, "WPS: Wait for response from an " "external Registrar"); return WPS_PENDING; } #endif /* CONFIG_WPS_UPNP */ wpa_printf(MSG_DEBUG, "WPS: No more registrars available - " "terminate negotiation"); } return WPS_FAILURE; } static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; int old_state; u16 config_error; wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); old_state = wps->state; wps->state = SEND_WSC_NACK; if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; } if (*attr.msg_type != WPS_WSC_NACK) { wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", *attr.msg_type); return WPS_FAILURE; } #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && wps->ext_reg) { wpa_printf(MSG_DEBUG, "WPS: Negotiation using external " "Registrar terminated by the Enrollee"); return WPS_FAILURE; } #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } if (attr.config_error == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute " "in WSC_NACK"); return WPS_FAILURE; } config_error = WPA_GET_BE16(attr.config_error); wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with " "Configuration Error %d", config_error); switch (old_state) { case RECV_M3: wps_fail_event(wps->wps, WPS_M2, config_error, wps->error_indication, wps->mac_addr_e); break; case RECV_M5: wps_fail_event(wps->wps, WPS_M4, config_error, wps->error_indication, wps->mac_addr_e); break; case RECV_M7: wps_fail_event(wps->wps, WPS_M6, config_error, wps->error_indication, wps->mac_addr_e); break; case RECV_DONE: wps_fail_event(wps->wps, WPS_M8, config_error, wps->error_indication, wps->mac_addr_e); break; default: break; } return WPS_FAILURE; } static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done"); if (wps->state != RECV_DONE && (!wps->wps->wps_upnp || !wps->ext_reg)) { wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for " "receiving WSC_Done", wps->state); return WPS_FAILURE; } if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; } if (*attr.msg_type != WPS_WSC_DONE) { wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d", *attr.msg_type); return WPS_FAILURE; } #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && wps->ext_reg) { wpa_printf(MSG_DEBUG, "WPS: Negotiation using external " "Registrar completed successfully"); wps_device_store(wps->wps->registrar, &wps->peer_dev, wps->uuid_e); return WPS_DONE; } #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully"); wps_device_store(wps->wps->registrar, &wps->peer_dev, wps->uuid_e); if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk && wps->wps->ap && !wps->wps->registrar->disable_auto_conf) { struct wps_credential cred; wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based " "on first Enrollee connection"); os_memset(&cred, 0, sizeof(cred)); os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len); cred.ssid_len = wps->wps->ssid_len; if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) { cred.auth_type = WPS_AUTH_WPA2PSK; cred.encr_type = WPS_ENCR_AES; } else { cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; } os_memcpy(cred.key, wps->new_psk, wps->new_psk_len); cred.key_len = wps->new_psk_len; wps->wps->wps_state = WPS_STATE_CONFIGURED; wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated random passphrase", wps->new_psk, wps->new_psk_len); if (wps->wps->cred_cb) wps->wps->cred_cb(wps->wps->cb_ctx, &cred); os_free(wps->new_psk); wps->new_psk = NULL; } if (!wps->wps->ap && !wps->er) wps_sta_cred_cb(wps); if (wps->new_psk) { if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e, wps->p2p_dev_addr, wps->new_psk, wps->new_psk_len)) { wpa_printf(MSG_DEBUG, "WPS: Failed to configure the " "new PSK"); } os_free(wps->new_psk); wps->new_psk = NULL; } wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e, wps->dev_password, wps->dev_password_len); if (wps->pbc) { wps_registrar_remove_pbc_session(wps->wps->registrar, wps->uuid_e, wps->p2p_dev_addr); wps_registrar_pbc_completed(wps->wps->registrar); #ifdef WPS_WORKAROUNDS os_get_reltime(&wps->wps->registrar->pbc_ignore_start); #endif /* WPS_WORKAROUNDS */ os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e, WPS_UUID_LEN); } else { wps_registrar_pin_completed(wps->wps->registrar); } /* TODO: maintain AuthorizedMACs somewhere separately for each ER and * merge them into APs own list.. */ wps_success_event(wps->wps, wps->mac_addr_e); return WPS_DONE; } enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, enum wsc_op_code op_code, const struct wpabuf *msg) { enum wps_process_res ret; wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu " "op_code=%d)", (unsigned long) wpabuf_len(msg), op_code); #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) { struct wps_parse_attr attr; if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type && *attr.msg_type == WPS_M3) wps->ext_reg = 2; /* past M2/M2D phase */ } if (wps->ext_reg > 1) wps_registrar_free_pending_m2(wps->wps); if (wps->wps->wps_upnp && wps->ext_reg && wps->wps->upnp_msgs == NULL && (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK)) { struct wps_parse_attr attr; int type; if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL) type = -1; else type = *attr.msg_type; wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)" " to external Registrar for processing", type); upnp_wps_device_send_wlan_event(wps->wps->wps_upnp, wps->mac_addr_e, UPNP_WPS_WLANEVENT_TYPE_EAP, msg); if (op_code == WSC_MSG) return WPS_PENDING; } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) { wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using " "external Registrar"); return WPS_CONTINUE; } #endif /* CONFIG_WPS_UPNP */ switch (op_code) { case WSC_MSG: return wps_process_wsc_msg(wps, msg); case WSC_ACK: if (wps_validate_wsc_ack(msg) < 0) return WPS_FAILURE; return wps_process_wsc_ack(wps, msg); case WSC_NACK: if (wps_validate_wsc_nack(msg) < 0) return WPS_FAILURE; return wps_process_wsc_nack(wps, msg); case WSC_Done: if (wps_validate_wsc_done(msg) < 0) return WPS_FAILURE; ret = wps_process_wsc_done(wps, msg); if (ret == WPS_FAILURE) { wps->state = SEND_WSC_NACK; wps_fail_event(wps->wps, WPS_WSC_DONE, wps->config_error, wps->error_indication, wps->mac_addr_e); } return ret; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); return WPS_FAILURE; } } int wps_registrar_update_ie(struct wps_registrar *reg) { return wps_set_ie(reg); } static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx) { struct wps_registrar *reg = eloop_ctx; wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - " "unselect internal Registrar"); reg->selected_registrar = 0; reg->pbc = 0; wps_registrar_expire_pins(reg); wps_registrar_selected_registrar_changed(reg, 0); } #ifdef CONFIG_WPS_UPNP static void wps_registrar_sel_reg_add(struct wps_registrar *reg, struct subscription *s) { int i, j; wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d " "config_methods=0x%x)", s->dev_password_id, s->config_methods); reg->sel_reg_union = 1; if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON) reg->sel_reg_dev_password_id_override = s->dev_password_id; if (reg->sel_reg_config_methods_override == -1) reg->sel_reg_config_methods_override = 0; reg->sel_reg_config_methods_override |= s->config_methods; for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) if (is_zero_ether_addr(reg->authorized_macs_union[i])) break; for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS; j++) { if (is_zero_ether_addr(s->authorized_macs[j])) break; wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: " MACSTR, MAC2STR(s->authorized_macs[j])); os_memcpy(reg->authorized_macs_union[i], s->authorized_macs[j], ETH_ALEN); i++; } wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union", (u8 *) reg->authorized_macs_union, sizeof(reg->authorized_macs_union)); } #endif /* CONFIG_WPS_UPNP */ static void wps_registrar_sel_reg_union(struct wps_registrar *reg) { #ifdef CONFIG_WPS_UPNP struct subscription *s; if (reg->wps->wps_upnp == NULL) return; dl_list_for_each(s, ®->wps->wps_upnp->subscriptions, struct subscription, list) { struct subscr_addr *sa; sa = dl_list_first(&s->addr_list, struct subscr_addr, list); if (sa) { wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d", inet_ntoa(sa->saddr.sin_addr), ntohs(sa->saddr.sin_port)); } if (s->selected_registrar) wps_registrar_sel_reg_add(reg, s); else wpa_printf(MSG_DEBUG, "WPS: External Registrar not " "selected"); } #endif /* CONFIG_WPS_UPNP */ } /** * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change * @reg: Registrar data from wps_registrar_init() * * This function is called when selected registrar state changes, e.g., when an * AP receives a SetSelectedRegistrar UPnP message. */ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg, u16 dev_pw_id) { wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed"); reg->sel_reg_union = reg->selected_registrar; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; os_memcpy(reg->authorized_macs_union, reg->authorized_macs, WPS_MAX_AUTHORIZED_MACS * ETH_ALEN); wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)", (u8 *) reg->authorized_macs_union, sizeof(reg->authorized_macs_union)); if (reg->selected_registrar) { u16 methods; methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); if (reg->pbc) { reg->sel_reg_dev_password_id_override = DEV_PW_PUSHBUTTON; wps_set_pushbutton(&methods, reg->wps->config_methods); } else if (dev_pw_id) reg->sel_reg_dev_password_id_override = dev_pw_id; wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected " "(pbc=%d)", reg->pbc); reg->sel_reg_config_methods_override = methods; } else wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected"); wps_registrar_sel_reg_union(reg); wps_set_ie(reg); wps_cb_set_sel_reg(reg); } int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, char *buf, size_t buflen) { struct wps_registrar_device *d; int len = 0, ret; char uuid[40]; char devtype[WPS_DEV_TYPE_BUFSIZE]; d = wps_device_get(reg, addr); if (d == NULL) return 0; if (uuid_bin2str(d->uuid, uuid, sizeof(uuid))) return 0; ret = os_snprintf(buf + len, buflen - len, "wpsUuid=%s\n" "wpsPrimaryDeviceType=%s\n" "wpsDeviceName=%s\n" "wpsManufacturer=%s\n" "wpsModelName=%s\n" "wpsModelNumber=%s\n" "wpsSerialNumber=%s\n", uuid, wps_dev_type_bin2str(d->dev.pri_dev_type, devtype, sizeof(devtype)), d->dev.device_name ? d->dev.device_name : "", d->dev.manufacturer ? d->dev.manufacturer : "", d->dev.model_name ? d->dev.model_name : "", d->dev.model_number ? d->dev.model_number : "", d->dev.serial_number ? d->dev.serial_number : ""); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; return len; } int wps_registrar_config_ap(struct wps_registrar *reg, struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type); if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { if (cred->encr_type & WPS_ENCR_WEP) { wpa_printf(MSG_INFO, "WPS: Reject new AP settings " "due to WEP configuration"); return -1; } wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " "invalid encr_type 0x%x", cred->encr_type); return -1; } if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP) { wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " "TKIP+AES"); cred->encr_type |= WPS_ENCR_AES; } if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == WPS_AUTH_WPAPSK) { wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " "WPAPSK+WPA2PSK"); cred->auth_type |= WPS_AUTH_WPA2PSK; } if (reg->wps->cred_cb) return reg->wps->cred_cb(reg->wps->cb_ctx, cred); return -1; } int wps_registrar_update_multi_ap(struct wps_registrar *reg, const u8 *multi_ap_backhaul_ssid, size_t multi_ap_backhaul_ssid_len, const u8 *multi_ap_backhaul_network_key, size_t multi_ap_backhaul_network_key_len) { if (multi_ap_backhaul_ssid) { os_memcpy(reg->multi_ap_backhaul_ssid, multi_ap_backhaul_ssid, multi_ap_backhaul_ssid_len); reg->multi_ap_backhaul_ssid_len = multi_ap_backhaul_ssid_len; } os_free(reg->multi_ap_backhaul_network_key); reg->multi_ap_backhaul_network_key = NULL; reg->multi_ap_backhaul_network_key_len = 0; if (multi_ap_backhaul_network_key) { reg->multi_ap_backhaul_network_key = os_memdup(multi_ap_backhaul_network_key, multi_ap_backhaul_network_key_len); if (!reg->multi_ap_backhaul_network_key) return -1; reg->multi_ap_backhaul_network_key_len = multi_ap_backhaul_network_key_len; } return 0; } #ifdef CONFIG_WPS_NFC int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, const u8 *pubkey_hash, u16 pw_id, const u8 *dev_pw, size_t dev_pw_len, int pk_hash_provided_oob) { struct wps_nfc_pw_token *token; if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN) return -1; if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER && (pubkey_hash == NULL || !pk_hash_provided_oob)) { wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token " "addition - missing public key hash"); return -1; } wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id); token = os_zalloc(sizeof(*token)); if (token == NULL) return -1; token->peer_pk_hash_known = pubkey_hash != NULL; if (pubkey_hash) os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); token->pw_id = pw_id; token->pk_hash_provided_oob = pk_hash_provided_oob; if (dev_pw) { wpa_snprintf_hex_uppercase((char *) token->dev_pw, sizeof(token->dev_pw), dev_pw, dev_pw_len); token->dev_pw_len = dev_pw_len * 2; } dl_list_add(®->nfc_pw_tokens, &token->list); reg->selected_registrar = 1; reg->pbc = 0; wps_registrar_add_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg, pw_id); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_set_selected_timeout, reg, NULL); wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar", pw_id); return 0; } int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, const u8 *oob_dev_pw, size_t oob_dev_pw_len) { const u8 *pos, *hash, *dev_pw; u16 id; size_t dev_pw_len; if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 || oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 + WPS_OOB_DEVICE_PASSWORD_LEN) return -1; hash = oob_dev_pw; pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN; id = WPA_GET_BE16(pos); dev_pw = pos + 2; dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw; wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u", id); wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", hash, WPS_OOB_PUBKEY_HASH_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len); return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw, dev_pw_len, 0); } void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, struct wps_nfc_pw_token *token) { wps_registrar_remove_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg, 0); /* * Free the NFC password token if it was used only for a single protocol * run. The static handover case uses the same password token multiple * times, so do not free that case here. */ if (token->peer_pk_hash_known) os_free(token); } #endif /* CONFIG_WPS_NFC */ diff --git a/tests/hwsim/test_multi_ap.py b/tests/hwsim/test_multi_ap.py index ca8ea3a31f90..99db14ebfae9 100644 --- a/tests/hwsim/test_multi_ap.py +++ b/tests/hwsim/test_multi_ap.py @@ -1,363 +1,368 @@ # Test cases for Multi-AP # Copyright (c) 2018, The Linux Foundation # # This software may be distributed under the terms of the BSD license. # See README for more details. import hostapd from wpasupplicant import WpaSupplicant from utils import * def test_multi_ap_association(dev, apdev): """Multi-AP association in backhaul BSS""" run_multi_ap_association(dev, apdev, 1) dev[1].connect("multi-ap", psk="12345678", scan_freq="2412", wait_connect=False) ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED", "CTRL-EVENT-CONNECTED", "CTRL-EVENT-ASSOC-REJECT"], timeout=5) dev[1].request("DISCONNECT") if ev is None: raise Exception("Connection result not reported") if "CTRL-EVENT-ASSOC-REJECT" not in ev: raise Exception("Association rejection not reported") if "status_code=12" not in ev: raise Exception("Unexpected association status code: " + ev) def test_multi_ap_association_shared_bss(dev, apdev): """Multi-AP association in backhaul BSS (with fronthaul BSS enabled)""" run_multi_ap_association(dev, apdev, 3) dev[1].connect("multi-ap", psk="12345678", scan_freq="2412") def run_multi_ap_association(dev, apdev, multi_ap, wait_connect=True): params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678") if multi_ap: params["multi_ap"] = str(multi_ap) hapd = hostapd.add_ap(apdev[0], params) dev[0].connect("multi-ap", psk="12345678", scan_freq="2412", multi_ap_backhaul_sta="1", wait_connect=wait_connect) def test_multi_ap_backhaul_roam_with_bridge(dev, apdev): """Multi-AP backhaul BSS reassociation to another BSS with bridge""" br_ifname = 'sta-br0' ifname = 'wlan5' try: run_multi_ap_backhaul_roam_with_bridge(dev, apdev) finally: subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down']) subprocess.call(['brctl', 'delif', br_ifname, ifname]) subprocess.call(['brctl', 'delbr', br_ifname]) subprocess.call(['iw', ifname, 'set', '4addr', 'off']) def run_multi_ap_backhaul_roam_with_bridge(dev, apdev): br_ifname = 'sta-br0' ifname = 'wlan5' wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') subprocess.call(['brctl', 'addbr', br_ifname]) subprocess.call(['brctl', 'setfd', br_ifname, '0']) subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up']) subprocess.call(['iw', ifname, 'set', '4addr', 'on']) subprocess.check_call(['brctl', 'addif', br_ifname, ifname]) wpas.interface_add(ifname, br_ifname=br_ifname) wpas.flush_scan_cache() params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678") params["multi_ap"] = "1" hapd = hostapd.add_ap(apdev[0], params) wpas.connect("multi-ap", psk="12345678", scan_freq="2412", multi_ap_backhaul_sta="1") hapd2 = hostapd.add_ap(apdev[1], params) bssid2 = hapd2.own_addr() wpas.scan_for_bss(bssid2, freq="2412", force_scan=True) wpas.roam(bssid2) def test_multi_ap_disabled_on_ap(dev, apdev): """Multi-AP association attempt when disabled on AP""" run_multi_ap_association(dev, apdev, 0, wait_connect=False) ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED", "CTRL-EVENT-CONNECTED"], timeout=5) dev[0].request("DISCONNECT") if ev is None: raise Exception("Connection result not reported") if "CTRL-EVENT-DISCONNECTED" not in ev: raise Exception("Unexpected connection result") def test_multi_ap_fronthaul_on_ap(dev, apdev): """Multi-AP association attempt when only fronthaul BSS on AP""" run_multi_ap_association(dev, apdev, 2, wait_connect=False) ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED", "CTRL-EVENT-CONNECTED", "CTRL-EVENT-ASSOC-REJECT"], timeout=5) dev[0].request("DISCONNECT") if ev is None: raise Exception("Connection result not reported") if "CTRL-EVENT-DISCONNECTED" not in ev: raise Exception("Unexpected connection result") +def remove_apdev(dev, ifname): + hglobal = hostapd.HostapdGlobal() + hglobal.remove(ifname) + dev.cmd_execute(['iw', ifname, 'del']) + def run_multi_ap_wps(dev, apdev, params, params_backhaul=None, add_apdev=False, run_csa=False, allow_csa_fail=False): """Helper for running Multi-AP WPS tests dev[0] does multi_ap WPS, dev[1] does normal WPS. apdev[0] is the fronthaul BSS. If there is a separate backhaul BSS, it must have been set up by the caller. params are the normal SSID parameters, they will be extended with the WPS parameters. multi_ap_bssid must be given if it is not equal to the fronthaul BSSID.""" wpas_apdev = None if params_backhaul: hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul) multi_ap_bssid = hapd_backhaul.own_addr() else: multi_ap_bssid = apdev[0]['bssid'] params.update({"wps_state": "2", "eap_server": "1"}) # WPS with multi-ap station dev[0] hapd = hostapd.add_ap(apdev[0], params) conf = hapd.request("GET_CONFIG").splitlines() if "ssid=" + params['ssid'] not in conf: raise Exception("GET_CONFIG did not show correct ssid entry") if "multi_ap" in params and \ "multi_ap=" + params["multi_ap"] not in conf: raise Exception("GET_CONFIG did not show correct multi_ap entry") if "multi_ap_backhaul_ssid" in params and \ "multi_ap_backhaul_ssid=" + params["multi_ap_backhaul_ssid"].strip('"') not in conf: raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_ssid entry") if "wpa" in params and "multi_ap_backhaul_wpa_passphrase" in params and \ "multi_ap_backhaul_wpa_passphrase=" + params["multi_ap_backhaul_wpa_passphrase"] not in conf: raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_passphrase entry") if "multi_ap_backhaul_wpa_psk" in params and \ "multi_ap_backhaul_wpa_psk=" + params["multi_ap_backhaul_wpa_psk"] not in conf: raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_psk entry") hapd.request("WPS_PBC") if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): raise Exception("PBC status not shown correctly") dev[0].request("WPS_PBC multi_ap=1") dev[0].wait_connected(timeout=20) status = dev[0].get_status() if status['wpa_state'] != 'COMPLETED' or status['bssid'] != multi_ap_bssid: raise Exception("Not fully connected") if status['ssid'] != params['multi_ap_backhaul_ssid'].strip('"'): raise Exception("Unexpected SSID %s != %s" % (status['ssid'], params["multi_ap_backhaul_ssid"])) if status['pairwise_cipher'] != 'CCMP': raise Exception("Unexpected encryption configuration %s" % status['pairwise_cipher']) if status['key_mgmt'] != 'WPA2-PSK': raise Exception("Unexpected key_mgmt") status = hapd.request("WPS_GET_STATUS") if "PBC Status: Disabled" not in status: raise Exception("PBC status not shown correctly") if "Last WPS result: Success" not in status: raise Exception("Last WPS result not shown correctly") if "Peer Address: " + dev[0].own_addr() not in status: raise Exception("Peer address not shown correctly") if len(dev[0].list_networks()) != 1: raise Exception("Unexpected number of network blocks") # WPS with non-Multi-AP station dev[1] hapd.request("WPS_PBC") if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): raise Exception("PBC status not shown correctly") dev[1].request("WPS_PBC") dev[1].wait_connected(timeout=20) status = dev[1].get_status() if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']: raise Exception("Not fully connected") if status['ssid'] != params["ssid"]: raise Exception("Unexpected SSID") # Fronthaul may be something else than WPA2-PSK so don't test it. status = hapd.request("WPS_GET_STATUS") if "PBC Status: Disabled" not in status: raise Exception("PBC status not shown correctly") if "Last WPS result: Success" not in status: raise Exception("Last WPS result not shown correctly") if "Peer Address: " + dev[1].own_addr() not in status: raise Exception("Peer address not shown correctly") if len(dev[1].list_networks()) != 1: raise Exception("Unexpected number of network blocks") try: # Add apdev to the same phy that dev[0] if add_apdev: wpas_apdev = {} wpas_apdev['ifname'] = dev[0].ifname + "_ap" status, buf = dev[0].cmd_execute(['iw', dev[0].ifname, 'interface', 'add', wpas_apdev['ifname'], 'type', 'managed']) if status != 0: raise Exception("iw interface add failed") wpas_hapd = hostapd.add_ap(wpas_apdev, params) if run_csa: if 'OK' not in hapd.request("CHAN_SWITCH 5 2462 ht"): raise Exception("chan switch request failed") ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5) if not ev: raise Exception("chan switch failed") # now check station ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH", "CTRL-EVENT-DISCONNECTED"], timeout=5) if not ev: raise Exception("sta - no chanswitch event") if "CTRL-EVENT-CHANNEL-SWITCH" not in ev and not allow_csa_fail: raise Exception("Received disconnection event instead of channel switch event") if add_apdev: - dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del']) + remove_apdev(dev[0], wpas_apdev['ifname']) except: if wpas_apdev: - dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del']) + remove_apdev(dev[0], wpas_apdev['ifname']) raise return hapd def test_multi_ap_wps_shared(dev, apdev): """WPS on shared fronthaul/backhaul AP""" ssid = "multi-ap-wps" passphrase = "12345678" params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) params.update({"multi_ap": "3", "multi_ap_backhaul_ssid": '"%s"' % ssid, "multi_ap_backhaul_wpa_passphrase": passphrase}) hapd = run_multi_ap_wps(dev, apdev, params) # Verify WPS parameter update with Multi-AP if "OK" not in hapd.request("RELOAD"): raise Exception("hostapd RELOAD failed") dev[0].wait_disconnected() dev[0].request("REMOVE_NETWORK all") hapd.request("WPS_PBC") dev[0].request("WPS_PBC multi_ap=1") dev[0].wait_connected(timeout=20) def test_multi_ap_wps_shared_csa(dev, apdev): """WPS on shared fronthaul/backhaul AP, run CSA""" ssid = "multi-ap-wps-csa" passphrase = "12345678" params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) params.update({"multi_ap": "3", "multi_ap_backhaul_ssid": '"%s"' % ssid, "multi_ap_backhaul_wpa_passphrase": passphrase}) run_multi_ap_wps(dev, apdev, params, run_csa=True) def test_multi_ap_wps_shared_apdev_csa(dev, apdev): """WPS on shared fronthaul/backhaul AP add apdev on same phy and run CSA""" ssid = "multi-ap-wps-apdev-csa" passphrase = "12345678" params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase) params.update({"multi_ap": "3", "multi_ap_backhaul_ssid": '"%s"' % ssid, "multi_ap_backhaul_wpa_passphrase": passphrase}) # This case is currently failing toc omplete CSA on the station interface. # For the time being, ignore that to avoid always failing tests. Full # validation can be enabled once the issue behind this is fixed. run_multi_ap_wps(dev, apdev, params, add_apdev=True, run_csa=True, allow_csa_fail=True) def test_multi_ap_wps_shared_psk(dev, apdev): """WPS on shared fronthaul/backhaul AP using PSK""" ssid = "multi-ap-wps" psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef" params = hostapd.wpa2_params(ssid=ssid) params.update({"wpa_psk": psk, "multi_ap": "3", "multi_ap_backhaul_ssid": '"%s"' % ssid, "multi_ap_backhaul_wpa_psk": psk}) run_multi_ap_wps(dev, apdev, params) def test_multi_ap_wps_split(dev, apdev): """WPS on split fronthaul and backhaul AP""" backhaul_ssid = "multi-ap-backhaul-wps" backhaul_passphrase = "87654321" params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps", passphrase="12345678") params.update({"multi_ap": "2", "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}) params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase) params_backhaul.update({"multi_ap": "1"}) run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_split_psk(dev, apdev): """WPS on split fronthaul and backhaul AP""" backhaul_ssid = "multi-ap-backhaul-wps" backhaul_psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef" params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps", passphrase="12345678") params.update({"multi_ap": "2", "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, "multi_ap_backhaul_wpa_psk": backhaul_psk}) params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid) params_backhaul.update({"multi_ap": "1", "wpa_psk": backhaul_psk}) run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_split_mixed(dev, apdev): """WPS on split fronthaul and backhaul AP with mixed-mode fronthaul""" skip_without_tkip(dev[0]) backhaul_ssid = "multi-ap-backhaul-wps" backhaul_passphrase = "87654321" params = hostapd.wpa_mixed_params(ssid="multi-ap-fronthaul-wps", passphrase="12345678") params.update({"multi_ap": "2", "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}) params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase) params_backhaul.update({"multi_ap": "1"}) run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_split_open(dev, apdev): """WPS on split fronthaul and backhaul AP with open fronthaul""" backhaul_ssid = "multi-ap-backhaul-wps" backhaul_passphrase = "87654321" params = {"ssid": "multi-ap-wps-fronthaul", "multi_ap": "2", "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid, "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase} params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid, passphrase=backhaul_passphrase) params_backhaul.update({"multi_ap": "1"}) run_multi_ap_wps(dev, apdev, params, params_backhaul) def test_multi_ap_wps_fail_non_multi_ap(dev, apdev): """Multi-AP WPS on non-WPS AP fails""" params = hostapd.wpa2_params(ssid="non-multi-ap-wps", passphrase="12345678") params.update({"wps_state": "2", "eap_server": "1"}) hapd = hostapd.add_ap(apdev[0], params) hapd.request("WPS_PBC") if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"): raise Exception("PBC status not shown correctly") dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412") dev[0].request("WPS_PBC %s multi_ap=1" % apdev[0]['bssid']) # Since we will fail to associate and WPS doesn't even get started, there # isn't much we can do except wait for timeout. For PBC, it is not possible # to change the timeout from 2 minutes. Instead of waiting for the timeout, # just check that WPS doesn't finish within reasonable time. for i in range(2): ev = dev[0].wait_event(["WPS-SUCCESS", "WPS-FAIL", "CTRL-EVENT-DISCONNECTED"], timeout=10) if ev and "WPS-" in ev: raise Exception("WPS operation completed: " + ev) dev[0].request("WPS_CANCEL") diff --git a/tests/hwsim/test_pasn.py b/tests/hwsim/test_pasn.py index c8bcd63f6ac7..6f7a806f5c46 100644 --- a/tests/hwsim/test_pasn.py +++ b/tests/hwsim/test_pasn.py @@ -1,850 +1,854 @@ # Test cases for PASN # Copyright (C) 2019 Intel Corporation # # This software may be distributed under the terms of the BSD license. # See README for more details. from remotehost import remote_compatible import binascii import os import time import logging logger = logging.getLogger() import socket import struct import subprocess import re import hwsim_utils import hostapd from wpasupplicant import WpaSupplicant from utils import * from hwsim import HWSimRadio from test_erp import start_erp_as from test_ap_ft import run_roams, ft_params1, ft_params2 def check_pasn_capab(dev): if "PASN" not in dev.get_capability("auth_alg"): raise HwsimSkip("PASN not supported") def pasn_ap_params(akmp="PASN", cipher="CCMP", group="19"): params = {"ssid": "test-wpa2-pasn", "wpa_passphrase": "12345678", "wpa": "2", "ieee80211w": "2", "wpa_key_mgmt": "WPA-PSK " + akmp, "rsn_pairwise": cipher, "pasn_groups" : group} return params def start_pasn_ap(apdev, params): try: return hostapd.add_ap(apdev, params) except Exception as e: if "Failed to set hostapd parameter wpa_key_mgmt" in str(e) or \ "Failed to set hostapd parameter force_kdk_derivation" in str(e): raise HwsimSkip("PASN not supported") raise def check_pasn_ptk(dev, hapd, cipher, fail_ptk=False, clear_keys=True): sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher) ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher) if not (sta_ptksa and ap_ptksa): if fail_ptk: return raise Exception("Could not get PTKSA entry") logger.info("sta: TK: %s KDK: %s" % (sta_ptksa['tk'], sta_ptksa['kdk'])) logger.info("ap : TK: %s KDK: %s" % (ap_ptksa['tk'], ap_ptksa['kdk'])) if sta_ptksa['tk'] != ap_ptksa['tk'] or sta_ptksa['kdk'] != ap_ptksa['kdk']: raise Exception("TK/KDK mismatch") elif fail_ptk: raise Exception("TK/KDK match although key derivation should have failed") elif clear_keys: cmd = "PASN_DEAUTH bssid=%s" % hapd.own_addr() dev.request(cmd) # Wait a little to let the AP process the deauth time.sleep(0.2) sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher) ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher) if sta_ptksa or ap_ptksa: raise Exception("TK/KDK not deleted as expected") def check_pasn_akmp_cipher(dev, hapd, akmp="PASN", cipher="CCMP", group="19", status=0, fail=0, nid="", fail_ptk=False): dev.flush_scan_cache() dev.scan(type="ONLY", freq=2412) cmd = "PASN_START bssid=%s akmp=%s cipher=%s group=%s" % (hapd.own_addr(), akmp, cipher, group) if nid != "": cmd += " nid=%s" % nid resp = dev.request(cmd) if fail: if "OK" in resp: raise Exception("Unexpected success to start PASN authentication") return if "OK" not in resp: raise Exception("Failed to start PASN authentication") ev = dev.wait_event(["PASN-AUTH-STATUS"], 3) if not ev: raise Exception("PASN: PASN-AUTH-STATUS not seen") if hapd.own_addr() + " akmp=" + akmp + ", status=" + str(status) not in ev: raise Exception("PASN: unexpected status") if status: return check_pasn_ptk(dev, hapd, cipher, fail_ptk) @remote_compatible def test_pasn_ccmp(dev, apdev): """PASN authentication with WPA2/CCMP AP""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP", "19") hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") @remote_compatible def test_pasn_gcmp(dev, apdev): """PASN authentication with WPA2/GCMP AP""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "GCMP", "19") hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP") @remote_compatible def test_pasn_ccmp_256(dev, apdev): """PASN authentication with WPA2/CCMP256 AP""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP-256", "19") hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP-256") @remote_compatible def test_pasn_gcmp_256(dev, apdev): """PASN authentication with WPA2/GCMP-256 AP""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "GCMP-256", "19") hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP-256") @remote_compatible def test_pasn_group_mismatch(dev, apdev): """PASN authentication with WPA2/CCMP AP with group mismatch""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP", "20") hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", status=77) @remote_compatible def test_pasn_channel_mismatch(dev, apdev): """PASN authentication with WPA2/CCMP AP with channel mismatch""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP") params['channel'] = "6" hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1) @remote_compatible def test_pasn_while_connected_same_channel(dev, apdev): """PASN authentication with WPA2/CCMP AP while connected same channel""" check_pasn_capab(dev[0]) ssid = "test-wpa2-psk" psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6' params = hostapd.wpa2_params(ssid=ssid) params['wpa_psk'] = psk hapd = start_pasn_ap(apdev[0], params) dev[0].connect(ssid, raw_psk=psk, scan_freq="2412") params = pasn_ap_params("PASN", "CCMP") hapd = start_pasn_ap(apdev[1], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") @remote_compatible def test_pasn_while_connected_same_ap(dev, apdev): """PASN authentication with WPA2/CCMP AP while connected to it""" check_pasn_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") hapd = start_pasn_ap(apdev[0], params) dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412") check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1) @remote_compatible def test_pasn_while_connected_diff_channel(dev, apdev): """PASN authentication with WPA2/CCMP AP while connected diff channel""" check_pasn_capab(dev[0]) with HWSimRadio(n_channels=2) as (radio, iface): wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') wpas.interface_add(iface) if wpas.get_mcc() < 2: raise HwsimSkip("PASN: New radio does not support MCC") params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678") params['channel'] = "6" hapd = start_pasn_ap(apdev[0], params) wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2437") params = pasn_ap_params("PASN", "CCMP") hapd2 = start_pasn_ap(apdev[1], params) check_pasn_akmp_cipher(wpas, hapd2, "PASN", "CCMP") @remote_compatible def test_pasn_sae_pmksa_cache(dev, apdev): """PASN authentication with SAE AP with PMKSA caching""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['sae_pwe'] = "2" hapd = start_pasn_ap(apdev[0], params) try: dev[0].set("sae_groups", "19") dev[0].set("sae_pwe", "2") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") hapd.wait_sta() hwsim_utils.test_connectivity(dev[0], hapd) dev[0].request("DISCONNECT") dev[0].wait_disconnected() check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP") finally: dev[0].set("sae_pwe", "0") def check_pasn_fils_pmksa_cache(dev, apdev, params, key_mgmt): check_fils_capa(dev[0]) check_erp_capa(dev[0]) check_pasn_capab(dev[0]) start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) bssid = apdev[0]['bssid'] params = hostapd.wpa2_eap_params(ssid="fils") params['wpa_key_mgmt'] = key_mgmt + " PASN" params['auth_server_port'] = "18128" params['erp_domain'] = 'example.com' params['fils_realm'] = 'example.com' hapd = start_pasn_ap(apdev[0], params) dev[0].scan_for_bss(bssid, freq=2412) dev[0].request("ERP_FLUSH") id = dev[0].connect("fils", key_mgmt=key_mgmt, eap="PSK", identity="psk.user@example.com", password_hex="0123456789abcdef0123456789abcdef", erp="1", scan_freq="2412") pmksa = dev[0].get_pmksa(bssid) if pmksa is None: raise Exception("No PMKSA cache entry created") hapd.wait_sta() hwsim_utils.test_connectivity(dev[0], hapd) dev[0].request("DISCONNECT") dev[0].wait_disconnected() check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP") @remote_compatible def test_pasn_fils_sha256_pmksa_cache(dev, apdev, params): """PASN authentication with FILS-SHA256 with PMKSA caching""" check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA256") @remote_compatible def test_pasn_fils_sha384_pmksa_cache(dev, apdev, params): """PASN authentication with FILS-SHA384 with PMKSA caching""" check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA384") @remote_compatible def test_pasn_sae_kdk(dev, apdev): """Station authentication with SAE AP with KDK derivation during connection""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) try: params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['sae_pwe'] = "2" params['force_kdk_derivation'] = "1" hapd = start_pasn_ap(apdev[0], params) dev[0].set("force_kdk_derivation", "1") dev[0].set("sae_pwe", "2") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False) finally: dev[0].set("force_kdk_derivation", "0") dev[0].set("sae_pwe", "0") def check_pasn_fils_kdk(dev, apdev, params, key_mgmt): check_fils_capa(dev[0]) check_erp_capa(dev[0]) check_pasn_capab(dev[0]) start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) try: bssid = apdev[0]['bssid'] params = hostapd.wpa2_eap_params(ssid="fils") params['wpa_key_mgmt'] = key_mgmt params['auth_server_port'] = "18128" params['erp_domain'] = 'example.com' params['fils_realm'] = 'example.com' params['disable_pmksa_caching'] = '1' params['force_kdk_derivation'] = "1" hapd = start_pasn_ap(apdev[0], params) dev[0].scan_for_bss(bssid, freq=2412) dev[0].request("ERP_FLUSH") dev[0].set("force_kdk_derivation", "1") id = dev[0].connect("fils", key_mgmt=key_mgmt, eap="PSK", identity="psk.user@example.com", password_hex="0123456789abcdef0123456789abcdef", erp="1", scan_freq="2412") hapd.wait_sta() hwsim_utils.test_connectivity(dev[0], hapd) check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False) dev[0].request("DISCONNECT") dev[0].wait_disconnected() dev[0].dump_monitor() dev[0].select_network(id, freq=2412) ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", "EVENT-ASSOC-REJECT", "CTRL-EVENT-CONNECTED"], timeout=10) if ev is None: raise Exception("Connection using FILS/ERP timed out") if "CTRL-EVENT-EAP-STARTED" in ev: raise Exception("Unexpected EAP exchange") if "EVENT-ASSOC-REJECT" in ev: raise Exception("Association failed") hapd.wait_sta() hwsim_utils.test_connectivity(dev[0], hapd) check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False) finally: dev[0].set("force_kdk_derivation", "0") @remote_compatible def test_pasn_fils_sha256_kdk(dev, apdev, params): """Station authentication with FILS-SHA256 with KDK derivation during connection""" check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA256") @remote_compatible def test_pasn_fils_sha384_kdk(dev, apdev, params): """Station authentication with FILS-SHA384 with KDK derivation during connection""" check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA384") @remote_compatible def test_pasn_sae(dev, apdev): """PASN authentication with SAE AP with PMK derivation + PMKSA caching""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-pasn-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['sae_pwe'] = "2" hapd = start_pasn_ap(apdev[0], params) try: dev[0].set("sae_pwe", "2") dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", only_add_network=True) # first test with a valid PSK check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0") # And now with PMKSA caching check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP") # And now with a wrong passphrase if "FAIL" in dev[0].request("PMKSA_FLUSH"): raise Exception("PMKSA_FLUSH failed") dev[0].set_network_quoted(0, "psk", "12345678787") check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0") finally: dev[0].set("sae_pwe", "0") @remote_compatible def test_pasn_sae_while_connected_same_channel(dev, apdev): """PASN SAE authentication while connected same channel""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk", passphrase="12345678") hapd = hostapd.add_ap(apdev[0], params) try: dev[0].set("sae_pwe", "2") dev[0].connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2412") params = hostapd.wpa2_params(ssid="test-pasn-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['sae_pwe'] = "2" hapd = start_pasn_ap(apdev[1], params) dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", only_add_network=True) check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="1") finally: dev[0].set("sae_pwe", "0") @remote_compatible def test_pasn_sae_while_connected_diff_channel(dev, apdev): """PASN SAE authentication while connected diff channel""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) with HWSimRadio(n_channels=2) as (radio, iface): wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') wpas.interface_add(iface) if wpas.get_mcc() < 2: raise HwsimSkip("PASN: New radio does not support MCC") params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk", passphrase="12345678") params['channel'] = "6" hapd = hostapd.add_ap(apdev[0], params) try: wpas.set("sae_pwe", "2") wpas.connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2437") params = hostapd.wpa2_params(ssid="test-pasn-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['sae_pwe'] = "2" hapd = start_pasn_ap(apdev[1], params) wpas.connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", only_add_network=True) check_pasn_akmp_cipher(wpas, hapd, "SAE", "CCMP", nid="1") finally: wpas.set("sae_pwe", "0") def pasn_fils_setup(wpas, apdev, params, key_mgmt): check_fils_capa(wpas) check_erp_capa(wpas) wpas.flush_scan_cache() start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) bssid = apdev[0]['bssid'] params = hostapd.wpa2_eap_params(ssid="fils") params['wpa_key_mgmt'] = key_mgmt + " PASN" params['auth_server_port'] = "18128" params['erp_domain'] = 'example.com' params['fils_realm'] = 'example.com' params['disable_pmksa_caching'] = '1' hapd = hostapd.add_ap(apdev[0]['ifname'], params) id = wpas.connect("fils", key_mgmt=key_mgmt, eap="PSK", identity="psk.user@example.com", password_hex="0123456789abcdef0123456789abcdef", erp="1", scan_freq="2412") wpas.request("DISCONNECT") wpas.wait_disconnected() wpas.dump_monitor() if "FAIL" in wpas.request("PMKSA_FLUSH"): raise Exception("PMKSA_FLUSH failed") return hapd def check_pasn_fils(dev, apdev, params, key_mgmt): check_pasn_capab(dev[0]) hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt); check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0") @remote_compatible def test_pasn_fils_sha256(dev, apdev, params): """PASN FILS authentication using SHA-256""" check_pasn_fils(dev, apdev, params, "FILS-SHA256") @remote_compatible def test_pasn_fils_sha384(dev, apdev, params): """PASN FILS authentication using SHA-384""" check_pasn_fils(dev, apdev, params, "FILS-SHA384") def check_pasn_fils_connected_same_channel(dev, apdev, params, key_mgmt): check_pasn_capab(dev[0]) hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt); # Connect to another AP on the same channel hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open"}) dev[0].connect("open", key_mgmt="NONE", scan_freq="2412", bg_scan_period="0") hwsim_utils.test_connectivity(dev[0], hapd1) # And perform the PASN authentication with FILS check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0") @remote_compatible def test_pasn_fils_sha256_connected_same_channel(dev, apdev, params): """PASN FILS authentication using SHA-256 while connected same channel""" check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA256") @remote_compatible def test_pasn_fils_sha384_connected_same_channel(dev, apdev, params): """PASN FILS authentication using SHA-384 while connected same channel""" check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA384") def check_pasn_fils_connected_diff_channel(dev, apdev, params, key_mgmt): check_pasn_capab(dev[0]) with HWSimRadio(n_channels=2) as (radio, iface): wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') wpas.interface_add(iface) if wpas.get_mcc() < 2: raise Exception("New radio does not support MCC") hapd = pasn_fils_setup(wpas, apdev, params, key_mgmt); # Connect to another AP on a different channel hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open", "channel" : "6"}) wpas.connect("open", key_mgmt="NONE", scan_freq="2437", bg_scan_period="0") hwsim_utils.test_connectivity(wpas, hapd1) # And perform the PASN authentication with FILS check_pasn_akmp_cipher(wpas, hapd, key_mgmt, "CCMP", nid="0") @remote_compatible def test_pasn_fils_sha256_connected_diff_channel(dev, apdev, params): """PASN FILS authentication using SHA-256 while connected diff channel""" check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA256") @remote_compatible def test_pasn_fils_sha384_connected_diff_channel(dev, apdev, params): """PASN FILS authentication using SHA-384 while connected diff channel""" check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA384") def test_pasn_ft_psk(dev, apdev): """PASN authentication with FT-PSK""" check_pasn_capab(dev[0]) ssid = "test-pasn-ft-psk" passphrase = "12345678" params = ft_params1(ssid=ssid, passphrase=passphrase) params['wpa_key_mgmt'] += " PASN" hapd0 = hostapd.add_ap(apdev[0], params) params = ft_params2(ssid=ssid, passphrase=passphrase) params['wpa_key_mgmt'] += " PASN" hapd1 = hostapd.add_ap(apdev[1], params) run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase) if dev[0].get_status_field('bssid') == apdev[0]['bssid']: pasn_hapd = hapd1 else: pasn_hapd = hapd0 check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP") run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, only_one_way=1) if dev[0].get_status_field('bssid') == apdev[0]['bssid']: pasn_hapd = hapd1 else: pasn_hapd = hapd0 check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP") def test_pasn_ft_eap(dev, apdev): """PASN authentication with FT-EAP""" check_pasn_capab(dev[0]) ssid = "test-pasn-ft-psk" passphrase = "12345678" identity = "gpsk user" radius = hostapd.radius_params() params = ft_params1(ssid=ssid, passphrase=passphrase) params['wpa_key_mgmt'] = "FT-EAP PASN" params["ieee8021x"] = "1" params = dict(list(radius.items()) + list(params.items())) hapd0 = hostapd.add_ap(apdev[0], params) params = ft_params2(ssid=ssid, passphrase=passphrase) params['wpa_key_mgmt'] = "FT-EAP PASN" params["ieee8021x"] = "1" params = dict(list(radius.items()) + list(params.items())) hapd1 = hostapd.add_ap(apdev[1], params) run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True, eap_identity=identity) if dev[0].get_status_field('bssid') == apdev[0]['bssid']: pasn_hapd = hapd1 else: pasn_hapd = hapd0 check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP", "CCMP") def test_pasn_ft_eap_sha384(dev, apdev): """PASN authentication with FT-EAP-SHA-384""" check_pasn_capab(dev[0]) ssid = "test-pasn-ft-psk" passphrase = "12345678" identity = "gpsk user" radius = hostapd.radius_params() params = ft_params1(ssid=ssid, passphrase=passphrase) params["ieee80211w"] = "2" params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN" params["ieee8021x"] = "1" params = dict(list(radius.items()) + list(params.items())) hapd0 = hostapd.add_ap(apdev[0], params) params = ft_params2(ssid=ssid, passphrase=passphrase) params["ieee80211w"] = "2" params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN" params["ieee8021x"] = "1" params = dict(list(radius.items()) + list(params.items())) hapd1 = hostapd.add_ap(apdev[1], params) run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True, sha384=True) if dev[0].get_status_field('bssid') == apdev[0]['bssid']: pasn_hapd = hapd1 else: pasn_hapd = hapd0 check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP-SHA384", "CCMP") def test_pasn_sta_mic_error(dev, apdev): """PASN authentication with WPA2/CCMP AP with corrupted MIC on station""" + check_pasn_capab(dev[0]) + params = pasn_ap_params("PASN", "CCMP", "19") hapd = hostapd.add_ap(apdev[0], params) try: # When forcing MIC corruption, the exchange would be still successful # on the station side, but the AP would fail the exchange and would not # store the keys. dev[0].set("pasn_corrupt_mic", "1") check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail_ptk=True) finally: dev[0].set("pasn_corrupt_mic", "0") # Now verify the successful case check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") def test_pasn_ap_mic_error(dev, apdev): """PASN authentication with WPA2/CCMP AP with corrupted MIC on AP""" + check_pasn_capab(dev[0]) + params = pasn_ap_params("PASN", "CCMP", "19") hapd0 = hostapd.add_ap(apdev[0], params) params['pasn_corrupt_mic'] = "1" hapd1 = hostapd.add_ap(apdev[1], params) check_pasn_akmp_cipher(dev[0], hapd1, "PASN", "CCMP", status=1) check_pasn_akmp_cipher(dev[0], hapd0, "PASN", "CCMP") @remote_compatible def test_pasn_comeback(dev, apdev, params): """PASN authentication with comeback flow""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP", "19") params['sae_anti_clogging_threshold'] = '0' hapd = hostapd.add_ap(apdev[0], params) bssid = hapd.own_addr() dev[0].scan(type="ONLY", freq=2412) cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19" % bssid resp = dev[0].request(cmd) if "OK" not in resp: raise Exception("Failed to start PASN authentication") ev = dev[0].wait_event(["PASN-AUTH-STATUS"], 3) if not ev: raise Exception("PASN: PASN-AUTH-STATUS not seen") if bssid + " akmp=PASN, status=30 comeback_after=" not in ev: raise Exception("PASN: unexpected status") comeback = re.split("comeback=", ev)[1] cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19 comeback=%s" % \ (bssid, comeback) resp = dev[0].request(cmd) if "OK" not in resp: raise Exception("Failed to start PASN authentication") ev = dev[0].wait_event(["PASN-AUTH-STATUS"], 3) if not ev: raise Exception("PASN: PASN-AUTH-STATUS not seen") if bssid + " akmp=PASN, status=0" not in ev: raise Exception("PASN: unexpected status with comeback token") check_pasn_ptk(dev[0], hapd, "CCMP") @remote_compatible def test_pasn_comeback_after_0(dev, apdev, params): """PASN authentication with comeback flow with comeback after set to 0""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP", "19") params['anti_clogging_threshold'] = '0' params['pasn_comeback_after'] = '0' hapd = start_pasn_ap(apdev[0], params) check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") @remote_compatible def test_pasn_comeback_after_0_sae(dev, apdev): """PASN authentication with SAE, with comeback flow where comeback after is set to 0""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-pasn-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['anti_clogging_threshold'] = '0' params['pasn_comeback_after'] = '0' params['sae_pwe'] = "2" hapd = start_pasn_ap(apdev[0], params) try: dev[0].set("sae_pwe", "2") dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", only_add_network=True) # first test with a valid PSK check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0") # And now with PMKSA caching check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP") # And now with a wrong passphrase if "FAIL" in dev[0].request("PMKSA_FLUSH"): raise Exception("PMKSA_FLUSH failed") dev[0].set_network_quoted(0, "psk", "12345678787") check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0") finally: dev[0].set("sae_pwe", "0") @remote_compatible def test_pasn_comeback_multi(dev, apdev): """PASN authentication with SAE, with multiple stations with comeback""" check_pasn_capab(dev[0]) check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-pasn-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE PASN' params['anti_clogging_threshold'] = '1' params['pasn_comeback_after'] = '0' hapd = start_pasn_ap(apdev[0], params) bssid = hapd.own_addr() id = {} for i in range(0, 2): dev[i].flush_scan_cache() dev[i].scan(type="ONLY", freq=2412) id[i] = dev[i].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", only_add_network=True) for i in range(0, 2): cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19, nid=%s" % (bssid, id[i]) resp = dev[i].request(cmd) if "OK" not in resp: raise Exception("Failed to start pasn authentication") for i in range(0, 2): ev = dev[i].wait_event(["PASN-AUTH-STATUS"], 3) if not ev: raise Exception("PASN: PASN-AUTH-STATUS not seen") if bssid + " akmp=PASN, status=0" not in ev: raise Exception("PASN: unexpected status") check_pasn_ptk(dev[i], hapd, "CCMP") def test_pasn_kdk_derivation(dev, apdev): """PASN authentication with forced KDK derivation""" check_pasn_capab(dev[0]) params = pasn_ap_params("PASN", "CCMP", "19") hapd0 = start_pasn_ap(apdev[0], params) params['force_kdk_derivation'] = "1" hapd1 = start_pasn_ap(apdev[1], params) try: check_pasn_akmp_cipher(dev[0], hapd0, "PASN", "CCMP") dev[0].set("force_kdk_derivation", "1") check_pasn_akmp_cipher(dev[0], hapd1, "PASN", "CCMP") finally: dev[0].set("force_kdk_derivation", "0") diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py index 159678e0c7be..9925d98f3a7c 100644 --- a/tests/hwsim/test_sae.py +++ b/tests/hwsim/test_sae.py @@ -1,2777 +1,2793 @@ # Test cases for SAE # Copyright (c) 2013-2020, Jouni Malinen # # This software may be distributed under the terms of the BSD license. # See README for more details. from remotehost import remote_compatible import binascii import os import time import logging logger = logging.getLogger() import socket import struct import subprocess import hwsim_utils import hostapd from wpasupplicant import WpaSupplicant from utils import * from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations @remote_compatible def test_sae(dev, apdev): """SAE with default group""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) key_mgmt = hapd.get_config()['key_mgmt'] if key_mgmt.split(' ')[0] != "SAE": raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt) dev[0].request("SET sae_groups ") id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") hapd.wait_sta() if dev[0].get_status_field('sae_group') != '19': raise Exception("Expected default SAE group not used") bss = dev[0].get_bss(apdev[0]['bssid']) if 'flags' not in bss: raise Exception("Could not get BSS flags from BSS table") if "[WPA2-SAE-CCMP]" not in bss['flags']: raise Exception("Unexpected BSS flags: " + bss['flags']) res = hapd.request("STA-FIRST") if "sae_group=19" not in res.splitlines(): raise Exception("hostapd STA output did not specify SAE group") pmk_h = hapd.request("GET_PMK " + dev[0].own_addr()) pmk_w = dev[0].get_pmk(id) if pmk_h != pmk_w: raise Exception("Fetched PMK does not match: hostapd %s, wpa_supplicant %s" % (pmk_h, pmk_w)) dev[0].request("DISCONNECT") dev[0].wait_disconnected() pmk_h2 = hapd.request("GET_PMK " + dev[0].own_addr()) if pmk_h != pmk_h2: raise Exception("Fetched PMK from PMKSA cache does not match: %s, %s" % (pmk_h, pmk_h2)) if "FAIL" not in hapd.request("GET_PMK foo"): raise Exception("Invalid GET_PMK did not return failure") if "FAIL" not in hapd.request("GET_PMK 02:ff:ff:ff:ff:ff"): raise Exception("GET_PMK for unknown STA did not return failure") @remote_compatible def test_sae_password_ecc(dev, apdev): """SAE with number of different passwords (ECC)""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups 19") for i in range(10): password = "12345678-" + str(i) hapd.set("wpa_passphrase", password) dev[0].connect("test-sae", psk=password, key_mgmt="SAE", scan_freq="2412") dev[0].request("REMOVE_NETWORK all") dev[0].wait_disconnected() @remote_compatible def test_sae_password_ffc(dev, apdev): """SAE with number of different passwords (FFC)""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params['sae_groups'] = '15' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups 15") for i in range(10): password = "12345678-" + str(i) hapd.set("wpa_passphrase", password) dev[0].connect("test-sae", psk=password, key_mgmt="SAE", scan_freq="2412") dev[0].request("REMOVE_NETWORK all") dev[0].wait_disconnected() @remote_compatible def test_sae_pmksa_caching(dev, apdev): """SAE and PMKSA caching""" run_sae_pmksa_caching(dev, apdev) @remote_compatible def test_sae_pmksa_caching_pmkid(dev, apdev): """SAE and PMKSA caching (PMKID in AssocReq after SAE)""" try: dev[0].set("sae_pmkid_in_assoc", "1") run_sae_pmksa_caching(dev, apdev) finally: dev[0].set("sae_pmkid_in_assoc", "0") def run_sae_pmksa_caching(dev, apdev): check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5) if ev is None: raise Exception("No connection event received from hostapd") sta0 = hapd.get_sta(dev[0].own_addr()) if sta0['wpa'] != '2' or sta0['AKMSuiteSelector'] != '00-0f-ac-8': raise Exception("SAE STA(0) AKM suite selector reported incorrectly") dev[0].request("DISCONNECT") dev[0].wait_disconnected() dev[0].request("RECONNECT") dev[0].wait_connected(timeout=15, error="Reconnect timed out") if dev[0].get_status_field('sae_group') is not None: raise Exception("SAE group claimed to have been used") sta0 = hapd.get_sta(dev[0].own_addr()) if sta0['wpa'] != '2' or sta0['AKMSuiteSelector'] != '00-0f-ac-8': raise Exception("SAE STA(0) AKM suite selector reported incorrectly after PMKSA caching") @remote_compatible def test_sae_pmksa_caching_disabled(dev, apdev): """SAE and PMKSA caching disabled""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params['disable_pmksa_caching'] = '1' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5) if ev is None: raise Exception("No connection event received from hostapd") dev[0].request("DISCONNECT") dev[0].wait_disconnected() dev[0].request("RECONNECT") dev[0].wait_connected(timeout=15, error="Reconnect timed out") if dev[0].get_status_field('sae_group') != '19': raise Exception("Expected default SAE group not used") def test_sae_groups(dev, apdev): """SAE with all supported groups""" check_sae_capab(dev[0]) # This is the full list of supported groups, but groups 14-16 (2048-4096 bit # MODP) and group 21 (521-bit random ECP group) are a bit too slow on some # VMs and can result in hitting the mac80211 authentication timeout, so # allow them to fail and just report such failures in the debug log. sae_groups = [19, 25, 26, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24] tls = dev[0].request("GET tls_library") if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls: logger.info("Add Brainpool EC groups since OpenSSL is new enough") sae_groups += [27, 28, 29, 30] heavy_groups = [14, 15, 16] suitable_groups = [15, 16, 17, 18, 19, 20, 21] groups = [str(g) for g in sae_groups] params = hostapd.wpa2_params(ssid="test-sae-groups", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params['sae_groups'] = ' '.join(groups) hapd = hostapd.add_ap(apdev[0], params) for g in groups: logger.info("Testing SAE group " + g) dev[0].request("SET sae_groups " + g) id = dev[0].connect("test-sae-groups", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) if int(g) in heavy_groups: ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5) if ev is None: logger.info("No connection with heavy SAE group %s did not connect - likely hitting timeout in mac80211" % g) dev[0].remove_network(id) time.sleep(0.1) dev[0].dump_monitor() continue logger.info("Connection with heavy SAE group " + g) else: ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) if ev is None: if "BoringSSL" in tls and int(g) in [25]: logger.info("Ignore connection failure with group " + g + " with BoringSSL") dev[0].remove_network(id) dev[0].dump_monitor() continue if int(g) not in suitable_groups: logger.info("Ignore connection failure with unsuitable group " + g) dev[0].remove_network(id) dev[0].dump_monitor() continue raise Exception("Connection timed out with group " + g) if dev[0].get_status_field('sae_group') != g: raise Exception("Expected SAE group not used") pmksa = dev[0].get_pmksa(hapd.own_addr()) if not pmksa: raise Exception("No PMKSA cache entry added") if pmksa['pmkid'] == '00000000000000000000000000000000': raise Exception("All zeros PMKID derived for group %s" % g) dev[0].remove_network(id) dev[0].wait_disconnected() dev[0].dump_monitor() @remote_compatible def test_sae_group_nego(dev, apdev): """SAE group negotiation""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae-group-nego", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params['sae_groups'] = '19' hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups 25 26 20 19") dev[0].connect("test-sae-group-nego", psk="12345678", key_mgmt="SAE", scan_freq="2412") if dev[0].get_status_field('sae_group') != '19': raise Exception("Expected SAE group not used") def test_sae_group_nego_no_match(dev, apdev): """SAE group negotiation (no match)""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae-group-nego", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' # None-existing SAE group to force all attempts to be rejected params['sae_groups'] = '0' hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-sae-group-nego", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10) dev[0].request("REMOVE_NETWORK all") if ev is None: raise Exception("Network profile disabling not reported") @remote_compatible def test_sae_anti_clogging(dev, apdev): """SAE anti clogging""" check_sae_capab(dev[0]) check_sae_capab(dev[1]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params['sae_anti_clogging_threshold'] = '1' hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[1].request("SET sae_groups ") id = {} for i in range(0, 2): dev[i].scan(freq="2412") id[i] = dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", only_add_network=True) for i in range(0, 2): dev[i].select_network(id[i]) for i in range(0, 2): dev[i].wait_connected(timeout=10) def test_sae_forced_anti_clogging(dev, apdev): """SAE anti clogging (forced)""" check_sae_capab(dev[0]) check_sae_capab(dev[1]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE WPA-PSK' params['sae_anti_clogging_threshold'] = '0' hostapd.add_ap(apdev[0], params) dev[2].connect("test-sae", psk="12345678", scan_freq="2412") for i in range(0, 2): dev[i].request("SET sae_groups ") dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") def test_sae_mixed(dev, apdev): """Mixed SAE and non-SAE network""" check_sae_capab(dev[0]) check_sae_capab(dev[1]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE WPA-PSK' params['sae_anti_clogging_threshold'] = '0' hapd = hostapd.add_ap(apdev[0], params) dev[2].connect("test-sae", psk="12345678", scan_freq="2412") for i in range(0, 2): dev[i].request("SET sae_groups ") dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") sta0 = hapd.get_sta(dev[0].own_addr()) sta2 = hapd.get_sta(dev[2].own_addr()) if sta0['wpa'] != '2' or sta0['AKMSuiteSelector'] != '00-0f-ac-8': raise Exception("SAE STA(0) AKM suite selector reported incorrectly") if sta2['wpa'] != '2' or sta2['AKMSuiteSelector'] != '00-0f-ac-2': raise Exception("PSK STA(2) AKM suite selector reported incorrectly") def test_sae_and_psk(dev, apdev): """SAE and PSK enabled in network profile""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE WPA-PSK", scan_freq="2412") def test_sae_and_psk2(dev, apdev): """SAE and PSK enabled in network profile (use PSK)""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-psk", passphrase="12345678") hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-psk", psk="12345678", key_mgmt="SAE WPA-PSK", scan_freq="2412") def test_sae_wpa3_roam(dev, apdev): """SAE and WPA3-Personal transition mode roaming""" check_sae_capab(dev[0]) # WPA3-Personal only AP params = hostapd.wpa2_params(ssid="test", passphrase="12345678") params['ieee80211w'] = '2' params['wpa_key_mgmt'] = 'SAE' hapd0 = hostapd.add_ap(apdev[0], params) # WPA2-Personal only AP params = hostapd.wpa2_params(ssid="test", passphrase="12345678") hapd1 = hostapd.add_ap(apdev[1], params) dev[0].set("sae_groups", "") dev[0].connect("test", psk="12345678", key_mgmt="SAE WPA-PSK", ieee80211w="1", scan_freq="2412") bssid = dev[0].get_status_field('bssid') # Disable the current AP to force roam to the other one if bssid == apdev[0]['bssid']: hapd0.disable() else: hapd1.disable() dev[0].wait_connected() # Disable the current AP to force roam to the other (previous) one if bssid == apdev[0]['bssid']: hapd0.enable() hapd1.disable() else: hapd1.enable() hapd0.disable() dev[0].wait_connected() # Force roam to an AP in WPA3-Personal transition mode if bssid == apdev[0]['bssid']: hapd1.set("ieee80211w", "1") hapd1.set("sae_require_mfp", "1") hapd1.set("wpa_key_mgmt", "SAE WPA-PSK") hapd1.enable() hapd0.disable() else: hapd0.set("ieee80211w", "1") hapd0.set("sae_require_mfp", "1") hapd0.set("wpa_key_mgmt", "SAE WPA-PSK") hapd0.enable() hapd1.disable() dev[0].wait_connected() status = dev[0].get_status() if status['key_mgmt'] != "SAE": raise Exception("Did not use SAE with WPA3-Personal transition mode AP") if status['pmf'] != "1": raise Exception("Did not use PMF with WPA3-Personal transition mode AP") def test_sae_mixed_mfp(dev, apdev): """Mixed SAE and non-SAE network and MFP required with SAE""" check_sae_capab(dev[0]) check_sae_capab(dev[1]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE WPA-PSK' params["ieee80211w"] = "1" params['sae_require_mfp'] = '1' hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="2", scan_freq="2412") dev[0].dump_monitor() dev[1].request("SET sae_groups ") dev[1].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="0", scan_freq="2412", wait_connect=False) ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-ASSOC-REJECT"], timeout=10) if ev is None: raise Exception("No connection result reported") if "CTRL-EVENT-ASSOC-REJECT" not in ev: raise Exception("SAE connection without MFP was not rejected") if "status_code=31" not in ev: raise Exception("Unexpected status code in rejection: " + ev) dev[1].request("DISCONNECT") dev[1].dump_monitor() dev[2].connect("test-sae", psk="12345678", ieee80211w="0", scan_freq="2412") dev[2].dump_monitor() def test_sae_and_psk_transition_disable(dev, apdev): """SAE and PSK transition disable indication""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params["ieee80211w"] = "1" params['wpa_key_mgmt'] = 'SAE WPA-PSK' params['transition_disable'] = '0x01' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE WPA-PSK", ieee80211w="1", scan_freq="2412") ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1) if ev is None: raise Exception("Transition disable not indicated") if ev.split(' ')[1] != "01": raise Exception("Unexpected transition disable bitmap: " + ev) val = dev[0].get_network(id, "ieee80211w") if val != "2": raise Exception("Unexpected ieee80211w value: " + val) val = dev[0].get_network(id, "key_mgmt") if val != "SAE": raise Exception("Unexpected key_mgmt value: " + val) val = dev[0].get_network(id, "group") if val != "CCMP": raise Exception("Unexpected group value: " + val) val = dev[0].get_network(id, "proto") if val != "RSN": raise Exception("Unexpected proto value: " + val) dev[0].request("DISCONNECT") dev[0].wait_disconnected() dev[0].request("RECONNECT") dev[0].wait_connected() def test_sae_mfp(dev, apdev): """SAE and MFP enabled without sae_require_mfp""" check_sae_capab(dev[0]) check_sae_capab(dev[1]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params["ieee80211w"] = "1" hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="2", scan_freq="2412") dev[1].request("SET sae_groups ") dev[1].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="0", scan_freq="2412") @remote_compatible def test_sae_missing_password(dev, apdev): """SAE and missing password""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups ") id = dev[0].connect("test-sae", raw_psk="46b4a73b8a951ad53ebd2e0afdb9c5483257edd4c21d12b7710759da70945858", key_mgmt="SAE", scan_freq="2412", wait_connect=False) ev = dev[0].wait_event(['CTRL-EVENT-SSID-TEMP-DISABLED'], timeout=10) if ev is None: raise Exception("Invalid network not temporarily disabled") def test_sae_key_lifetime_in_memory(dev, apdev, params): """SAE and key lifetime in memory""" check_sae_capab(dev[0]) password = "5ad144a7c1f5a5503baa6fa01dabc15b1843e8c01662d78d16b70b5cd23cf8b" p = hostapd.wpa2_params(ssid="test-sae", passphrase=password) p['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], p) pid = find_wpas_process(dev[0]) dev[0].request("SET sae_groups ") id = dev[0].connect("test-sae", psk=password, key_mgmt="SAE", scan_freq="2412") # The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED # event has been delivered, so verify that wpa_supplicant has returned to # eloop before reading process memory. time.sleep(1) dev[0].ping() password = password.encode() buf = read_process_memory(pid, password) dev[0].request("DISCONNECT") dev[0].wait_disconnected() dev[0].relog() sae_k = None sae_keyseed = None sae_kck = None pmk = None ptk = None gtk = None with open(os.path.join(params['logdir'], 'log0'), 'r') as f: for l in f.readlines(): if "SAE: k - hexdump" in l: val = l.strip().split(':')[3].replace(' ', '') sae_k = binascii.unhexlify(val) if "SAE: keyseed - hexdump" in l: val = l.strip().split(':')[3].replace(' ', '') sae_keyseed = binascii.unhexlify(val) if "SAE: KCK - hexdump" in l: val = l.strip().split(':')[3].replace(' ', '') sae_kck = binascii.unhexlify(val) if "SAE: PMK - hexdump" in l: val = l.strip().split(':')[3].replace(' ', '') pmk = binascii.unhexlify(val) if "WPA: PTK - hexdump" in l: val = l.strip().split(':')[3].replace(' ', '') ptk = binascii.unhexlify(val) if "WPA: Group Key - hexdump" in l: val = l.strip().split(':')[3].replace(' ', '') gtk = binascii.unhexlify(val) if not sae_k or not sae_keyseed or not sae_kck or not pmk or not ptk or not gtk: raise Exception("Could not find keys from debug log") if len(gtk) != 16: raise Exception("Unexpected GTK length") kck = ptk[0:16] kek = ptk[16:32] tk = ptk[32:48] fname = os.path.join(params['logdir'], 'sae_key_lifetime_in_memory.memctx-') logger.info("Checking keys in memory while associated") get_key_locations(buf, password, "Password") get_key_locations(buf, pmk, "PMK") if password not in buf: raise HwsimSkip("Password not found while associated") if pmk not in buf: raise HwsimSkip("PMK not found while associated") if kck not in buf: raise Exception("KCK not found while associated") if kek not in buf: raise Exception("KEK not found while associated") #if tk in buf: # raise Exception("TK found from memory") verify_not_present(buf, sae_k, fname, "SAE(k)") verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)") verify_not_present(buf, sae_kck, fname, "SAE(KCK)") logger.info("Checking keys in memory after disassociation") buf = read_process_memory(pid, password) # Note: Password is still present in network configuration # Note: PMK is in PMKSA cache get_key_locations(buf, password, "Password") get_key_locations(buf, pmk, "PMK") verify_not_present(buf, kck, fname, "KCK") verify_not_present(buf, kek, fname, "KEK") verify_not_present(buf, tk, fname, "TK") if gtk in buf: get_key_locations(buf, gtk, "GTK") verify_not_present(buf, gtk, fname, "GTK") verify_not_present(buf, sae_k, fname, "SAE(k)") verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)") verify_not_present(buf, sae_kck, fname, "SAE(KCK)") dev[0].request("PMKSA_FLUSH") logger.info("Checking keys in memory after PMKSA cache flush") buf = read_process_memory(pid, password) get_key_locations(buf, password, "Password") get_key_locations(buf, pmk, "PMK") verify_not_present(buf, pmk, fname, "PMK") dev[0].request("REMOVE_NETWORK all") logger.info("Checking keys in memory after network profile removal") buf = read_process_memory(pid, password) get_key_locations(buf, password, "Password") get_key_locations(buf, pmk, "PMK") verify_not_present(buf, password, fname, "password") verify_not_present(buf, pmk, fname, "PMK") verify_not_present(buf, kck, fname, "KCK") verify_not_present(buf, kek, fname, "KEK") verify_not_present(buf, tk, fname, "TK") verify_not_present(buf, gtk, fname, "GTK") verify_not_present(buf, sae_k, fname, "SAE(k)") verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)") verify_not_present(buf, sae_kck, fname, "SAE(KCK)") @remote_compatible def test_sae_oom_wpas(dev, apdev): """SAE and OOM in wpa_supplicant""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' params['sae_groups'] = '19 25 26 20' hapd = hostapd.add_ap(apdev[0], params) dev[0].request("SET sae_groups 20") with alloc_fail(dev[0], 1, "sae_set_group"): dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") dev[0].request("REMOVE_NETWORK all") dev[0].request("SET sae_groups ") with alloc_fail(dev[0], 2, "sae_set_group"): dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") dev[0].request("REMOVE_NETWORK all") with alloc_fail(dev[0], 1, "wpabuf_alloc;sme_auth_build_sae_commit"): dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") dev[0].request("REMOVE_NETWORK all") with alloc_fail(dev[0], 1, "wpabuf_alloc;sme_auth_build_sae_confirm"): dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) wait_fail_trigger(dev[0], "GET_ALLOC_FAIL") dev[0].request("REMOVE_NETWORK all") with alloc_fail(dev[0], 1, "=sme_authenticate"): dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) wait_fail_trigger(dev[0], "GET_ALLOC_FAIL") dev[0].request("REMOVE_NETWORK all") with alloc_fail(dev[0], 1, "radio_add_work;sme_authenticate"): dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) wait_fail_trigger(dev[0], "GET_ALLOC_FAIL") dev[0].request("REMOVE_NETWORK all") @remote_compatible def test_sae_proto_ecc(dev, apdev): """SAE protocol testing (ECC)""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) bssid = apdev[0]['bssid'] dev[0].request("SET sae_groups 19") tests = [("Confirm mismatch", "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", "0000800edebc3f260dc1fe7e0b20888af2b8a3316252ec37388a8504e25b73dc4240"), ("Commit without even full cyclic group field", "13", None), ("Too short commit", "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02", None), ("Invalid commit scalar (0)", "1300" + "0000000000000000000000000000000000000000000000000000000000000000" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", None), ("Invalid commit scalar (1)", "1300" + "0000000000000000000000000000000000000000000000000000000000000001" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", None), ("Invalid commit scalar (> r)", "1300" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", None), ("Commit element not on curve", "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728d0000000000000000000000000000000000000000000000000000000000000000", None), ("Invalid commit element (y coordinate > P)", "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", None), ("Invalid commit element (x coordinate > P)", "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", None), ("Different group in commit", "1400" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", None), ("Too short confirm", "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8", "0000800edebc3f260dc1fe7e0b20888af2b8a3316252ec37388a8504e25b73dc42")] for (note, commit, confirm) in tests: logger.info(note) dev[0].scan_for_bss(bssid, freq=2412) hapd.set("ext_mgmt_frame_handling", "1") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) logger.info("Commit") for i in range(0, 10): req = hapd.mgmt_rx() if req is None: raise Exception("MGMT RX wait timed out (commit)") if req['subtype'] == 11: break req = None if not req: raise Exception("Authentication frame (commit) not received") hapd.dump_monitor() resp = {} resp['fc'] = req['fc'] resp['da'] = req['sa'] resp['sa'] = req['da'] resp['bssid'] = req['bssid'] resp['payload'] = binascii.unhexlify("030001000000" + commit) hapd.mgmt_tx(resp) if confirm: logger.info("Confirm") for i in range(0, 10): req = hapd.mgmt_rx() if req is None: raise Exception("MGMT RX wait timed out (confirm)") if req['subtype'] == 11: break req = None if not req: raise Exception("Authentication frame (confirm) not received") hapd.dump_monitor() resp = {} resp['fc'] = req['fc'] resp['da'] = req['sa'] resp['sa'] = req['da'] resp['bssid'] = req['bssid'] resp['payload'] = binascii.unhexlify("030002000000" + confirm) hapd.mgmt_tx(resp) time.sleep(0.1) dev[0].request("REMOVE_NETWORK all") hapd.set("ext_mgmt_frame_handling", "0") hapd.dump_monitor() @remote_compatible def test_sae_proto_ffc(dev, apdev): """SAE protocol testing (FFC)""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) bssid = apdev[0]['bssid'] dev[0].request("SET sae_groups 2") tests = [("Confirm mismatch", "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "a8c00117493cdffa5dd671e934bc9cb1a69f39e25e9dd9cd9afd3aea2441a0f5491211c7ba50a753563f9ce943b043557cb71193b28e86ed9544f4289c471bf91b70af5c018cf4663e004165b0fd0bc1d8f3f78adf42eee92bcbc55246fd3ee9f107ab965dc7d4986f23eb71d616ebfe6bfe0a6c1ac5dc1718acee17c9a17486", "0000f3116a9731f1259622e3eb55d4b3b50ba16f8c5f5565b28e609b180c51460251"), ("Too short commit", "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "a8c00117493cdffa5dd671e934bc9cb1a69f39e25e9dd9cd9afd3aea2441a0f5491211c7ba50a753563f9ce943b043557cb71193b28e86ed9544f4289c471bf91b70af5c018cf4663e004165b0fd0bc1d8f3f78adf42eee92bcbc55246fd3ee9f107ab965dc7d4986f23eb71d616ebfe6bfe0a6c1ac5dc1718acee17c9a174", None), ("Invalid element (0) in commit", "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", None), ("Invalid element (1) in commit", "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", None), ("Invalid element (> P) in commit", "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", None)] for (note, commit, confirm) in tests: logger.info(note) dev[0].scan_for_bss(bssid, freq=2412) hapd.set("ext_mgmt_frame_handling", "1") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) logger.info("Commit") for i in range(0, 10): req = hapd.mgmt_rx() if req is None: raise Exception("MGMT RX wait timed out (commit)") if req['subtype'] == 11: break req = None if not req: raise Exception("Authentication frame (commit) not received") hapd.dump_monitor() resp = {} resp['fc'] = req['fc'] resp['da'] = req['sa'] resp['sa'] = req['da'] resp['bssid'] = req['bssid'] resp['payload'] = binascii.unhexlify("030001000000" + commit) hapd.mgmt_tx(resp) if confirm: logger.info("Confirm") for i in range(0, 10): req = hapd.mgmt_rx() if req is None: raise Exception("MGMT RX wait timed out (confirm)") if req['subtype'] == 11: break req = None if not req: raise Exception("Authentication frame (confirm) not received") hapd.dump_monitor() resp = {} resp['fc'] = req['fc'] resp['da'] = req['sa'] resp['sa'] = req['da'] resp['bssid'] = req['bssid'] resp['payload'] = binascii.unhexlify("030002000000" + confirm) hapd.mgmt_tx(resp) time.sleep(0.1) dev[0].request("REMOVE_NETWORK all") hapd.set("ext_mgmt_frame_handling", "0") hapd.dump_monitor() def test_sae_proto_commit_delayed(dev, apdev): """SAE protocol testing - Commit delayed""" check_sae_capab(dev[0]) params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678") params['wpa_key_mgmt'] = 'SAE' hapd = hostapd.add_ap(apdev[0], params) bssid = apdev[0]['bssid'] dev[0].request("SET sae_groups 19") dev[0].scan_for_bss(bssid, freq=2412) hapd.set("ext_mgmt_frame_handling", "1") dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412", wait_connect=False) logger.info("Commit") for i in range(0, 10): req = hapd.mgmt_rx() if req is None: raise Exception("MGMT RX wait timed out (commit)") if req['subtype'] == 11: break req = None if not req: raise Exception("Authentication frame (commit) not received") hapd.dump_monitor() time.sleep(2.5) hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode()) logger.info("Commit/Confirm") for i in range(0, 10): req = hapd.mgmt_rx() if req is None: raise Exception("MGMT RX wait timed out (confirm)") if req['subtype'] == 11: trans, = struct.unpack(' * Copyright (c) 2009, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "eapol_supp/eapol_supp_sm.h" #include "crypto/dh_group5.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" #ifdef NEED_AP_MLME #include "ap/ieee802_11.h" #endif /* NEED_AP_MLME */ #include "ap/beacon.h" #include "ap/ieee802_1x.h" #include "ap/wps_hostapd.h" #include "ap/ctrl_iface_ap.h" #include "ap/dfs.h" #include "wps/wps.h" #include "common/ieee802_11_defs.h" #include "config_ssid.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "p2p_supplicant.h" #include "ap.h" #include "ap/sta_info.h" #include "notify.h" #ifdef CONFIG_WPS static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P static bool is_chanwidth160_supported(struct hostapd_hw_modes *mode, struct hostapd_config *conf) { #ifdef CONFIG_IEEE80211AX if (conf->ieee80211ax) { struct he_capabilities *he_cap; he_cap = &mode->he_capab[IEEE80211_MODE_AP]; if (he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & (HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G | HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)) return true; } #endif /* CONFIG_IEEE80211AX */ if (mode->vht_capab & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) return true; return false; } #endif /* CONFIG_P2P */ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_config *conf, struct hostapd_hw_modes *mode) { #ifdef CONFIG_P2P u8 center_chan = 0; u8 channel = conf->channel; #endif /* CONFIG_P2P */ u8 freq_seg_idx; if (!conf->secondary_channel) goto no_vht; /* Use the maximum oper channel width if it's given. */ if (ssid->max_oper_chwidth) hostapd_set_oper_chwidth(conf, ssid->max_oper_chwidth); if (hostapd_get_oper_chwidth(conf) == CHANWIDTH_80P80MHZ) { ieee80211_freq_to_chan(ssid->vht_center_freq2, &freq_seg_idx); hostapd_set_oper_centr_freq_seg1_idx(conf, freq_seg_idx); } if (!ssid->p2p_group) { if (!ssid->vht_center_freq1) goto no_vht; ieee80211_freq_to_chan(ssid->vht_center_freq1, &freq_seg_idx); hostapd_set_oper_centr_freq_seg0_idx(conf, freq_seg_idx); wpa_printf(MSG_DEBUG, "VHT seg0 index %d and seg1 index %d for AP", hostapd_get_oper_centr_freq_seg0_idx(conf), hostapd_get_oper_centr_freq_seg1_idx(conf)); return; } #ifdef CONFIG_P2P switch (hostapd_get_oper_chwidth(conf)) { case CHANWIDTH_80MHZ: case CHANWIDTH_80P80MHZ: - center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel, + conf->op_class); wpa_printf(MSG_DEBUG, "VHT center channel %u for 80 or 80+80 MHz bandwidth", center_chan); break; case CHANWIDTH_160MHZ: - center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel, + conf->op_class); wpa_printf(MSG_DEBUG, "VHT center channel %u for 160 MHz bandwidth", center_chan); break; default: /* * conf->vht_oper_chwidth might not be set for non-P2P GO cases, * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is * not supported. */ hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ); - center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + ieee80211_freq_to_channel_ext(ssid->frequency, 0, + conf->vht_oper_chwidth, + &conf->op_class, + &conf->channel); + center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel, + conf->op_class); if (center_chan && is_chanwidth160_supported(mode, conf)) { wpa_printf(MSG_DEBUG, "VHT center channel %u for auto-selected 160 MHz bandwidth", center_chan); } else { hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ); + ieee80211_freq_to_channel_ext(ssid->frequency, 0, + conf->vht_oper_chwidth, + &conf->op_class, + &conf->channel); center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, - channel); + channel, + conf->op_class); wpa_printf(MSG_DEBUG, "VHT center channel %u for auto-selected 80 MHz bandwidth", center_chan); } break; } if (!center_chan) goto no_vht; hostapd_set_oper_centr_freq_seg0_idx(conf, center_chan); wpa_printf(MSG_DEBUG, "VHT seg0 index %d for P2P GO", hostapd_get_oper_centr_freq_seg0_idx(conf)); return; #endif /* CONFIG_P2P */ no_vht: wpa_printf(MSG_DEBUG, "No VHT higher bandwidth support for the selected channel %d", conf->channel); hostapd_set_oper_centr_freq_seg0_idx( conf, conf->channel + conf->secondary_channel * 2); hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT); } static struct hostapd_hw_modes * wpa_supplicant_find_hw_mode(struct wpa_supplicant *wpa_s, enum hostapd_hw_mode hw_mode) { struct hostapd_hw_modes *mode = NULL; int i; for (i = 0; i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].mode == hw_mode) { mode = &wpa_s->hw.modes[i]; break; } } return mode; } int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_config *conf) { - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - + conf->hw_mode = ieee80211_freq_to_channel_ext(ssid->frequency, 0, + ssid->max_oper_chwidth, + &conf->op_class, + &conf->channel); + /* ssid->max_oper_chwidth is not valid in all cases, so fall back to the + * less specific mechanism, if needed, at least for now */ + if (conf->hw_mode == NUM_HOSTAPD_MODES) + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); if (conf->hw_mode == NUM_HOSTAPD_MODES) { wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", ssid->frequency); return -1; } /* * Enable HT20 if the driver supports it, by setting conf->ieee80211n * and a mask of allowed capabilities within conf->ht_capab. * Using default config settings for: conf->ht_op_mode_fixed, * conf->secondary_channel, conf->require_ht */ if (wpa_s->hw.modes) { struct hostapd_hw_modes *mode = NULL; int no_ht = 0; wpa_printf(MSG_DEBUG, "Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)", ssid->frequency, conf->channel); mode = wpa_supplicant_find_hw_mode(wpa_s, conf->hw_mode); /* May drop to IEEE 802.11b if the driver does not support IEEE * 802.11g */ if (!mode && conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { conf->hw_mode = HOSTAPD_MODE_IEEE80211B; wpa_printf(MSG_INFO, "Try downgrade to IEEE 802.11b as 802.11g is not supported by the current hardware"); mode = wpa_supplicant_find_hw_mode(wpa_s, conf->hw_mode); } if (!mode) { wpa_printf(MSG_ERROR, "No match between requested and supported hw modes found"); return -1; } #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht) ssid->ht = 0; #endif /* CONFIG_HT_OVERRIDES */ if (!ssid->ht) { wpa_printf(MSG_DEBUG, "HT not enabled in network profile"); conf->ieee80211n = 0; conf->ht_capab = 0; no_ht = 1; } if (!no_ht && mode && mode->ht_capab) { wpa_printf(MSG_DEBUG, "Enable HT support (p2p_group=%d 11a=%d ht40_hw_capab=%d ssid->ht40=%d)", ssid->p2p_group, conf->hw_mode == HOSTAPD_MODE_IEEE80211A, !!(mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET), ssid->ht40); conf->ieee80211n = 1; if (ssid->ht40 && (mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) conf->secondary_channel = ssid->ht40; else conf->secondary_channel = 0; #ifdef CONFIG_P2P if (ssid->p2p_group && conf->hw_mode == HOSTAPD_MODE_IEEE80211A && (mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && ssid->ht40) { conf->secondary_channel = wpas_p2p_get_ht40_mode(wpa_s, mode, conf->channel); wpa_printf(MSG_DEBUG, "HT secondary channel offset %d for P2P group", conf->secondary_channel); } else if (ssid->p2p_group && conf->secondary_channel && conf->hw_mode != HOSTAPD_MODE_IEEE80211A) { /* This ended up trying to configure invalid * 2.4 GHz channels (e.g., HT40+ on channel 11) * in some cases, so clear the secondary channel * configuration now to avoid such cases that * would lead to group formation failures. */ wpa_printf(MSG_DEBUG, "Disable HT secondary channel for P2P group on 2.4 GHz"); conf->secondary_channel = 0; } #endif /* CONFIG_P2P */ if (!ssid->p2p_group && (mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { conf->secondary_channel = ssid->ht40; wpa_printf(MSG_DEBUG, "HT secondary channel offset %d for AP", conf->secondary_channel); } if (conf->secondary_channel) conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; /* * white-list capabilities that won't cause issues * to connecting stations, while leaving the current * capabilities intact (currently disabled SMPS). */ conf->ht_capab |= mode->ht_capab & (HT_CAP_INFO_GREEN_FIELD | HT_CAP_INFO_SHORT_GI20MHZ | HT_CAP_INFO_SHORT_GI40MHZ | HT_CAP_INFO_RX_STBC_MASK | HT_CAP_INFO_TX_STBC | HT_CAP_INFO_MAX_AMSDU_SIZE); /* check this before VHT, because setting oper chan * width and friends is the same call for HE and VHT * and checks if conf->ieee8021ax == 1 */ if (mode->he_capab[wpas_mode_to_ieee80211_mode( ssid->mode)].he_supported && ssid->he) conf->ieee80211ax = 1; if (mode->vht_capab && ssid->vht) { conf->ieee80211ac = 1; conf->vht_capab |= mode->vht_capab; wpas_conf_ap_vht(wpa_s, ssid, conf, mode); } } } if (conf->secondary_channel) { struct wpa_supplicant *iface; for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { if (iface == wpa_s || iface->wpa_state < WPA_AUTHENTICATING || (int) iface->assoc_freq != ssid->frequency) continue; /* * Do not allow 40 MHz co-ex PRI/SEC switch to force us * to change our PRI channel since we have an existing, * concurrent connection on that channel and doing * multi-channel concurrency is likely to cause more * harm than using different PRI/SEC selection in * environment with multiple BSSes on these two channels * with mixed 20 MHz or PRI channel selection. */ conf->no_pri_sec_switch = 1; } } return 0; } static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_config *conf) { struct hostapd_bss_config *bss = conf->bss[0]; conf->driver = wpa_s->driver; os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf)) return -1; if (ssid->pbss > 1) { wpa_printf(MSG_ERROR, "Invalid pbss value(%d) for AP mode", ssid->pbss); return -1; } bss->pbss = ssid->pbss; #ifdef CONFIG_ACS if (ssid->acs) { /* Setting channel to 0 in order to enable ACS */ conf->channel = 0; wpa_printf(MSG_DEBUG, "Use automatic channel selection"); } #endif /* CONFIG_ACS */ if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes, wpa_s->hw.num_modes) && wpa_s->conf->country[0]) { conf->ieee80211h = 1; conf->ieee80211d = 1; conf->country[0] = wpa_s->conf->country[0]; conf->country[1] = wpa_s->conf->country[1]; conf->country[2] = ' '; } #ifdef CONFIG_P2P if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G && (ssid->mode == WPAS_MODE_P2P_GO || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) { /* Remove 802.11b rates from supported and basic rate sets */ int *list = os_malloc(4 * sizeof(int)); if (list) { list[0] = 60; list[1] = 120; list[2] = 240; list[3] = -1; } conf->basic_rates = list; list = os_malloc(9 * sizeof(int)); if (list) { list[0] = 60; list[1] = 90; list[2] = 120; list[3] = 180; list[4] = 240; list[5] = 360; list[6] = 480; list[7] = 540; list[8] = -1; } conf->supported_rates = list; } #ifdef CONFIG_IEEE80211AX if (ssid->mode == WPAS_MODE_P2P_GO || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) conf->ieee80211ax = ssid->he; #endif /* CONFIG_IEEE80211AX */ bss->isolate = !wpa_s->conf->p2p_intra_bss; bss->extended_key_id = wpa_s->conf->extended_key_id; bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; bss->wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey; if (ssid->p2p_group) { os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4); os_memcpy(bss->ip_addr_mask, wpa_s->p2pdev->conf->ip_addr_mask, 4); os_memcpy(bss->ip_addr_start, wpa_s->p2pdev->conf->ip_addr_start, 4); os_memcpy(bss->ip_addr_end, wpa_s->p2pdev->conf->ip_addr_end, 4); } #endif /* CONFIG_P2P */ if (ssid->ssid_len == 0) { wpa_printf(MSG_ERROR, "No SSID configured for AP mode"); return -1; } os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len); bss->ssid.ssid_len = ssid->ssid_len; bss->ssid.ssid_set = 1; bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid; if (ssid->auth_alg) bss->auth_algs = ssid->auth_alg; if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) bss->wpa = ssid->proto; if (ssid->key_mgmt == DEFAULT_KEY_MGMT) bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; else bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) && ssid->passphrase) { bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); } else if (ssid->psk_set) { bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk)); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk == NULL) return -1; os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); bss->ssid.wpa_psk->group = 1; bss->ssid.wpa_psk_set = 1; } else if (ssid->passphrase) { bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); #ifdef CONFIG_WEP } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] || ssid->wep_key_len[2] || ssid->wep_key_len[3]) { struct hostapd_wep_keys *wep = &bss->ssid.wep; int i; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i] == 0) continue; wep->key[i] = os_memdup(ssid->wep_key[i], ssid->wep_key_len[i]); if (wep->key[i] == NULL) return -1; wep->len[i] = ssid->wep_key_len[i]; } wep->idx = ssid->wep_tx_keyidx; wep->keys_set = 1; #endif /* CONFIG_WEP */ } #ifdef CONFIG_SAE if (ssid->sae_password) { struct sae_password_entry *pw; pw = os_zalloc(sizeof(*pw)); if (!pw) return -1; os_memset(pw->peer_addr, 0xff, ETH_ALEN); pw->password = os_strdup(ssid->sae_password); if (!pw->password) { os_free(pw); return -1; } if (ssid->sae_password_id) { pw->identifier = os_strdup(ssid->sae_password_id); if (!pw->identifier) { str_clear_free(pw->password); os_free(pw); return -1; } } pw->next = bss->sae_passwords; bss->sae_passwords = pw; } bss->sae_pwe = wpa_s->conf->sae_pwe; #endif /* CONFIG_SAE */ if (wpa_s->conf->go_interworking) { wpa_printf(MSG_DEBUG, "P2P: Enable Interworking with access_network_type: %d", wpa_s->conf->go_access_network_type); bss->interworking = wpa_s->conf->go_interworking; bss->access_network_type = wpa_s->conf->go_access_network_type; bss->internet = wpa_s->conf->go_internet; if (wpa_s->conf->go_venue_group) { wpa_printf(MSG_DEBUG, "P2P: Venue group: %d Venue type: %d", wpa_s->conf->go_venue_group, wpa_s->conf->go_venue_type); bss->venue_group = wpa_s->conf->go_venue_group; bss->venue_type = wpa_s->conf->go_venue_type; bss->venue_info_set = 1; } } if (ssid->ap_max_inactivity) bss->ap_max_inactivity = ssid->ap_max_inactivity; if (ssid->dtim_period) bss->dtim_period = ssid->dtim_period; else if (wpa_s->conf->dtim_period) bss->dtim_period = wpa_s->conf->dtim_period; if (ssid->beacon_int) conf->beacon_int = ssid->beacon_int; else if (wpa_s->conf->beacon_int) conf->beacon_int = wpa_s->conf->beacon_int; #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) { wpa_printf(MSG_INFO, "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it", wpa_s->conf->p2p_go_ctwindow, conf->beacon_int); conf->p2p_go_ctwindow = 0; } else { conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow; } } #endif /* CONFIG_P2P */ if ((bss->wpa & 2) && bss->rsn_pairwise == 0) bss->rsn_pairwise = bss->wpa_pairwise; bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, bss->rsn_pairwise); if (bss->wpa && bss->ieee802_1x) { bss->ssid.security_policy = SECURITY_WPA; } else if (bss->wpa) { bss->ssid.security_policy = SECURITY_WPA_PSK; #ifdef CONFIG_WEP } else if (bss->ieee802_1x) { int cipher = WPA_CIPHER_NONE; bss->ssid.security_policy = SECURITY_IEEE_802_1X; bss->ssid.wep.default_len = bss->default_wep_key_len; if (bss->default_wep_key_len) cipher = bss->default_wep_key_len >= 13 ? WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; bss->wpa_group = cipher; bss->wpa_pairwise = cipher; bss->rsn_pairwise = cipher; } else if (bss->ssid.wep.keys_set) { int cipher = WPA_CIPHER_WEP40; if (bss->ssid.wep.len[0] >= 13) cipher = WPA_CIPHER_WEP104; bss->ssid.security_policy = SECURITY_STATIC_WEP; bss->wpa_group = cipher; bss->wpa_pairwise = cipher; bss->rsn_pairwise = cipher; #endif /* CONFIG_WEP */ } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; bss->wpa_group = WPA_CIPHER_NONE; bss->wpa_pairwise = WPA_CIPHER_NONE; bss->rsn_pairwise = WPA_CIPHER_NONE; } if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) && (bss->wpa_group == WPA_CIPHER_CCMP || bss->wpa_group == WPA_CIPHER_GCMP || bss->wpa_group == WPA_CIPHER_CCMP_256 || bss->wpa_group == WPA_CIPHER_GCMP_256)) { /* * Strong ciphers do not need frequent rekeying, so increase * the default GTK rekeying period to 24 hours. */ bss->wpa_group_rekey = 86400; } if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT) bss->ieee80211w = ssid->ieee80211w; #ifdef CONFIG_OCV bss->ocv = ssid->ocv; #endif /* CONFIG_OCV */ #ifdef CONFIG_WPS /* * Enable WPS by default for open and WPA/WPA2-Personal network, but * require user interaction to actually use it. Only the internal * Registrar is supported. */ if (bss->ssid.security_policy != SECURITY_WPA_PSK && bss->ssid.security_policy != SECURITY_PLAINTEXT) goto no_wps; if (bss->ssid.security_policy == SECURITY_WPA_PSK && (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) || !(bss->wpa & 2))) goto no_wps; /* WPS2 does not allow WPA/TKIP-only * configuration */ if (ssid->wps_disabled) goto no_wps; bss->eap_server = 1; if (!ssid->ignore_broadcast_ssid) bss->wps_state = 2; bss->ap_setup_locked = 2; if (wpa_s->conf->config_methods) bss->config_methods = os_strdup(wpa_s->conf->config_methods); os_memcpy(bss->device_type, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); if (wpa_s->conf->device_name) { bss->device_name = os_strdup(wpa_s->conf->device_name); bss->friendly_name = os_strdup(wpa_s->conf->device_name); } if (wpa_s->conf->manufacturer) bss->manufacturer = os_strdup(wpa_s->conf->manufacturer); if (wpa_s->conf->model_name) bss->model_name = os_strdup(wpa_s->conf->model_name); if (wpa_s->conf->model_number) bss->model_number = os_strdup(wpa_s->conf->model_number); if (wpa_s->conf->serial_number) bss->serial_number = os_strdup(wpa_s->conf->serial_number); if (is_nil_uuid(wpa_s->conf->uuid)) os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN); else os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); os_memcpy(bss->os_version, wpa_s->conf->os_version, 4); bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1; if (ssid->eap.fragment_size != DEFAULT_FRAGMENT_SIZE) bss->fragment_size = ssid->eap.fragment_size; no_wps: #endif /* CONFIG_WPS */ if (wpa_s->max_stations && wpa_s->max_stations < wpa_s->conf->max_num_sta) bss->max_num_sta = wpa_s->max_stations; else bss->max_num_sta = wpa_s->conf->max_num_sta; if (!bss->isolate) bss->isolate = wpa_s->conf->ap_isolate; bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; if (wpa_s->conf->ap_vendor_elements) { bss->vendor_elements = wpabuf_dup(wpa_s->conf->ap_vendor_elements); } bss->ftm_responder = wpa_s->conf->ftm_responder; bss->ftm_initiator = wpa_s->conf->ftm_initiator; bss->transition_disable = ssid->transition_disable; return 0; } static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq) { #ifdef CONFIG_P2P struct wpa_supplicant *wpa_s = ctx; const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; if (len < IEEE80211_HDRLEN + 1) return; if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) return; wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, mgmt->u.action.category, buf + IEEE80211_HDRLEN + 1, len - IEEE80211_HDRLEN - 1, freq); #endif /* CONFIG_P2P */ } static void ap_wps_event_cb(void *ctx, enum wps_event event, union wps_event_data *data) { #ifdef CONFIG_P2P struct wpa_supplicant *wpa_s = ctx; if (event == WPS_EV_FAIL) { struct wps_event_fail *fail = &data->fail; if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s && wpa_s == wpa_s->global->p2p_group_formation) { /* * src/ap/wps_hostapd.c has already sent this on the * main interface, so only send on the parent interface * here if needed. */ wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } wpas_p2p_wps_failed(wpa_s, fail); } #endif /* CONFIG_P2P */ } static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr, int authorized, const u8 *p2p_dev_addr) { wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr); } #ifdef CONFIG_P2P static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL) return; wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len); } #endif /* CONFIG_P2P */ static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq) { #ifdef CONFIG_P2P struct wpa_supplicant *wpa_s = ctx; const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; if (len < IEEE80211_HDRLEN + 1) return -1; wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, mgmt->u.action.category, buf + IEEE80211_HDRLEN + 1, len - IEEE80211_HDRLEN - 1, freq); #endif /* CONFIG_P2P */ return 0; } static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal) { struct wpa_supplicant *wpa_s = ctx; unsigned int freq = 0; if (wpa_s->ap_iface) freq = wpa_s->ap_iface->freq; return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len, freq, ssi_signal); } static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr, const u8 *uuid_e) { struct wpa_supplicant *wpa_s = ctx; wpas_p2p_wps_success(wpa_s, mac_addr, 1); } static void wpas_ap_configured_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; wpa_printf(MSG_DEBUG, "AP interface setup completed - state %s", hostapd_state_text(wpa_s->ap_iface->state)); if (wpa_s->ap_iface->state == HAPD_IFACE_DISABLED) { wpa_supplicant_ap_deinit(wpa_s); return; } #ifdef CONFIG_ACS if (wpa_s->current_ssid && wpa_s->current_ssid->acs) { wpa_s->assoc_freq = wpa_s->ap_iface->freq; wpa_s->current_ssid->frequency = wpa_s->ap_iface->freq; } #endif /* CONFIG_ACS */ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); if (wpa_s->ap_configured_cb) wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx, wpa_s->ap_configured_cb_data); } int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct wpa_driver_associate_params params; struct hostapd_iface *hapd_iface; struct hostapd_config *conf; size_t i; if (ssid->ssid == NULL || ssid->ssid_len == 0) { wpa_printf(MSG_ERROR, "No SSID configured for AP mode"); return -1; } wpa_supplicant_ap_deinit(wpa_s); wpa_printf(MSG_DEBUG, "Setting up AP (SSID='%s')", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); os_memset(¶ms, 0, sizeof(params)); params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; switch (ssid->mode) { case WPAS_MODE_AP: case WPAS_MODE_P2P_GO: case WPAS_MODE_P2P_GROUP_FORMATION: params.mode = IEEE80211_MODE_AP; break; default: return -1; } if (ssid->frequency == 0) ssid->frequency = 2462; /* default channel 11 */ params.freq.freq = ssid->frequency; if ((ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO) && ssid->enable_edmg) { u8 primary_channel; if (ieee80211_freq_to_chan(ssid->frequency, &primary_channel) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_WARNING, "EDMG: Failed to get the primary channel"); return -1; } hostapd_encode_edmg_chan(ssid->enable_edmg, ssid->edmg_channel, primary_channel, ¶ms.freq.edmg); } params.wpa_proto = ssid->proto; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; + else if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) + wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; else wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; params.key_mgmt_suite = wpa_s->key_mgmt; wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 1); if (wpa_s->pairwise_cipher < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " "cipher."); return -1; } params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = params.pairwise_suite; #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) params.p2p = 1; #endif /* CONFIG_P2P */ if (wpa_s->p2pdev->set_ap_uapsd) params.uapsd = wpa_s->p2pdev->ap_uapsd; else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) params.uapsd = 1; /* mandatory for P2P GO */ else params.uapsd = -1; if (ieee80211_is_dfs(params.freq.freq, wpa_s->hw.modes, wpa_s->hw.num_modes)) params.freq.freq = 0; /* set channel after CAC */ if (params.p2p) wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO); else wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS); if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; } wpa_s->ap_iface = hapd_iface = hostapd_alloc_iface(); if (hapd_iface == NULL) return -1; hapd_iface->owner = wpa_s; hapd_iface->drv_flags = wpa_s->drv_flags; hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads; hapd_iface->extended_capa = wpa_s->extended_capa; hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask; hapd_iface->extended_capa_len = wpa_s->extended_capa_len; wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); if (conf == NULL) { wpa_supplicant_ap_deinit(wpa_s); return -1; } os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, wpa_s->conf->wmm_ac_params, sizeof(wpa_s->conf->wmm_ac_params)); os_memcpy(wpa_s->ap_iface->conf->tx_queue, wpa_s->conf->tx_queue, sizeof(wpa_s->conf->tx_queue)); if (params.uapsd > 0) { conf->bss[0]->wmm_enabled = 1; conf->bss[0]->wmm_uapsd = 1; } if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) { wpa_printf(MSG_ERROR, "Failed to create AP configuration"); wpa_supplicant_ap_deinit(wpa_s); return -1; } #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO) conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER; else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER | P2P_GROUP_FORMATION; #endif /* CONFIG_P2P */ hapd_iface->num_bss = conf->num_bss; hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) { wpa_supplicant_ap_deinit(wpa_s); return -1; } for (i = 0; i < conf->num_bss; i++) { hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); if (hapd_iface->bss[i] == NULL) { wpa_supplicant_ap_deinit(wpa_s); return -1; } hapd_iface->bss[i]->msg_ctx = wpa_s; hapd_iface->bss[i]->msg_ctx_parent = wpa_s->p2pdev; hapd_iface->bss[i]->public_action_cb = ap_public_action_rx; hapd_iface->bss[i]->public_action_cb_ctx = wpa_s; hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx; hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s; hostapd_register_probereq_cb(hapd_iface->bss[i], ap_probe_req_rx, wpa_s); hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb; hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s; hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb; hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s; hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb; hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s; #ifdef CONFIG_P2P hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb; hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s; hapd_iface->bss[i]->p2p = wpa_s->global->p2p; hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s, ssid); #endif /* CONFIG_P2P */ hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb; hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s; #ifdef CONFIG_TESTING_OPTIONS hapd_iface->bss[i]->ext_eapol_frame_io = wpa_s->ext_eapol_frame_io; #endif /* CONFIG_TESTING_OPTIONS */ } os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN); hapd_iface->bss[0]->driver = wpa_s->driver; hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv; wpa_s->current_ssid = ssid; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); wpa_s->assoc_freq = ssid->frequency; wpa_s->ap_iface->conf->enable_edmg = ssid->enable_edmg; wpa_s->ap_iface->conf->edmg_channel = ssid->edmg_channel; #if defined(CONFIG_P2P) && defined(CONFIG_ACS) if (wpa_s->p2p_go_do_acs) { wpa_s->ap_iface->conf->channel = 0; wpa_s->ap_iface->conf->hw_mode = wpa_s->p2p_go_acs_band; ssid->acs = 1; } #endif /* CONFIG_P2P && CONFIG_ACS */ if (hostapd_setup_interface(wpa_s->ap_iface)) { wpa_printf(MSG_ERROR, "Failed to initialize AP interface"); wpa_supplicant_ap_deinit(wpa_s); return -1; } return 0; } void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); #endif /* CONFIG_WPS */ if (wpa_s->ap_iface == NULL) return; wpa_s->current_ssid = NULL; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->assoc_freq = 0; wpas_p2p_ap_deinit(wpa_s); wpa_s->ap_iface->driver_ap_teardown = !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); hostapd_interface_deinit(wpa_s->ap_iface); hostapd_interface_free(wpa_s->ap_iface); wpa_s->ap_iface = NULL; wpa_drv_deinit_ap(wpa_s); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d locally_generated=1", MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING); } void ap_tx_status(void *ctx, const u8 *addr, const u8 *buf, size_t len, int ack) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; hostapd_tx_status(wpa_s->ap_iface->bss[0], addr, buf, len, ack); #endif /* NEED_AP_MLME */ } void ap_eapol_tx_status(void *ctx, const u8 *dst, const u8 *data, size_t len, int ack) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; if (!wpa_s->ap_iface) return; hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack); #endif /* NEED_AP_MLME */ } void ap_client_poll_ok(void *ctx, const u8 *addr) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; if (wpa_s->ap_iface) hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr); #endif /* NEED_AP_MLME */ } void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds); #endif /* NEED_AP_MLME */ } void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; struct hostapd_frame_info fi; os_memset(&fi, 0, sizeof(fi)); fi.datarate = rx_mgmt->datarate; fi.ssi_signal = rx_mgmt->ssi_signal; ieee802_11_mgmt(wpa_s->ap_iface->bss[0], rx_mgmt->frame, rx_mgmt->frame_len, &fi); #endif /* NEED_AP_MLME */ } void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; ieee802_11_mgmt_cb(wpa_s->ap_iface->bss[0], buf, len, stype, ok); #endif /* NEED_AP_MLME */ } void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s, const u8 *src_addr, const u8 *buf, size_t len) { ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len); } #ifdef CONFIG_WPS int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *p2p_dev_addr) { if (!wpa_s->ap_iface) return -1; return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0], p2p_dev_addr); } int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s) { struct wps_registrar *reg; int reg_sel = 0, wps_sta = 0; if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps) return -1; reg = wpa_s->ap_iface->bss[0]->wps->registrar; reg_sel = wps_registrar_wps_cancel(reg); wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0], ap_sta_wps_cancel, NULL); if (!reg_sel && !wps_sta) { wpa_printf(MSG_DEBUG, "No WPS operation in progress at this " "time"); return -1; } /* * There are 2 cases to return wps cancel as success: * 1. When wps cancel was initiated but no connection has been * established with client yet. * 2. Client is in the middle of exchanging WPS messages. */ return 0; } int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, char *buf, size_t buflen, int timeout) { int ret, ret_len = 0; if (!wpa_s->ap_iface) return -1; if (pin == NULL) { unsigned int rpin; if (wps_generate_pin(&rpin) < 0) return -1; ret_len = os_snprintf(buf, buflen, "%08d", rpin); if (os_snprintf_error(buflen, ret_len)) return -1; pin = buf; } else if (buf) { ret_len = os_snprintf(buf, buflen, "%s", pin); if (os_snprintf_error(buflen, ret_len)) return -1; } ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, timeout); if (ret) return -1; return ret_len; } static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) { struct wpa_supplicant *wpa_s = eloop_data; wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); wpas_wps_ap_pin_disable(wpa_s); } static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return; hapd = wpa_s->ap_iface->bss[0]; wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); hapd->ap_pin_failures = 0; eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); if (timeout > 0) eloop_register_timeout(timeout, 0, wpas_wps_ap_pin_timeout, wpa_s, NULL); } void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return; wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); hapd = wpa_s->ap_iface->bss[0]; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = NULL; eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); } const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) { struct hostapd_data *hapd; unsigned int pin; char pin_txt[9]; if (wpa_s->ap_iface == NULL) return NULL; hapd = wpa_s->ap_iface->bss[0]; if (wps_generate_pin(&pin) < 0) return NULL; os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin); os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(pin_txt); if (hapd->conf->ap_pin == NULL) return NULL; wpas_wps_ap_pin_enable(wpa_s, timeout); return hapd->conf->ap_pin; } const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return NULL; hapd = wpa_s->ap_iface->bss[0]; return hapd->conf->ap_pin; } int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, int timeout) { struct hostapd_data *hapd; char pin_txt[9]; int ret; if (wpa_s->ap_iface == NULL) return -1; hapd = wpa_s->ap_iface->bss[0]; ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin); if (os_snprintf_error(sizeof(pin_txt), ret)) return -1; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(pin_txt); if (hapd->conf->ap_pin == NULL) return -1; wpas_wps_ap_pin_enable(wpa_s, timeout); return 0; } void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return; hapd = wpa_s->ap_iface->bss[0]; /* * Registrar failed to prove its knowledge of the AP PIN. Disable AP * PIN if this happens multiple times to slow down brute force attacks. */ hapd->ap_pin_failures++; wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", hapd->ap_pin_failures); if (hapd->ap_pin_failures < 3) return; wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN"); hapd->ap_pin_failures = 0; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = NULL; } #ifdef CONFIG_WPS_NFC struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return NULL; hapd = wpa_s->ap_iface->bss[0]; return hostapd_wps_nfc_config_token(hapd, ndef); } struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return NULL; hapd = wpa_s->ap_iface->bss[0]; return hostapd_wps_nfc_hs_cr(hapd, ndef); } int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, const struct wpabuf *req, const struct wpabuf *sel) { struct hostapd_data *hapd; if (wpa_s->ap_iface == NULL) return -1; hapd = wpa_s->ap_iface->bss[0]; return hostapd_wps_nfc_report_handover(hapd, req, sel); } #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ #ifdef CONFIG_CTRL_IFACE int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostapd_data *hapd; if (wpa_s->ap_iface) hapd = wpa_s->ap_iface->bss[0]; else if (wpa_s->ifmsh) hapd = wpa_s->ifmsh->bss[0]; else return -1; return hostapd_ctrl_iface_sta_first(hapd, buf, buflen); } int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen) { struct hostapd_data *hapd; if (wpa_s->ap_iface) hapd = wpa_s->ap_iface->bss[0]; else if (wpa_s->ifmsh) hapd = wpa_s->ifmsh->bss[0]; else return -1; return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen); } int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen) { struct hostapd_data *hapd; if (wpa_s->ap_iface) hapd = wpa_s->ap_iface->bss[0]; else if (wpa_s->ifmsh) hapd = wpa_s->ifmsh->bss[0]; else return -1; return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen); } int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s, const char *txtaddr) { if (wpa_s->ap_iface == NULL) return -1; return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0], txtaddr); } int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s, const char *txtaddr) { if (wpa_s->ap_iface == NULL) return -1; return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0], txtaddr); } int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen, int verbose) { char *pos = buf, *end = buf + buflen; int ret; struct hostapd_bss_config *conf; if (wpa_s->ap_iface == NULL) return -1; conf = wpa_s->ap_iface->bss[0]->conf; if (conf->wpa == 0) return 0; ret = os_snprintf(pos, end - pos, "pairwise_cipher=%s\n" "group_cipher=%s\n" "key_mgmt=%s\n", wpa_cipher_txt(conf->rsn_pairwise), wpa_cipher_txt(conf->wpa_group), wpa_key_mgmt_txt(conf->wpa_key_mgmt, conf->wpa)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; return pos - buf; } #endif /* CONFIG_CTRL_IFACE */ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s) { struct hostapd_iface *iface = wpa_s->ap_iface; struct wpa_ssid *ssid = wpa_s->current_ssid; struct hostapd_data *hapd; if (ssid == NULL || wpa_s->ap_iface == NULL || ssid->mode == WPAS_MODE_INFRA || ssid->mode == WPAS_MODE_IBSS) return -1; #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO) iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER; else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER | P2P_GROUP_FORMATION; #endif /* CONFIG_P2P */ hapd = iface->bss[0]; if (hapd->drv_priv == NULL) return -1; ieee802_11_set_beacons(iface); hostapd_set_ap_wps_ie(hapd); return 0; } int ap_switch_channel(struct wpa_supplicant *wpa_s, struct csa_settings *settings) { #ifdef NEED_AP_MLME struct hostapd_iface *iface = NULL; if (wpa_s->ap_iface) iface = wpa_s->ap_iface; else if (wpa_s->ifmsh) iface = wpa_s->ifmsh; if (!iface || !iface->bss[0]) return -1; return hostapd_switch_channel(iface->bss[0], settings); #else /* NEED_AP_MLME */ return -1; #endif /* NEED_AP_MLME */ } #ifdef CONFIG_CTRL_IFACE int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) { struct csa_settings settings; int ret = hostapd_parse_csa_settings(pos, &settings); if (ret) return ret; return ap_switch_channel(wpa_s, &settings); } #endif /* CONFIG_CTRL_IFACE */ void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, int offset, int width, int cf1, int cf2, int finished) { struct hostapd_iface *iface = wpa_s->ap_iface; if (!iface) iface = wpa_s->ifmsh; if (!iface) return; wpa_s->assoc_freq = freq; if (wpa_s->current_ssid) wpa_s->current_ssid->frequency = freq; hostapd_event_ch_switch(iface->bss[0], freq, ht, offset, width, cf1, cf2, finished); } int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr) { struct hostapd_data *hapd; struct hostapd_bss_config *conf; if (!wpa_s->ap_iface) return -1; if (addr) wpa_printf(MSG_DEBUG, "AP: Set MAC address filter: " MACSTR, MAC2STR(addr)); else wpa_printf(MSG_DEBUG, "AP: Clear MAC address filter"); hapd = wpa_s->ap_iface->bss[0]; conf = hapd->conf; os_free(conf->accept_mac); conf->accept_mac = NULL; conf->num_accept_mac = 0; os_free(conf->deny_mac); conf->deny_mac = NULL; conf->num_deny_mac = 0; if (addr == NULL) { conf->macaddr_acl = ACCEPT_UNLESS_DENIED; return 0; } conf->macaddr_acl = DENY_UNLESS_ACCEPTED; conf->accept_mac = os_zalloc(sizeof(struct mac_acl_entry)); if (conf->accept_mac == NULL) return -1; os_memcpy(conf->accept_mac[0].addr, addr, ETH_ALEN); conf->num_accept_mac = 1; return 0; } #ifdef CONFIG_WPS_NFC int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, const struct wpabuf *pw, const u8 *pubkey_hash) { struct hostapd_data *hapd; struct wps_context *wps; if (!wpa_s->ap_iface) return -1; hapd = wpa_s->ap_iface->bss[0]; wps = hapd->wps; if (wpa_s->p2pdev->conf->wps_nfc_dh_pubkey == NULL || wpa_s->p2pdev->conf->wps_nfc_dh_privkey == NULL) { wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known"); return -1; } dh5_free(wps->dh_ctx); wpabuf_free(wps->dh_pubkey); wpabuf_free(wps->dh_privkey); wps->dh_privkey = wpabuf_dup( wpa_s->p2pdev->conf->wps_nfc_dh_privkey); wps->dh_pubkey = wpabuf_dup( wpa_s->p2pdev->conf->wps_nfc_dh_pubkey); if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { wps->dh_ctx = NULL; wpabuf_free(wps->dh_pubkey); wps->dh_pubkey = NULL; wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; return -1; } wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); if (wps->dh_ctx == NULL) return -1; return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash, pw_id, pw ? wpabuf_head(pw) : NULL, pw ? wpabuf_len(pw) : 0, 1); } #endif /* CONFIG_WPS_NFC */ #ifdef CONFIG_CTRL_IFACE int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) { struct hostapd_data *hapd; if (!wpa_s->ap_iface) return -1; hapd = wpa_s->ap_iface->bss[0]; return hostapd_ctrl_iface_stop_ap(hapd); } int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) { size_t reply_len = 0, i; char ap_delimiter[] = "---- AP ----\n"; char mesh_delimiter[] = "---- mesh ----\n"; size_t dlen; if (wpa_s->ap_iface) { dlen = os_strlen(ap_delimiter); if (dlen > len - reply_len) return reply_len; os_memcpy(&buf[reply_len], ap_delimiter, dlen); reply_len += dlen; for (i = 0; i < wpa_s->ap_iface->num_bss; i++) { reply_len += hostapd_ctrl_iface_pmksa_list( wpa_s->ap_iface->bss[i], &buf[reply_len], len - reply_len); } } if (wpa_s->ifmsh) { dlen = os_strlen(mesh_delimiter); if (dlen > len - reply_len) return reply_len; os_memcpy(&buf[reply_len], mesh_delimiter, dlen); reply_len += dlen; reply_len += hostapd_ctrl_iface_pmksa_list( wpa_s->ifmsh->bss[0], &buf[reply_len], len - reply_len); } return reply_len; } void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s) { size_t i; if (wpa_s->ap_iface) { for (i = 0; i < wpa_s->ap_iface->num_bss; i++) hostapd_ctrl_iface_pmksa_flush(wpa_s->ap_iface->bss[i]); } if (wpa_s->ifmsh) hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]); } #ifdef CONFIG_PMKSA_CACHE_EXTERNAL #ifdef CONFIG_MESH int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr, char *buf, size_t len) { return hostapd_ctrl_iface_pmksa_list_mesh(wpa_s->ifmsh->bss[0], addr, &buf[0], len); } int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd) { struct external_pmksa_cache *entry; void *pmksa_cache; pmksa_cache = hostapd_ctrl_iface_pmksa_create_entry(wpa_s->own_addr, cmd); if (!pmksa_cache) return -1; entry = os_zalloc(sizeof(struct external_pmksa_cache)); if (!entry) return -1; entry->pmksa_cache = pmksa_cache; dl_list_add(&wpa_s->mesh_external_pmksa_cache, &entry->list); return 0; } #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ #endif /* CONFIG_CTRL_IFACE */ #ifdef NEED_AP_MLME void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { struct hostapd_iface *iface = wpa_s->ap_iface; if (!iface) iface = wpa_s->ifmsh; if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); hostapd_dfs_radar_detected(iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { struct hostapd_iface *iface = wpa_s->ap_iface; if (!iface) iface = wpa_s->ifmsh; if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq); hostapd_dfs_start_cac(iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { struct hostapd_iface *iface = wpa_s->ap_iface; if (!iface) iface = wpa_s->ifmsh; if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); hostapd_dfs_complete_cac(iface, 1, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { struct hostapd_iface *iface = wpa_s->ap_iface; if (!iface) iface = wpa_s->ifmsh; if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); hostapd_dfs_complete_cac(iface, 0, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { struct hostapd_iface *iface = wpa_s->ap_iface; if (!iface) iface = wpa_s->ifmsh; if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); hostapd_dfs_nop_finished(iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } #endif /* NEED_AP_MLME */ void ap_periodic(struct wpa_supplicant *wpa_s) { if (wpa_s->ap_iface) hostapd_periodic_iface(wpa_s->ap_iface); } diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index ba91cfb11d14..8a6a829b6665 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1,12516 +1,12535 @@ /* * WPA Supplicant / Control interface (shared code for all backends) * Copyright (c) 2004-2020, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #ifdef CONFIG_TESTING_OPTIONS #include #endif /* CONFIG_TESTING_OPTIONS */ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" #include "utils/module_tests.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #ifdef CONFIG_DPP #include "common/dpp.h" #endif /* CONFIG_DPP */ #include "common/ptksa_cache.h" #include "crypto/tls.h" #include "ap/hostapd.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "l2_packet/l2_packet.h" #include "wps/wps.h" #include "fst/fst.h" #include "fst/fst_ctrl_iface.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "wpas_glue.h" #include "ap.h" #include "p2p_supplicant.h" #include "p2p/p2p.h" #include "hs20_supplicant.h" #include "wifi_display.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "ctrl_iface.h" #include "interworking.h" #include "bssid_ignore.h" #include "autoscan.h" #include "wnm_sta.h" #include "offchannel.h" #include "drivers/driver.h" #include "mesh.h" #include "dpp_supplicant.h" #include "sme.h" #ifdef __NetBSD__ #include #elif !defined(__CYGWIN__) && !defined(CONFIG_NATIVE_WINDOWS) #include #endif static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, const char *input, char *buf, int len); static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val); static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val) { char *pos; u8 addr[ETH_ALEN], *filter = NULL, *n; size_t count = 0; pos = val; while (pos) { if (*pos == '\0') break; if (hwaddr_aton(pos, addr)) { os_free(filter); return -1; } n = os_realloc_array(filter, count + 1, ETH_ALEN); if (n == NULL) { os_free(filter); return -1; } filter = n; os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN); count++; pos = os_strchr(pos, ' '); if (pos) pos++; } wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN); os_free(wpa_s->bssid_filter); wpa_s->bssid_filter = filter; wpa_s->bssid_filter_count = count; return 0; } static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) { char *pos; u8 addr[ETH_ALEN], *bssid = NULL, *n; struct wpa_ssid_value *ssid = NULL, *ns; size_t count = 0, ssid_count = 0; struct wpa_ssid *c; /* * disallow_list ::= | | | "" * SSID_SPEC ::= ssid * BSSID_SPEC ::= bssid */ pos = val; while (pos) { if (*pos == '\0') break; if (os_strncmp(pos, "bssid ", 6) == 0) { int res; pos += 6; res = hwaddr_aton2(pos, addr); if (res < 0) { os_free(ssid); os_free(bssid); wpa_printf(MSG_DEBUG, "Invalid disallow_aps " "BSSID value '%s'", pos); return -1; } pos += res; n = os_realloc_array(bssid, count + 1, ETH_ALEN); if (n == NULL) { os_free(ssid); os_free(bssid); return -1; } bssid = n; os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN); count++; } else if (os_strncmp(pos, "ssid ", 5) == 0) { char *end; pos += 5; end = pos; while (*end) { if (*end == '\0' || *end == ' ') break; end++; } ns = os_realloc_array(ssid, ssid_count + 1, sizeof(struct wpa_ssid_value)); if (ns == NULL) { os_free(ssid); os_free(bssid); return -1; } ssid = ns; if ((end - pos) & 0x01 || end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { os_free(ssid); os_free(bssid); wpa_printf(MSG_DEBUG, "Invalid disallow_aps " "SSID value '%s'", pos); return -1; } ssid[ssid_count].ssid_len = (end - pos) / 2; wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID", ssid[ssid_count].ssid, ssid[ssid_count].ssid_len); ssid_count++; pos = end; } else { wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value " "'%s'", pos); os_free(ssid); os_free(bssid); return -1; } pos = os_strchr(pos, ' '); if (pos) pos++; } wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN); os_free(wpa_s->disallow_aps_bssid); wpa_s->disallow_aps_bssid = bssid; wpa_s->disallow_aps_bssid_count = count; wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count); os_free(wpa_s->disallow_aps_ssid); wpa_s->disallow_aps_ssid = ssid; wpa_s->disallow_aps_ssid_count = ssid_count; if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING) return 0; c = wpa_s->current_ssid; if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS) return 0; if (!disallowed_bssid(wpa_s, wpa_s->bssid) && !disallowed_ssid(wpa_s, c->ssid, c->ssid_len)) return 0; wpa_printf(MSG_DEBUG, "Disconnect and try to find another network " "because current AP was marked disallowed"); #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_s->reassociate = 1; wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; } #ifndef CONFIG_NO_CONFIG_BLOBS static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos) { char *name = pos; struct wpa_config_blob *blob; size_t len; pos = os_strchr(pos, ' '); if (pos == NULL) return -1; *pos++ = '\0'; len = os_strlen(pos); if (len & 1) return -1; wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name); blob = os_zalloc(sizeof(*blob)); if (blob == NULL) return -1; blob->name = os_strdup(name); blob->data = os_malloc(len / 2); if (blob->name == NULL || blob->data == NULL) { wpa_config_free_blob(blob); return -1; } if (hexstr2bin(pos, blob->data, len / 2) < 0) { wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data"); wpa_config_free_blob(blob); return -1; } blob->len = len / 2; wpa_config_set_blob(wpa_s->conf, blob); return 0; } #endif /* CONFIG_NO_CONFIG_BLOBS */ static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd) { char *params; char *pos; int *freqs = NULL; int ret; if (atoi(cmd)) { params = os_strchr(cmd, ' '); os_free(wpa_s->manual_sched_scan_freqs); if (params) { params++; pos = os_strstr(params, "freq="); if (pos) freqs = freq_range_to_channel_list(wpa_s, pos + 5); } wpa_s->manual_sched_scan_freqs = freqs; ret = wpas_start_pno(wpa_s); } else { ret = wpas_stop_pno(wpa_s); } return ret; } static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *bands) { union wpa_event_data event; u32 setband_mask = WPA_SETBAND_AUTO; /* * For example: * SET setband 2G,6G * SET setband 5G * SET setband AUTO */ if (!os_strstr(bands, "AUTO")) { if (os_strstr(bands, "5G")) setband_mask |= WPA_SETBAND_5G; if (os_strstr(bands, "6G")) setband_mask |= WPA_SETBAND_6G; if (os_strstr(bands, "2G")) setband_mask |= WPA_SETBAND_2G; if (setband_mask == WPA_SETBAND_AUTO) return -1; } wpa_s->setband_mask = setband_mask; if (wpa_drv_setband(wpa_s, wpa_s->setband_mask) == 0) { os_memset(&event, 0, sizeof(event)); event.channel_list_changed.initiator = REGDOM_SET_BY_USER; event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event); } return 0; } static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s, const char *cmd) { struct wpabuf *lci; if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) { wpabuf_free(wpa_s->lci); wpa_s->lci = NULL; return 0; } lci = wpabuf_parse_bin(cmd); if (!lci) return -1; if (os_get_reltime(&wpa_s->lci_time)) { wpabuf_free(lci); return -1; } wpabuf_free(wpa_s->lci); wpa_s->lci = lci; return 0; } static int wpas_ctrl_set_relative_rssi(struct wpa_supplicant *wpa_s, const char *cmd) { int relative_rssi; if (os_strcmp(cmd, "disable") == 0) { wpa_s->srp.relative_rssi_set = 0; return 0; } relative_rssi = atoi(cmd); if (relative_rssi < 0 || relative_rssi > 100) return -1; wpa_s->srp.relative_rssi = relative_rssi; wpa_s->srp.relative_rssi_set = 1; return 0; } static int wpas_ctrl_set_relative_band_adjust(struct wpa_supplicant *wpa_s, const char *cmd) { char *pos; int adjust_rssi; /* :adjust_value */ pos = os_strchr(cmd, ':'); if (!pos) return -1; pos++; adjust_rssi = atoi(pos); if (adjust_rssi < -100 || adjust_rssi > 100) return -1; if (os_strncmp(cmd, "2G", 2) == 0) wpa_s->srp.relative_adjust_band = WPA_SETBAND_2G; else if (os_strncmp(cmd, "5G", 2) == 0) wpa_s->srp.relative_adjust_band = WPA_SETBAND_5G; else return -1; wpa_s->srp.relative_adjust_rssi = adjust_rssi; return 0; } static int wpas_ctrl_iface_set_ric_ies(struct wpa_supplicant *wpa_s, const char *cmd) { struct wpabuf *ric_ies; if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) { wpabuf_free(wpa_s->ric_ies); wpa_s->ric_ies = NULL; return 0; } ric_ies = wpabuf_parse_bin(cmd); if (!ric_ies) return -1; wpabuf_free(wpa_s->ric_ies); wpa_s->ric_ies = ric_ies; return 0; } #ifdef CONFIG_TESTING_OPTIONS static int wpas_ctrl_iface_set_dso(struct wpa_supplicant *wpa_s, const char *val) { u8 bssid[ETH_ALEN]; const char *pos = val; struct driver_signal_override *dso = NULL, *tmp, parsed; if (hwaddr_aton(pos, bssid)) return -1; pos = os_strchr(pos, ' '); dl_list_for_each(tmp, &wpa_s->drv_signal_override, struct driver_signal_override, list) { if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) { dso = tmp; break; } } if (!pos) { /* Remove existing entry */ if (dso) { dl_list_del(&dso->list); os_free(dso); } return 0; } pos++; /* Update an existing entry or add a new one */ os_memset(&parsed, 0, sizeof(parsed)); if (sscanf(pos, "%d %d %d %d %d", &parsed.si_current_signal, &parsed.si_avg_signal, &parsed.si_avg_beacon_signal, &parsed.si_current_noise, &parsed.scan_level) != 5) return -1; if (!dso) { dso = os_zalloc(sizeof(*dso)); if (!dso) return -1; os_memcpy(dso->bssid, bssid, ETH_ALEN); dl_list_add(&wpa_s->drv_signal_override, &dso->list); } dso->si_current_signal = parsed.si_current_signal; dso->si_avg_signal = parsed.si_avg_signal; dso->si_avg_beacon_signal = parsed.si_avg_beacon_signal; dso->si_current_noise = parsed.si_current_noise; dso->scan_level = parsed.scan_level; return 0; } #endif /* CONFIG_TESTING_OPTIONS */ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { char *value; int ret = 0; value = os_strchr(cmd, ' '); if (value == NULL) return -1; *value++ = '\0'; wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { eapol_sm_configure(wpa_s->eapol, atoi(value), -1, -1, -1); } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) { eapol_sm_configure(wpa_s->eapol, -1, atoi(value), -1, -1); } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) { eapol_sm_configure(wpa_s->eapol, -1, -1, atoi(value), -1); } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) { eapol_sm_configure(wpa_s->eapol, -1, -1, -1, atoi(value)); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "EAPOL::portControl") == 0) { if (os_strcmp(value, "Auto") == 0) eapol_sm_notify_portControl(wpa_s->eapol, Auto); else if (os_strcmp(value, "ForceUnauthorized") == 0) eapol_sm_notify_portControl(wpa_s->eapol, ForceUnauthorized); else if (os_strcmp(value, "ForceAuthorized") == 0) eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); else ret = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, atoi(value))) { ret = -1; } else { value[-1] = '='; wpa_config_process_global(wpa_s->conf, cmd, -1); } } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, atoi(value))) { ret = -1; } else { value[-1] = '='; wpa_config_process_global(wpa_s->conf, cmd, -1); } } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) { ret = -1; } else { value[-1] = '='; wpa_config_process_global(wpa_s->conf, cmd, -1); } } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) { wpa_s->wps_fragment_size = atoi(value); #ifdef CONFIG_WPS_TESTING } else if (os_strcasecmp(cmd, "wps_version_number") == 0) { long int val; val = strtol(value, NULL, 0); if (val < 0 || val > 0xff) { ret = -1; wpa_printf(MSG_DEBUG, "WPS: Invalid " "wps_version_number %ld", val); } else { wps_version_number = val; wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS " "version %u.%u", (wps_version_number & 0xf0) >> 4, wps_version_number & 0x0f); } } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) { wps_testing_dummy_cred = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", wps_testing_dummy_cred); } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { wps_corrupt_pkhash = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); } else if (os_strcasecmp(cmd, "wps_force_auth_types") == 0) { if (value[0] == '\0') { wps_force_auth_types_in_use = 0; } else { wps_force_auth_types = strtol(value, NULL, 0); wps_force_auth_types_in_use = 1; } } else if (os_strcasecmp(cmd, "wps_force_encr_types") == 0) { if (value[0] == '\0') { wps_force_encr_types_in_use = 0; } else { wps_force_encr_types = strtol(value, NULL, 0); wps_force_encr_types_in_use = 1; } #endif /* CONFIG_WPS_TESTING */ } else if (os_strcasecmp(cmd, "ampdu") == 0) { if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0) ret = -1; #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING } else if (os_strcasecmp(cmd, "tdls_testing") == 0) { tdls_testing = strtol(value, NULL, 0); wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing); #endif /* CONFIG_TDLS_TESTING */ } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) { int disabled = atoi(value); wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled); if (disabled) { if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0) ret = -1; } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0) ret = -1; wpa_tdls_enable(wpa_s->wpa, !disabled); #endif /* CONFIG_TDLS */ } else if (os_strcasecmp(cmd, "pno") == 0) { ret = wpas_ctrl_pno(wpa_s, value); } else if (os_strcasecmp(cmd, "radio_disabled") == 0) { int disabled = atoi(value); if (wpa_drv_radio_disable(wpa_s, disabled) < 0) ret = -1; else if (disabled) wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); } else if (os_strcasecmp(cmd, "uapsd") == 0) { if (os_strcmp(value, "disable") == 0) wpa_s->set_sta_uapsd = 0; else { int be, bk, vi, vo; char *pos; /* format: BE,BK,VI,VO;max SP Length */ be = atoi(value); pos = os_strchr(value, ','); if (pos == NULL) return -1; pos++; bk = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vi = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vo = atoi(pos); /* ignore max SP Length for now */ wpa_s->set_sta_uapsd = 1; wpa_s->sta_uapsd = 0; if (be) wpa_s->sta_uapsd |= BIT(0); if (bk) wpa_s->sta_uapsd |= BIT(1); if (vi) wpa_s->sta_uapsd |= BIT(2); if (vo) wpa_s->sta_uapsd |= BIT(3); } } else if (os_strcasecmp(cmd, "ps") == 0) { ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strcasecmp(cmd, "wifi_display") == 0) { int enabled = !!atoi(value); if (enabled && !wpa_s->global->p2p) ret = -1; else wifi_display_enable(wpa_s->global, enabled); #endif /* CONFIG_WIFI_DISPLAY */ } else if (os_strcasecmp(cmd, "bssid_filter") == 0) { ret = set_bssid_filter(wpa_s, value); } else if (os_strcasecmp(cmd, "disallow_aps") == 0) { ret = set_disallow_aps(wpa_s, value); } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) { wpa_s->no_keep_alive = !!atoi(value); #ifdef CONFIG_DPP } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { os_free(wpa_s->dpp_configurator_params); wpa_s->dpp_configurator_params = os_strdup(value); } else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) { wpa_s->dpp_init_max_tries = atoi(value); } else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) { wpa_s->dpp_init_retry_time = atoi(value); } else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) { wpa_s->dpp_resp_wait_time = atoi(value); } else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) { wpa_s->dpp_resp_max_tries = atoi(value); } else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) { wpa_s->dpp_resp_retry_time = atoi(value); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "dpp_pkex_own_mac_override") == 0) { if (hwaddr_aton(value, dpp_pkex_own_mac_override)) ret = -1; } else if (os_strcasecmp(cmd, "dpp_pkex_peer_mac_override") == 0) { if (hwaddr_aton(value, dpp_pkex_peer_mac_override)) ret = -1; } else if (os_strcasecmp(cmd, "dpp_pkex_ephemeral_key_override") == 0) { size_t hex_len = os_strlen(value); if (hex_len > 2 * sizeof(dpp_pkex_ephemeral_key_override)) ret = -1; else if (hexstr2bin(value, dpp_pkex_ephemeral_key_override, hex_len / 2)) ret = -1; else dpp_pkex_ephemeral_key_override_len = hex_len / 2; } else if (os_strcasecmp(cmd, "dpp_protocol_key_override") == 0) { size_t hex_len = os_strlen(value); if (hex_len > 2 * sizeof(dpp_protocol_key_override)) ret = -1; else if (hexstr2bin(value, dpp_protocol_key_override, hex_len / 2)) ret = -1; else dpp_protocol_key_override_len = hex_len / 2; } else if (os_strcasecmp(cmd, "dpp_nonce_override") == 0) { size_t hex_len = os_strlen(value); if (hex_len > 2 * sizeof(dpp_nonce_override)) ret = -1; else if (hexstr2bin(value, dpp_nonce_override, hex_len / 2)) ret = -1; else dpp_nonce_override_len = hex_len / 2; } else if (os_strcasecmp(cmd, "dpp_version_override") == 0) { dpp_version_override = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_DPP */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { wpa_s->ext_mgmt_frame_handling = !!atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { wpa_s->ext_eapol_frame_io = !!atoi(value); #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_s->ap_iface->bss[0]->ext_eapol_frame_io = wpa_s->ext_eapol_frame_io; } #endif /* CONFIG_AP */ } else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) { wpa_s->extra_roc_dur = atoi(value); } else if (os_strcasecmp(cmd, "test_failure") == 0) { wpa_s->test_failure = atoi(value); } else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) { wpa_s->p2p_go_csa_on_inv = !!atoi(value); } else if (os_strcasecmp(cmd, "ignore_auth_resp") == 0) { wpa_s->ignore_auth_resp = !!atoi(value); } else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) { wpa_s->ignore_assoc_disallow = !!atoi(value); wpa_drv_ignore_assoc_disallow(wpa_s, wpa_s->ignore_assoc_disallow); } else if (os_strcasecmp(cmd, "disable_sa_query") == 0) { wpa_s->disable_sa_query = !!atoi(value); } else if (os_strcasecmp(cmd, "ignore_sae_h2e_only") == 0) { wpa_s->ignore_sae_h2e_only = !!atoi(value); } else if (os_strcasecmp(cmd, "extra_sae_rejected_groups") == 0) { char *pos; os_free(wpa_s->extra_sae_rejected_groups); wpa_s->extra_sae_rejected_groups = NULL; pos = value; while (pos && pos[0]) { int group; group = atoi(pos); wpa_printf(MSG_DEBUG, "TESTING: Extra rejection of SAE group %d", group); if (group) int_array_add_unique( &wpa_s->extra_sae_rejected_groups, group); pos = os_strchr(pos, ' '); if (!pos) break; pos++; } } else if (os_strcasecmp(cmd, "ft_rsnxe_used") == 0) { wpa_s->ft_rsnxe_used = atoi(value); } else if (os_strcasecmp(cmd, "oci_freq_override_eapol") == 0) { wpa_s->oci_freq_override_eapol = atoi(value); } else if (os_strcasecmp(cmd, "oci_freq_override_saquery_req") == 0) { wpa_s->oci_freq_override_saquery_req = atoi(value); } else if (os_strcasecmp(cmd, "oci_freq_override_saquery_resp") == 0) { wpa_s->oci_freq_override_saquery_resp = atoi(value); } else if (os_strcasecmp(cmd, "oci_freq_override_eapol_g2") == 0) { wpa_s->oci_freq_override_eapol_g2 = atoi(value); /* Populate value to wpa_sm if already associated. */ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2, wpa_s->oci_freq_override_eapol_g2); } else if (os_strcasecmp(cmd, "oci_freq_override_ft_assoc") == 0) { wpa_s->oci_freq_override_ft_assoc = atoi(value); /* Populate value to wpa_sm if already associated. */ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC, wpa_s->oci_freq_override_ft_assoc); } else if (os_strcasecmp(cmd, "oci_freq_override_fils_assoc") == 0) { wpa_s->oci_freq_override_fils_assoc = atoi(value); } else if (os_strcasecmp(cmd, "oci_freq_override_wnm_sleep") == 0) { wpa_s->oci_freq_override_wnm_sleep = atoi(value); } else if (os_strcasecmp(cmd, "rsne_override_eapol") == 0) { wpabuf_free(wpa_s->rsne_override_eapol); if (os_strcmp(value, "NULL") == 0) wpa_s->rsne_override_eapol = NULL; else wpa_s->rsne_override_eapol = wpabuf_parse_bin(value); } else if (os_strcasecmp(cmd, "rsnxe_override_assoc") == 0) { wpabuf_free(wpa_s->rsnxe_override_assoc); if (os_strcmp(value, "NULL") == 0) wpa_s->rsnxe_override_assoc = NULL; else wpa_s->rsnxe_override_assoc = wpabuf_parse_bin(value); } else if (os_strcasecmp(cmd, "rsnxe_override_eapol") == 0) { wpabuf_free(wpa_s->rsnxe_override_eapol); if (os_strcmp(value, "NULL") == 0) wpa_s->rsnxe_override_eapol = NULL; else wpa_s->rsnxe_override_eapol = wpabuf_parse_bin(value); } else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) { wpa_s->reject_btm_req_reason = atoi(value); } else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) { os_free(wpa_s->get_pref_freq_list_override); if (!value[0]) wpa_s->get_pref_freq_list_override = NULL; else wpa_s->get_pref_freq_list_override = os_strdup(value); } else if (os_strcasecmp(cmd, "sae_commit_override") == 0) { wpabuf_free(wpa_s->sae_commit_override); if (value[0] == '\0') wpa_s->sae_commit_override = NULL; else wpa_s->sae_commit_override = wpabuf_parse_bin(value); } else if (os_strcasecmp(cmd, "driver_signal_override") == 0) { ret = wpas_ctrl_iface_set_dso(wpa_s, value); #ifdef CONFIG_DPP } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { os_free(wpa_s->dpp_config_obj_override); if (value[0] == '\0') wpa_s->dpp_config_obj_override = NULL; else wpa_s->dpp_config_obj_override = os_strdup(value); } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { os_free(wpa_s->dpp_discovery_override); if (value[0] == '\0') wpa_s->dpp_discovery_override = NULL; else wpa_s->dpp_discovery_override = os_strdup(value); } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { os_free(wpa_s->dpp_groups_override); if (value[0] == '\0') wpa_s->dpp_groups_override = NULL; else wpa_s->dpp_groups_override = os_strdup(value); } else if (os_strcasecmp(cmd, "dpp_ignore_netaccesskey_mismatch") == 0) { wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value); } else if (os_strcasecmp(cmd, "dpp_test") == 0) { dpp_test = atoi(value); #endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_FILS } else if (os_strcasecmp(cmd, "disable_fils") == 0) { wpa_s->disable_fils = !!atoi(value); wpa_drv_disable_fils(wpa_s, wpa_s->disable_fils); wpa_supplicant_set_default_scan_ies(wpa_s); #endif /* CONFIG_FILS */ #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strcmp(cmd, "blob") == 0) { ret = wpas_ctrl_set_blob(wpa_s, value); #endif /* CONFIG_NO_CONFIG_BLOBS */ } else if (os_strcasecmp(cmd, "setband") == 0) { ret = wpas_ctrl_set_band(wpa_s, value); #ifdef CONFIG_MBO } else if (os_strcasecmp(cmd, "non_pref_chan") == 0) { ret = wpas_mbo_update_non_pref_chan(wpa_s, value); if (ret == 0) { value[-1] = '='; wpa_config_process_global(wpa_s->conf, cmd, -1); } } else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) { wpas_mbo_update_cell_capa(wpa_s, atoi(value)); } else if (os_strcasecmp(cmd, "oce") == 0) { wpa_s->conf->oce = atoi(value); if (wpa_s->conf->oce) { if ((wpa_s->conf->oce & OCE_STA) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA)) wpa_s->enable_oce = OCE_STA; if ((wpa_s->conf->oce & OCE_STA_CFON) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) { /* TODO: Need to add STA-CFON support */ wpa_printf(MSG_ERROR, "OCE STA-CFON feature is not yet supported"); return -1; } } else { wpa_s->enable_oce = 0; } wpa_supplicant_set_default_scan_ies(wpa_s); #endif /* CONFIG_MBO */ } else if (os_strcasecmp(cmd, "lci") == 0) { ret = wpas_ctrl_iface_set_lci(wpa_s, value); } else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) { ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value)); } else if (os_strcasecmp(cmd, "relative_rssi") == 0) { ret = wpas_ctrl_set_relative_rssi(wpa_s, value); } else if (os_strcasecmp(cmd, "relative_band_adjust") == 0) { ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value); } else if (os_strcasecmp(cmd, "ric_ies") == 0) { ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value); } else if (os_strcasecmp(cmd, "roaming") == 0) { ret = wpa_drv_roaming(wpa_s, atoi(value), NULL); #ifdef CONFIG_WNM } else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) { struct wpabuf *elems; elems = wpabuf_parse_bin(value); if (!elems) return -1; wnm_set_coloc_intf_elems(wpa_s, elems); #endif /* CONFIG_WNM */ } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); if (ret == 0) wpa_supplicant_update_config(wpa_s); else if (ret == 1) ret = 0; } return ret; } static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int res = -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); if (os_strcmp(cmd, "version") == 0) { res = os_snprintf(buf, buflen, "%s", VERSION_STR); } else if (os_strcasecmp(cmd, "max_command_len") == 0) { res = os_snprintf(buf, buflen, "%u", CTRL_IFACE_MAX_LEN); } else if (os_strcasecmp(cmd, "country") == 0) { if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) res = os_snprintf(buf, buflen, "%c%c", wpa_s->conf->country[0], wpa_s->conf->country[1]); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strcasecmp(cmd, "wifi_display") == 0) { int enabled; if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) enabled = 0; else enabled = wpa_s->global->wifi_display; res = os_snprintf(buf, buflen, "%d", enabled); #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_TESTING_GET_GTK } else if (os_strcmp(cmd, "gtk") == 0) { if (wpa_s->last_gtk_len == 0) return -1; res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk, wpa_s->last_gtk_len); return res; #endif /* CONFIG_TESTING_GET_GTK */ } else if (os_strcmp(cmd, "tls_library") == 0) { res = tls_get_library_version(buf, buflen); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcmp(cmd, "anonce") == 0) { return wpa_snprintf_hex(buf, buflen, wpa_sm_get_anonce(wpa_s->wpa), WPA_NONCE_LEN); } else if (os_strcasecmp(cmd, "last_tk_key_idx") == 0) { res = os_snprintf(buf, buflen, "%d", wpa_s->last_tk_key_idx); #endif /* CONFIG_TESTING_OPTIONS */ } else { res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen); } if (os_snprintf_error(buflen, res)) return -1; return res; } #ifdef IEEE8021X_EAPOL static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, char *addr) { u8 bssid[ETH_ALEN]; struct wpa_ssid *ssid = wpa_s->current_ssid; if (hwaddr_aton(addr, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " "'%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); rsn_preauth_deinit(wpa_s->wpa); if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL)) return -1; return 0; } #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_TDLS static int wpa_supplicant_ctrl_iface_tdls_discover( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR, MAC2STR(peer)); if (wpa_tdls_is_external_setup(wpa_s->wpa)) ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer); else ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer); return ret; } static int wpa_supplicant_ctrl_iface_tdls_setup( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR, MAC2STR(peer)); if ((wpa_s->conf->tdls_external_control) && wpa_tdls_is_external_setup(wpa_s->wpa)) return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); wpa_tdls_remove(wpa_s->wpa, peer); if (wpa_tdls_is_external_setup(wpa_s->wpa)) ret = wpa_tdls_start(wpa_s->wpa, peer); else ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); return ret; } static int wpa_supplicant_ctrl_iface_tdls_teardown( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; int ret; if (os_strcmp(addr, "*") == 0) { /* remove everyone */ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *"); wpa_tdls_teardown_peers(wpa_s->wpa); return 0; } if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR, MAC2STR(peer)); if ((wpa_s->conf->tdls_external_control) && wpa_tdls_is_external_setup(wpa_s->wpa)) return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); if (wpa_tdls_is_external_setup(wpa_s->wpa)) ret = wpa_tdls_teardown_link( wpa_s->wpa, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); else ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); return ret; } static int ctrl_iface_get_capability_tdls( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { int ret; ret = os_snprintf(buf, buflen, "%s\n", wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ? (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ? "EXTERNAL" : "INTERNAL") : "UNSUPPORTED"); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpa_supplicant_ctrl_iface_tdls_chan_switch( struct wpa_supplicant *wpa_s, char *cmd) { u8 peer[ETH_ALEN]; struct hostapd_freq_params freq_params; u8 oper_class; char *pos, *end; if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { wpa_printf(MSG_INFO, "tdls_chanswitch: Only supported with external setup"); return -1; } os_memset(&freq_params, 0, sizeof(freq_params)); pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; oper_class = strtol(pos, &end, 10); if (pos == end) { wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid op class provided"); return -1; } pos = end; freq_params.freq = atoi(pos); if (freq_params.freq == 0) { wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided"); return -1; } #define SET_FREQ_SETTING(str) \ do { \ const char *pos2 = os_strstr(pos, " " #str "="); \ if (pos2) { \ pos2 += sizeof(" " #str "=") - 1; \ freq_params.str = atoi(pos2); \ } \ } while (0) SET_FREQ_SETTING(center_freq1); SET_FREQ_SETTING(center_freq2); SET_FREQ_SETTING(bandwidth); SET_FREQ_SETTING(sec_channel_offset); #undef SET_FREQ_SETTING freq_params.ht_enabled = !!os_strstr(pos, " ht"); freq_params.vht_enabled = !!os_strstr(pos, " vht"); if (hwaddr_aton(cmd, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'", cmd); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s", MAC2STR(peer), oper_class, freq_params.freq, freq_params.center_freq1, freq_params.center_freq2, freq_params.bandwidth, freq_params.sec_channel_offset, freq_params.ht_enabled ? " HT" : "", freq_params.vht_enabled ? " VHT" : ""); return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class, &freq_params); } static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch( struct wpa_supplicant *wpa_s, char *cmd) { u8 peer[ETH_ALEN]; if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { wpa_printf(MSG_INFO, "tdls_chanswitch: Only supported with external setup"); return -1; } if (hwaddr_aton(cmd, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'", cmd); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR, MAC2STR(peer)); return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); } static int wpa_supplicant_ctrl_iface_tdls_link_status( struct wpa_supplicant *wpa_s, const char *addr, char *buf, size_t buflen) { u8 peer[ETH_ALEN]; const char *tdls_status; int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR, MAC2STR(peer)); tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer); wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status); ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status); if (os_snprintf_error(buflen, ret)) return -1; return ret; } #endif /* CONFIG_TDLS */ static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd) { char *token, *context = NULL; struct wmm_ac_ts_setup_params params = { .tsid = 0xff, .direction = 0xff, }; while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "tsid=%i", ¶ms.tsid) == 1 || sscanf(token, "up=%i", ¶ms.user_priority) == 1 || sscanf(token, "nominal_msdu_size=%i", ¶ms.nominal_msdu_size) == 1 || sscanf(token, "mean_data_rate=%i", ¶ms.mean_data_rate) == 1 || sscanf(token, "min_phy_rate=%i", ¶ms.minimum_phy_rate) == 1 || sscanf(token, "sba=%i", ¶ms.surplus_bandwidth_allowance) == 1) continue; if (os_strcasecmp(token, "downlink") == 0) { params.direction = WMM_TSPEC_DIRECTION_DOWNLINK; } else if (os_strcasecmp(token, "uplink") == 0) { params.direction = WMM_TSPEC_DIRECTION_UPLINK; } else if (os_strcasecmp(token, "bidi") == 0) { params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL; } else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) { params.fixed_nominal_msdu = 1; } else { wpa_printf(MSG_DEBUG, "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'", token); return -1; } } return wpas_wmm_ac_addts(wpa_s, ¶ms); } static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd) { u8 tsid = atoi(cmd); return wpas_wmm_ac_delts(wpa_s, tsid); } #ifdef CONFIG_IEEE80211R static int wpa_supplicant_ctrl_iface_ft_ds( struct wpa_supplicant *wpa_s, char *addr) { u8 target_ap[ETH_ALEN]; struct wpa_bss *bss; const u8 *mdie; if (hwaddr_aton(addr, target_ap)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap)); bss = wpa_bss_get_bssid(wpa_s, target_ap); if (bss) mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); else mdie = NULL; return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN], *_bssid = bssid; #ifdef CONFIG_P2P u8 p2p_dev_addr[ETH_ALEN]; #endif /* CONFIG_P2P */ #ifdef CONFIG_AP u8 *_p2p_dev_addr = NULL; #endif /* CONFIG_AP */ char *pos; int multi_ap = 0; if (!cmd || os_strcmp(cmd, "any") == 0 || os_strncmp(cmd, "any ", 4) == 0) { _bssid = NULL; #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { if (hwaddr_aton(cmd + 13, p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid " "P2P Device Address '%s'", cmd + 13); return -1; } _p2p_dev_addr = p2p_dev_addr; #endif /* CONFIG_P2P */ } else if (os_strncmp(cmd, "multi_ap=", 9) == 0) { _bssid = NULL; multi_ap = atoi(cmd + 9); } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", cmd); return -1; } if (cmd) { pos = os_strstr(cmd, " multi_ap="); if (pos) { pos += 10; multi_ap = atoi(pos); } } #ifdef CONFIG_AP if (wpa_s->ap_iface) return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr); #endif /* CONFIG_AP */ return wpas_wps_start_pbc(wpa_s, _bssid, 0, multi_ap); } static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN], *_bssid = bssid; char *pin; int ret; pin = os_strchr(cmd, ' '); if (pin) *pin++ = '\0'; if (os_strcmp(cmd, "any") == 0) _bssid = NULL; else if (os_strcmp(cmd, "get") == 0) { if (wps_generate_pin((unsigned int *) &ret) < 0) return -1; goto done; } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", cmd); return -1; } #ifdef CONFIG_AP if (wpa_s->ap_iface) { int timeout = 0; char *pos; if (pin) { pos = os_strchr(pin, ' '); if (pos) { *pos++ = '\0'; timeout = atoi(pos); } } return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin, buf, buflen, timeout); } #endif /* CONFIG_AP */ if (pin) { ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, DEV_PW_DEFAULT); if (ret < 0) return -1; ret = os_snprintf(buf, buflen, "%s", pin); if (os_snprintf_error(buflen, ret)) return -1; return ret; } ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT); if (ret < 0) return -1; done: /* Return the generated PIN */ ret = os_snprintf(buf, buflen, "%08d", ret); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpa_supplicant_ctrl_iface_wps_check_pin( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { char pin[9]; size_t len; char *pos; int ret; wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN", (u8 *) cmd, os_strlen(cmd)); for (pos = cmd, len = 0; *pos != '\0'; pos++) { if (*pos < '0' || *pos > '9') continue; pin[len++] = *pos; if (len == 9) { wpa_printf(MSG_DEBUG, "WPS: Too long PIN"); return -1; } } if (len != 4 && len != 8) { wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len); return -1; } pin[len] = '\0'; if (len == 8) { unsigned int pin_val; pin_val = atoi(pin); if (!wps_pin_valid(pin_val)) { wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); if (os_snprintf_error(buflen, ret)) return -1; return ret; } } ret = os_snprintf(buf, buflen, "%s", pin); if (os_snprintf_error(buflen, ret)) return -1; return ret; } #ifdef CONFIG_WPS_NFC static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN], *_bssid = bssid; if (cmd == NULL || cmd[0] == '\0') _bssid = NULL; else if (hwaddr_aton(cmd, bssid)) return -1; return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL, 0, 0); } static int wpa_supplicant_ctrl_iface_wps_nfc_config_token( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { int ndef; struct wpabuf *buf; int res; char *pos; pos = os_strchr(cmd, ' '); if (pos) *pos++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } static int wpa_supplicant_ctrl_iface_wps_nfc_token( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { int ndef; struct wpabuf *buf; int res; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; buf = wpas_wps_nfc_token(wpa_s, ndef); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( struct wpa_supplicant *wpa_s, char *pos) { size_t len; struct wpabuf *buf; int ret; char *freq; int forced_freq = 0; freq = strstr(pos, " freq="); if (freq) { *freq = '\0'; freq += 6; forced_freq = atoi(freq); } len = os_strlen(pos); if (len & 0x01) return -1; len /= 2; buf = wpabuf_alloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { wpabuf_free(buf); return -1; } ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq); wpabuf_free(buf); return ret; } static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef) { struct wpabuf *buf; int res; buf = wpas_wps_nfc_handover_req(wpa_s, ndef); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #ifdef CONFIG_P2P static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef) { struct wpabuf *buf; int res; buf = wpas_p2p_nfc_handover_req(wpa_s, ndef); if (buf == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request"); return -1; } res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #endif /* CONFIG_P2P */ static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos; int ndef; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { if (!ndef) return -1; return wpas_ctrl_nfc_get_handover_req_wps( wpa_s, reply, max_len, ndef); } #ifdef CONFIG_P2P if (os_strcmp(pos, "P2P-CR") == 0) { return wpas_ctrl_nfc_get_handover_req_p2p( wpa_s, reply, max_len, ndef); } #endif /* CONFIG_P2P */ return -1; } static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef, int cr, char *uuid) { struct wpabuf *buf; int res; buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #ifdef CONFIG_P2P static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s, char *reply, size_t max_len, int ndef, int tag) { struct wpabuf *buf; int res; buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #endif /* CONFIG_P2P */ static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos, *pos2; int ndef; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; pos2 = os_strchr(pos, ' '); if (pos2) *pos2++ = '\0'; if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { if (!ndef) return -1; return wpas_ctrl_nfc_get_handover_sel_wps( wpa_s, reply, max_len, ndef, os_strcmp(pos, "WPS-CR") == 0, pos2); } #ifdef CONFIG_P2P if (os_strcmp(pos, "P2P-CR") == 0) { return wpas_ctrl_nfc_get_handover_sel_p2p( wpa_s, reply, max_len, ndef, 0); } if (os_strcmp(pos, "P2P-CR-TAG") == 0) { return wpas_ctrl_nfc_get_handover_sel_p2p( wpa_s, reply, max_len, ndef, 1); } #endif /* CONFIG_P2P */ return -1; } static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s, char *cmd) { size_t len; struct wpabuf *req, *sel; int ret; char *pos, *role, *type, *pos2; #ifdef CONFIG_P2P char *freq; int forced_freq = 0; freq = strstr(cmd, " freq="); if (freq) { *freq = '\0'; freq += 6; forced_freq = atoi(freq); } #endif /* CONFIG_P2P */ role = cmd; pos = os_strchr(role, ' '); if (pos == NULL) { wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report"); return -1; } *pos++ = '\0'; type = pos; pos = os_strchr(type, ' '); if (pos == NULL) { wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report"); return -1; } *pos++ = '\0'; pos2 = os_strchr(pos, ' '); if (pos2 == NULL) { wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report"); return -1; } *pos2++ = '\0'; len = os_strlen(pos); if (len & 0x01) { wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report"); return -1; } len /= 2; req = wpabuf_alloc(len); if (req == NULL) { wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message"); return -1; } if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report"); wpabuf_free(req); return -1; } len = os_strlen(pos2); if (len & 0x01) { wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report"); wpabuf_free(req); return -1; } len /= 2; sel = wpabuf_alloc(len); if (sel == NULL) { wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message"); wpabuf_free(req); return -1; } if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report"); wpabuf_free(req); wpabuf_free(sel); return -1; } wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d", role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel)); if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) { ret = wpas_wps_nfc_report_handover(wpa_s, req, sel); #ifdef CONFIG_AP } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) { ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel); if (ret < 0) ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel); #endif /* CONFIG_AP */ #ifdef CONFIG_P2P } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0) { ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0); } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0) { ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel, forced_freq); #endif /* CONFIG_P2P */ } else { wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " "reported: role=%s type=%s", role, type); ret = -1; } wpabuf_free(req); wpabuf_free(sel); if (ret) wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages"); return ret; } #endif /* CONFIG_WPS_NFC */ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN]; char *pin; char *new_ssid; char *new_auth; char *new_encr; char *new_key; struct wps_new_ap_settings ap; pin = os_strchr(cmd, ' '); if (pin == NULL) return -1; *pin++ = '\0'; if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", cmd); return -1; } new_ssid = os_strchr(pin, ' '); if (new_ssid == NULL) return wpas_wps_start_reg(wpa_s, bssid, pin, NULL); *new_ssid++ = '\0'; new_auth = os_strchr(new_ssid, ' '); if (new_auth == NULL) return -1; *new_auth++ = '\0'; new_encr = os_strchr(new_auth, ' '); if (new_encr == NULL) return -1; *new_encr++ = '\0'; new_key = os_strchr(new_encr, ' '); if (new_key == NULL) return -1; *new_key++ = '\0'; os_memset(&ap, 0, sizeof(ap)); ap.ssid_hex = new_ssid; ap.auth = new_auth; ap.encr = new_encr; ap.key_hex = new_key; return wpas_wps_start_reg(wpa_s, bssid, pin, &ap); } #ifdef CONFIG_AP static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int timeout = 300; char *pos; const char *pin_txt; if (!wpa_s->ap_iface) return -1; pos = os_strchr(cmd, ' '); if (pos) *pos++ = '\0'; if (os_strcmp(cmd, "disable") == 0) { wpas_wps_ap_pin_disable(wpa_s); return os_snprintf(buf, buflen, "OK\n"); } if (os_strcmp(cmd, "random") == 0) { if (pos) timeout = atoi(pos); pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout); if (pin_txt == NULL) return -1; return os_snprintf(buf, buflen, "%s", pin_txt); } if (os_strcmp(cmd, "get") == 0) { pin_txt = wpas_wps_ap_pin_get(wpa_s); if (pin_txt == NULL) return -1; return os_snprintf(buf, buflen, "%s", pin_txt); } if (os_strcmp(cmd, "set") == 0) { char *pin; if (pos == NULL) return -1; pin = pos; pos = os_strchr(pos, ' '); if (pos) { *pos++ = '\0'; timeout = atoi(pos); } if (os_strlen(pin) > buflen) return -1; if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0) return -1; return os_snprintf(buf, buflen, "%s", pin); } return -1; } #endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, char *cmd) { char *uuid = cmd, *pin, *pos; u8 addr_buf[ETH_ALEN], *addr = NULL; pin = os_strchr(uuid, ' '); if (pin == NULL) return -1; *pin++ = '\0'; pos = os_strchr(pin, ' '); if (pos) { *pos++ = '\0'; if (hwaddr_aton(pos, addr_buf) == 0) addr = addr_buf; } return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin); } static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s, char *cmd) { char *uuid = cmd, *pin; pin = os_strchr(uuid, ' '); if (pin == NULL) return -1; *pin++ = '\0'; return wpas_wps_er_learn(wpa_s, uuid, pin); } static int wpa_supplicant_ctrl_iface_wps_er_set_config( struct wpa_supplicant *wpa_s, char *cmd) { char *uuid = cmd, *id; id = os_strchr(uuid, ' '); if (id == NULL) return -1; *id++ = '\0'; return wpas_wps_er_set_config(wpa_s, uuid, atoi(id)); } static int wpa_supplicant_ctrl_iface_wps_er_config( struct wpa_supplicant *wpa_s, char *cmd) { char *pin; char *new_ssid; char *new_auth; char *new_encr; char *new_key; struct wps_new_ap_settings ap; pin = os_strchr(cmd, ' '); if (pin == NULL) return -1; *pin++ = '\0'; new_ssid = os_strchr(pin, ' '); if (new_ssid == NULL) return -1; *new_ssid++ = '\0'; new_auth = os_strchr(new_ssid, ' '); if (new_auth == NULL) return -1; *new_auth++ = '\0'; new_encr = os_strchr(new_auth, ' '); if (new_encr == NULL) return -1; *new_encr++ = '\0'; new_key = os_strchr(new_encr, ' '); if (new_key == NULL) return -1; *new_key++ = '\0'; os_memset(&ap, 0, sizeof(ap)); ap.ssid_hex = new_ssid; ap.auth = new_auth; ap.encr = new_encr; ap.key_hex = new_key; return wpas_wps_er_config(wpa_s, cmd, pin, &ap); } #ifdef CONFIG_WPS_NFC static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { int ndef; struct wpabuf *buf; int res; char *uuid; uuid = os_strchr(cmd, ' '); if (uuid == NULL) return -1; *uuid++ = '\0'; if (os_strcmp(cmd, "WPS") == 0) ndef = 0; else if (os_strcmp(cmd, "NDEF") == 0) ndef = 1; else return -1; buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid); if (buf == NULL) return -1; res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), wpabuf_len(buf)); reply[res++] = '\n'; reply[res] = '\0'; wpabuf_free(buf); return res; } #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN static int wpa_supplicant_ctrl_iface_ibss_rsn( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR, MAC2STR(peer)); return ibss_rsn_start(wpa_s->ibss_rsn, peer); } #endif /* CONFIG_IBSS_RSN */ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, char *rsp) { #ifdef IEEE8021X_EAPOL char *pos, *id_pos; int id; struct wpa_ssid *ssid; pos = os_strchr(rsp, '-'); if (pos == NULL) return -1; *pos++ = '\0'; id_pos = pos; pos = os_strchr(pos, ':'); if (pos == NULL) return -1; *pos++ = '\0'; id = atoi(id_pos); wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) pos, os_strlen(pos)); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "to update", id); return -1; } return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp, pos); #else /* IEEE8021X_EAPOL */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); return -1; #endif /* IEEE8021X_EAPOL */ } static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, const char *params, char *buf, size_t buflen) { char *pos, *end, tmp[30]; int res, verbose, wps, ret; #ifdef CONFIG_HS20 const u8 *hs20; #endif /* CONFIG_HS20 */ const u8 *sess_id; size_t sess_id_len; if (os_strcmp(params, "-DRIVER") == 0) return wpa_drv_status(wpa_s, buf, buflen); verbose = os_strcmp(params, "-VERBOSE") == 0; wps = os_strcmp(params, "-WPS") == 0; pos = buf; end = buf + buflen; if (wpa_s->wpa_state >= WPA_ASSOCIATED) { struct wpa_ssid *ssid = wpa_s->current_ssid; ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(wpa_s->bssid)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "freq=%u\n", wpa_s->assoc_freq); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (ssid) { u8 *_ssid = ssid->ssid; size_t ssid_len = ssid->ssid_len; u8 ssid_buf[SSID_MAX_LEN]; if (ssid_len == 0) { int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); if (_res < 0) ssid_len = 0; else ssid_len = _res; _ssid = ssid_buf; } ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", wpa_ssid_txt(_ssid, ssid_len), ssid->id); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (wps && ssid->passphrase && wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO)) { ret = os_snprintf(pos, end - pos, "passphrase=%s\n", ssid->passphrase); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (ssid->id_str) { ret = os_snprintf(pos, end - pos, "id_str=%s\n", ssid->id_str); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } switch (ssid->mode) { case WPAS_MODE_INFRA: ret = os_snprintf(pos, end - pos, "mode=station\n"); break; case WPAS_MODE_IBSS: ret = os_snprintf(pos, end - pos, "mode=IBSS\n"); break; case WPAS_MODE_AP: ret = os_snprintf(pos, end - pos, "mode=AP\n"); break; case WPAS_MODE_P2P_GO: ret = os_snprintf(pos, end - pos, "mode=P2P GO\n"); break; case WPAS_MODE_P2P_GROUP_FORMATION: ret = os_snprintf(pos, end - pos, "mode=P2P GO - group " "formation\n"); break; case WPAS_MODE_MESH: ret = os_snprintf(pos, end - pos, "mode=mesh\n"); break; default: ret = 0; break; } if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (wpa_s->connection_set && (wpa_s->connection_ht || wpa_s->connection_vht || wpa_s->connection_he)) { ret = os_snprintf(pos, end - pos, "wifi_generation=%u\n", wpa_s->connection_he ? 6 : (wpa_s->connection_vht ? 5 : 4)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_AP if (wpa_s->ap_iface) { pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos, end - pos, verbose); } else #endif /* CONFIG_AP */ pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); } #ifdef CONFIG_SME #ifdef CONFIG_SAE if (wpa_s->wpa_state >= WPA_ASSOCIATED && #ifdef CONFIG_AP !wpa_s->ap_iface && #endif /* CONFIG_AP */ wpa_s->sme.sae.state == SAE_ACCEPTED) { ret = os_snprintf(pos, end - pos, "sae_group=%d\n" "sae_h2e=%d\n" "sae_pk=%d\n", wpa_s->sme.sae.group, wpa_s->sme.sae.h2e, wpa_s->sme.sae.pk); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ #endif /* CONFIG_SME */ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", wpa_supplicant_state_txt(wpa_s->wpa_state)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (wpa_s->l2 && l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_P2P if (wpa_s->global->p2p) { ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR "\n", MAC2STR(wpa_s->global->p2p_dev_addr)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_P2P */ ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n", MAC2STR(wpa_s->own_addr)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #ifdef CONFIG_HS20 if (wpa_s->current_bss && (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE)) && wpa_s->wpa_proto == WPA_PROTO_RSN && wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { int release = 1; if (hs20[1] >= 5) { u8 rel_num = (hs20[6] & 0xf0) >> 4; release = rel_num + 1; } ret = os_snprintf(pos, end - pos, "hs20=%d\n", release); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (wpa_s->current_ssid) { struct wpa_cred *cred; char *type; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { size_t i; if (wpa_s->current_ssid->parent_cred != cred) continue; if (cred->provisioning_sp) { ret = os_snprintf(pos, end - pos, "provisioning_sp=%s\n", cred->provisioning_sp); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (!cred->domain) goto no_domain; i = 0; if (wpa_s->current_bss && wpa_s->current_bss->anqp) { struct wpabuf *names = wpa_s->current_bss->anqp->domain_name; for (i = 0; names && i < cred->num_domain; i++) { if (domain_name_list_contains( names, cred->domain[i], 1)) break; } if (i == cred->num_domain) i = 0; /* show first entry by default */ } ret = os_snprintf(pos, end - pos, "home_sp=%s\n", cred->domain[i]); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; no_domain: if (wpa_s->current_bss == NULL || wpa_s->current_bss->anqp == NULL) res = -1; else res = interworking_home_sp_cred( wpa_s, cred, wpa_s->current_bss->anqp->domain_name); if (res > 0) type = "home"; else if (res == 0) type = "roaming"; else type = "unknown"; ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; break; } } #endif /* CONFIG_HS20 */ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, verbose); if (res >= 0) pos += res; } #ifdef CONFIG_MACSEC res = ieee802_1x_kay_get_status(wpa_s->kay, pos, end - pos); if (res > 0) pos += res; #endif /* CONFIG_MACSEC */ sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len); if (sess_id) { char *start = pos; ret = os_snprintf(pos, end - pos, "eap_session_id="); if (os_snprintf_error(end - pos, ret)) return start - buf; pos += ret; ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len); if (ret <= 0) return start - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return start - buf; pos += ret; } res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); if (res >= 0) pos += res; #ifdef CONFIG_WPS { char uuid_str[100]; uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str)); ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_WPS */ if (wpa_s->ieee80211ac) { ret = os_snprintf(pos, end - pos, "ieee80211ac=1\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef ANDROID /* * Allow using the STATUS command with default behavior, say for debug, * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE * events with STATUS-NO_EVENTS. */ if (os_strcmp(params, "-NO_EVENTS")) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE "id=%d state=%d BSSID=" MACSTR " SSID=%s", wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, wpa_s->wpa_state, MAC2STR(wpa_s->bssid), wpa_s->current_ssid && wpa_s->current_ssid->ssid ? wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len) : ""); if (wpa_s->wpa_state == WPA_COMPLETED) { struct wpa_ssid *ssid = wpa_s->current_ssid; wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- connection to " MACSTR " completed %s [id=%d id_str=%s]", MAC2STR(wpa_s->bssid), "(auth)", ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : ""); } } #endif /* ANDROID */ return pos - buf; } static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; int id; struct wpa_ssid *ssid; u8 bssid[ETH_ALEN]; /* cmd: " " */ pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos); if (hwaddr_aton(pos, bssid)) { wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos); return -1; } ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "to update", id); return -1; } os_memcpy(ssid->bssid, bssid, ETH_ALEN); ssid->bssid_set = !is_zero_ether_addr(bssid); return 0; } static int wpa_supplicant_ctrl_iface_bssid_ignore(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN]; struct wpa_bssid_ignore *e; char *pos, *end; int ret; /* cmd: "BSSID_IGNORE []" */ if (*cmd == '\0') { pos = buf; end = buf + buflen; e = wpa_s->bssid_ignore; while (e) { ret = os_snprintf(pos, end - pos, MACSTR "\n", MAC2STR(e->bssid)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; e = e->next; } return pos - buf; } cmd++; if (os_strncmp(cmd, "clear", 5) == 0) { wpa_bssid_ignore_clear(wpa_s); os_memcpy(buf, "OK\n", 3); return 3; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: BSSID_IGNORE bssid='%s'", cmd); if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd); return -1; } /* * Add the BSSID twice, so its count will be 2, causing it to be * skipped when processing scan results. */ ret = wpa_bssid_ignore_add(wpa_s, bssid); if (ret < 0) return -1; ret = wpa_bssid_ignore_add(wpa_s, bssid); if (ret < 0) return -1; os_memcpy(buf, "OK\n", 3); return 3; } static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { char *pos, *end, *stamp; int ret; /* cmd: "LOG_LEVEL []" */ if (*cmd == '\0') { pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "Current level: %s\n" "Timestamp: %d\n", debug_level_str(wpa_debug_level), wpa_debug_timestamp); if (os_snprintf_error(end - pos, ret)) ret = 0; return ret; } while (*cmd == ' ') cmd++; stamp = os_strchr(cmd, ' '); if (stamp) { *stamp++ = '\0'; while (*stamp == ' ') { stamp++; } } if (os_strlen(cmd)) { int level = str_to_debug_level(cmd); if (level < 0) return -1; wpa_debug_level = level; } if (stamp && os_strlen(stamp)) wpa_debug_timestamp = atoi(stamp); os_memcpy(buf, "OK\n", 3); return 3; } static int wpa_supplicant_ctrl_iface_list_networks( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { char *pos, *end, *prev; struct wpa_ssid *ssid; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "network id / ssid / bssid / flags\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ssid = wpa_s->conf->ssid; /* skip over ssids until we find next one */ if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) { int last_id = atoi(cmd + 8); if (last_id != -1) { while (ssid != NULL && ssid->id <= last_id) { ssid = ssid->next; } } } while (ssid) { prev = pos; ret = os_snprintf(pos, end - pos, "%d\t%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; if (ssid->bssid_set) { ret = os_snprintf(pos, end - pos, "\t" MACSTR, MAC2STR(ssid->bssid)); } else { ret = os_snprintf(pos, end - pos, "\tany"); } if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", ssid->disabled ? "[DISABLED]" : "", ssid->disabled_until.sec ? "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ssid = ssid->next; } return pos - buf; } static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) { int ret; ret = os_snprintf(pos, end - pos, "-"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; ret = wpa_write_ciphers(pos, end, cipher, "+"); if (ret < 0) return pos; pos += ret; return pos; } static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, const u8 *ie, size_t ie_len) { struct wpa_ie_data data; char *start; int ret; ret = os_snprintf(pos, end - pos, "[%s-", proto); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { ret = os_snprintf(pos, end - pos, "?]"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; return pos; } start = pos; if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sEAP", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "%sPSK", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, "%sNone", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_SAE) { ret = os_snprintf(pos, end - pos, "%sSAE", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #ifdef CONFIG_IEEE80211R if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sFT/EAP", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "%sFT/PSK", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, "%sFT/SAE", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_IEEE80211R */ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { ret = os_snprintf(pos, end - pos, "%sEAP-SHA256", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "%sPSK-SHA256", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #ifdef CONFIG_SUITEB if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_SUITEB192 */ #ifdef CONFIG_FILS if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { ret = os_snprintf(pos, end - pos, "%sFILS-SHA256", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { ret = os_snprintf(pos, end - pos, "%sFILS-SHA384", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #ifdef CONFIG_IEEE80211R if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (data.key_mgmt & WPA_KEY_MGMT_OWE) { ret = os_snprintf(pos, end - pos, "%sOWE", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP if (data.key_mgmt & WPA_KEY_MGMT_DPP) { ret = os_snprintf(pos, end - pos, "%sDPP", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_DPP */ if (data.key_mgmt & WPA_KEY_MGMT_OSEN) { ret = os_snprintf(pos, end - pos, "%sOSEN", pos == start ? "" : "+"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); if (data.capabilities & WPA_CAPABILITY_PREAUTH) { ret = os_snprintf(pos, end - pos, "-preauth"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } ret = os_snprintf(pos, end - pos, "]"); if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; return pos; } #ifdef CONFIG_WPS static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, char *pos, char *end, struct wpabuf *wps_ie) { int ret; const char *txt; if (wps_ie == NULL) return pos; if (wps_is_selected_pbc_registrar(wps_ie)) txt = "[WPS-PBC]"; else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0)) txt = "[WPS-AUTH]"; else if (wps_is_selected_pin_registrar(wps_ie)) txt = "[WPS-PIN]"; else txt = "[WPS]"; ret = os_snprintf(pos, end - pos, "%s", txt); if (!os_snprintf_error(end - pos, ret)) pos += ret; wpabuf_free(wps_ie); return pos; } #endif /* CONFIG_WPS */ static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s, char *pos, char *end, const struct wpa_bss *bss) { #ifdef CONFIG_WPS struct wpabuf *wps_ie; wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie); #else /* CONFIG_WPS */ return pos; #endif /* CONFIG_WPS */ } /* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( struct wpa_supplicant *wpa_s, const struct wpa_bss *bss, char *buf, size_t buflen) { char *pos, *end; int ret; const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe, *rsnxe; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); if (!p2p) p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE); if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN && os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) return 0; /* Do not show P2P listen discovery results here */ pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", MAC2STR(bss->bssid), bss->freq, bss->level); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) { pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); } rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) { ret = os_snprintf(pos, end - pos, "[SAE-H2E]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_PK)) { ret = os_snprintf(pos, end - pos, "[SAE-PK]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); if (osen_ie) pos = wpa_supplicant_ie_txt(pos, end, "OSEN", osen_ie, 2 + osen_ie[1]); owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (owe) { ret = os_snprintf(pos, end - pos, ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (mesh) { ret = os_snprintf(pos, end - pos, "[MESH]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (bss_is_dmg(bss)) { const char *s; if (wpa_bss_get_ie_ext(bss, WLAN_EID_EXT_EDMG_OPERATION)) { ret = os_snprintf(pos, end - pos, "[EDMG]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } ret = os_snprintf(pos, end - pos, "[DMG]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; switch (bss->caps & IEEE80211_CAP_DMG_MASK) { case IEEE80211_CAP_DMG_IBSS: s = "[IBSS]"; break; case IEEE80211_CAP_DMG_AP: s = "[ESS]"; break; case IEEE80211_CAP_DMG_PBSS: s = "[PBSS]"; break; default: s = ""; break; } ret = os_snprintf(pos, end - pos, "%s", s); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } else { if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } } if (p2p) { ret = os_snprintf(pos, end - pos, "[P2P]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { ret = os_snprintf(pos, end - pos, "[HS20]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #endif /* CONFIG_HS20 */ #ifdef CONFIG_FILS if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) { ret = os_snprintf(pos, end - pos, "[FILS]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #endif /* CONFIG_FILS */ #ifdef CONFIG_FST if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { ret = os_snprintf(pos, end - pos, "[FST]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #endif /* CONFIG_FST */ if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) { ret = os_snprintf(pos, end - pos, "[UTF-8]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; return pos - buf; } static int wpa_supplicant_ctrl_iface_scan_results( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { char *pos, *end; struct wpa_bss *bss; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " "flags / ssid\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos, end - pos); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } return pos - buf; } #ifdef CONFIG_MESH static int wpa_supplicant_ctrl_iface_mesh_interface_add( struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos, ifname[IFNAMSIZ + 1]; ifname[0] = '\0'; pos = os_strstr(cmd, "ifname="); if (pos) { pos += 7; os_strlcpy(ifname, pos, sizeof(ifname)); } if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0) return -1; os_strlcpy(reply, ifname, max_len); return os_strlen(ifname); } static int wpa_supplicant_ctrl_iface_mesh_group_add( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network id=%d", id); return -1; } if (ssid->mode != WPAS_MODE_MESH) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network"); return -1; } if (ssid->key_mgmt != WPA_KEY_MGMT_NONE && ssid->key_mgmt != WPA_KEY_MGMT_SAE) { wpa_printf(MSG_ERROR, "CTRL_IFACE: key_mgmt for mesh network should be open or SAE"); return -1; } /* * TODO: If necessary write our own group_add function, * for now we can reuse select_network */ wpa_supplicant_select_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_mesh_group_remove( struct wpa_supplicant *wpa_s, char *cmd) { struct wpa_supplicant *orig; struct wpa_global *global; int found = 0; wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd); global = wpa_s->global; orig = wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, cmd) == 0) { found = 1; break; } } if (!found) { wpa_printf(MSG_ERROR, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found", cmd); return -1; } if (wpa_s->mesh_if_created && wpa_s == orig) { wpa_printf(MSG_ERROR, "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself"); return -1; } wpa_s->reassociate = 0; wpa_s->disconnected = 1; wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); /* * TODO: If necessary write our own group_remove function, * for now we can reuse deauthenticate */ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); if (wpa_s->mesh_if_created) wpa_supplicant_remove_iface(global, wpa_s, 0); return 0; } static int wpa_supplicant_ctrl_iface_mesh_peer_remove( struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; if (hwaddr_aton(cmd, addr) < 0) return -1; return wpas_mesh_peer_remove(wpa_s, addr); } static int wpa_supplicant_ctrl_iface_mesh_peer_add( struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; int duration; char *pos; pos = os_strstr(cmd, " duration="); if (pos) { *pos = '\0'; duration = atoi(pos + 10); } else { duration = -1; } if (hwaddr_aton(cmd, addr)) return -1; return wpas_mesh_peer_add(wpa_s, addr, duration); } static int wpa_supplicant_ctrl_iface_mesh_link_probe( struct wpa_supplicant *wpa_s, char *cmd) { struct ether_header *eth; u8 addr[ETH_ALEN]; u8 *buf; char *pos; size_t payload_len = 0, len; int ret = -1; if (hwaddr_aton(cmd, addr)) return -1; pos = os_strstr(cmd, " payload="); if (pos) { pos = pos + 9; payload_len = os_strlen(pos); if (payload_len & 1) return -1; payload_len /= 2; } len = ETH_HLEN + payload_len; buf = os_malloc(len); if (!buf) return -1; eth = (struct ether_header *) buf; os_memcpy(eth->ether_dhost, addr, ETH_ALEN); os_memcpy(eth->ether_shost, wpa_s->own_addr, ETH_ALEN); eth->ether_type = htons(ETH_P_802_3); if (payload_len && hexstr2bin(pos, buf + ETH_HLEN, payload_len) < 0) goto fail; ret = wpa_drv_mesh_link_probe(wpa_s, addr, buf, len); fail: os_free(buf); return -ret; } #endif /* CONFIG_MESH */ static int wpa_supplicant_ctrl_iface_select_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; char *pos; /* cmd: "" or "any" */ if (os_strncmp(cmd, "any", 3) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); ssid = NULL; } else { id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id); return -1; } if (ssid->disabled == 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " "SELECT_NETWORK with persistent P2P group"); return -1; } } pos = os_strstr(cmd, " freq="); if (pos) { int *freqs = freq_range_to_channel_list(wpa_s, pos + 6); if (freqs) { os_free(wpa_s->select_network_scan_freqs); wpa_s->select_network_scan_freqs = freqs; } } wpa_s->scan_min_time.sec = 0; wpa_s->scan_min_time.usec = 0; wpa_supplicant_select_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_enable_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all"); ssid = NULL; } else { id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id); return -1; } if (ssid->disabled == 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " "ENABLE_NETWORK with persistent P2P group"); return -1; } if (os_strstr(cmd, " no-connect")) { ssid->disabled = 0; return 0; } } wpa_s->scan_min_time.sec = 0; wpa_s->scan_min_time.usec = 0; wpa_supplicant_enable_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_disable_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all"); ssid = NULL; } else { id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id); return -1; } if (ssid->disabled == 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " "DISABLE_NETWORK with persistent P2P " "group"); return -1; } } wpa_supplicant_disable_network(wpa_s, ssid); return 0; } static int wpa_supplicant_ctrl_iface_add_network( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_ssid *ssid; int ret; wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); ssid = wpa_supplicant_add_network(wpa_s); if (ssid == NULL) return -1; ret = os_snprintf(buf, buflen, "%d\n", ssid->id); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpa_supplicant_ctrl_iface_remove_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; int result; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); return wpa_supplicant_remove_all_networks(wpa_s); } id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); result = wpa_supplicant_remove_network(wpa_s, id); if (result == -1) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } if (result == -2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " "network id=%d", id); return -1; } return 0; } static int wpa_supplicant_ctrl_iface_update_network( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, char *name, char *value) { int ret; ret = wpa_config_set(ssid, name, value, 0); if (ret < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " "variable '%s'", name); return -1; } if (ret == 1) return 0; /* No change to the previously configured value */ #ifdef CONFIG_BGSCAN if (os_strcmp(name, "bgscan") == 0) { /* * Reset the bgscan parameters for the current network and * return. There's no need to flush caches for bgscan parameter * changes. */ if (wpa_s->current_ssid == ssid && wpa_s->wpa_state == WPA_COMPLETED) wpa_supplicant_reset_bgscan(wpa_s); return 0; } #endif /* CONFIG_BGSCAN */ if (os_strcmp(name, "bssid") != 0 && os_strcmp(name, "bssid_hint") != 0 && os_strcmp(name, "priority") != 0) { wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { /* * Invalidate the EAP session cache if anything in the * current or previously used configuration changes. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } } if ((os_strcmp(name, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || (os_strcmp(name, "ssid") == 0 && ssid->passphrase)) wpa_config_update_psk(ssid); else if (os_strcmp(name, "priority") == 0) wpa_config_update_prio_list(wpa_s->conf); return 0; } static int wpa_supplicant_ctrl_iface_set_network( struct wpa_supplicant *wpa_s, char *cmd) { int id, ret, prev_bssid_set, prev_disabled; struct wpa_ssid *ssid; char *name, *value; u8 prev_bssid[ETH_ALEN]; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL) return -1; *name++ = '\0'; value = os_strchr(name, ' '); if (value == NULL) return -1; *value++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'", id, name); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) value, os_strlen(value)); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } prev_bssid_set = ssid->bssid_set; prev_disabled = ssid->disabled; os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN); ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name, value); if (ret == 0 && (ssid->bssid_set != prev_bssid_set || os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0)) wpas_notify_network_bssid_set_changed(wpa_s, ssid); if (prev_disabled != ssid->disabled && (prev_disabled == 2 || ssid->disabled == 2)) wpas_notify_network_type_changed(wpa_s, ssid); return ret; } static int wpa_supplicant_ctrl_iface_get_network( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int id; size_t res; struct wpa_ssid *ssid; char *name, *value; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL || buflen == 0) return -1; *name++ = '\0'; id = atoi(cmd); wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", id, name); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } value = wpa_config_get_no_key(ssid, name); if (value == NULL) { wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network " "variable '%s'", name); return -1; } res = os_strlcpy(buf, value, buflen); if (res >= buflen) { os_free(value); return -1; } os_free(value); return res; } static int wpa_supplicant_ctrl_iface_dup_network( struct wpa_supplicant *wpa_s, char *cmd, struct wpa_supplicant *dst_wpa_s) { struct wpa_ssid *ssid_s, *ssid_d; char *name, *id, *value; int id_s, id_d, ret; /* cmd: " " */ id = os_strchr(cmd, ' '); if (id == NULL) return -1; *id++ = '\0'; name = os_strchr(id, ' '); if (name == NULL) return -1; *name++ = '\0'; id_s = atoi(cmd); id_d = atoi(id); wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'", wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name); ssid_s = wpa_config_get_network(wpa_s->conf, id_s); if (ssid_s == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id_s); return -1; } ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d); if (ssid_d == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id_d); return -1; } value = wpa_config_get(ssid_s, name); if (value == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " "variable '%s'", name); return -1; } ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name, value); os_free(value); return ret; } static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { char *pos, *end; struct wpa_cred *cred; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "cred id / realm / username / domain / imsi\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; cred = wpa_s->conf->cred; while (cred) { ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n", cred->id, cred->realm ? cred->realm : "", cred->username ? cred->username : "", cred->domain ? cred->domain[0] : "", cred->imsi ? cred->imsi : ""); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; cred = cred->next; } return pos - buf; } static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_cred *cred; int ret; wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED"); cred = wpa_config_add_cred(wpa_s->conf); if (cred == NULL) return -1; wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id); ret = os_snprintf(buf, buflen, "%d\n", cred->id); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s, struct wpa_cred *cred) { struct wpa_ssid *ssid; char str[20]; int id; if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); return -1; } id = cred->id; if (wpa_config_remove_cred(wpa_s->conf, id) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); return -1; } wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id); /* Remove any network entry created based on the removed credential */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->parent_cred == cred) { int res; wpa_printf(MSG_DEBUG, "Remove network id %d since it " "used the removed credential", ssid->id); res = os_snprintf(str, sizeof(str), "%d", ssid->id); if (os_snprintf_error(sizeof(str), res)) str[sizeof(str) - 1] = '\0'; ssid = ssid->next; wpa_supplicant_ctrl_iface_remove_network(wpa_s, str); } else ssid = ssid->next; } return 0; } static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_cred *cred, *prev; /* cmd: "", "all", "sp_fqdn=", or * "provisioning_sp= */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all"); cred = wpa_s->conf->cred; while (cred) { prev = cred; cred = cred->next; wpas_ctrl_remove_cred(wpa_s, prev); } return 0; } if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'", cmd + 8); cred = wpa_s->conf->cred; while (cred) { prev = cred; cred = cred->next; if (prev->domain) { size_t i; for (i = 0; i < prev->num_domain; i++) { if (os_strcmp(prev->domain[i], cmd + 8) != 0) continue; wpas_ctrl_remove_cred(wpa_s, prev); break; } } } return 0; } if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'", cmd + 16); cred = wpa_s->conf->cred; while (cred) { prev = cred; cred = cred->next; if (prev->provisioning_sp && os_strcmp(prev->provisioning_sp, cmd + 16) == 0) wpas_ctrl_remove_cred(wpa_s, prev); } return 0; } id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id); cred = wpa_config_get_cred(wpa_s->conf, id); return wpas_ctrl_remove_cred(wpa_s, cred); } static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_cred *cred; char *name, *value; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL) return -1; *name++ = '\0'; value = os_strchr(name, ' '); if (value == NULL) return -1; *value++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'", id, name); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", (u8 *) value, os_strlen(value)); cred = wpa_config_get_cred(wpa_s->conf, id); if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", id); return -1; } if (wpa_config_set_cred(cred, name, value, 0) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred " "variable '%s'", name); return -1; } wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name); return 0; } static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int id; size_t res; struct wpa_cred *cred; char *name, *value; /* cmd: " " */ name = os_strchr(cmd, ' '); if (name == NULL) return -1; *name++ = '\0'; id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'", id, name); cred = wpa_config_get_cred(wpa_s->conf, id); if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", id); return -1; } value = wpa_config_get_cred_no_key(cred, name); if (value == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'", name); return -1; } res = os_strlcpy(buf, value, buflen); if (res >= buflen) { os_free(value); return -1; } os_free(value); return res; } #ifndef CONFIG_NO_CONFIG_WRITE static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) { int ret; if (!wpa_s->conf->update_config) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed " "to update configuration (update_config=0)"); return -1; } ret = wpa_config_write(wpa_s->confname, wpa_s->conf); if (ret) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to " "update configuration"); } else { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration" " updated"); } return ret; } #endif /* CONFIG_NO_CONFIG_WRITE */ struct cipher_info { unsigned int capa; const char *name; int group_only; }; static const struct cipher_info ciphers[] = { { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 }, { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 }, { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 }, { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 }, #ifndef CONFIG_NO_TKIP { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 }, #endif /* CONFIG_NO_TKIP */ { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 }, #ifdef CONFIG_WEP { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 }, { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 } #endif /* CONFIG_WEP */ }; static const struct cipher_info ciphers_group_mgmt[] = { { WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 }, { WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 }, { WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 }, { WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 }, }; static int ctrl_iface_get_capability_pairwise(int res, bool strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; unsigned int i; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; #ifdef CONFIG_NO_TKIP len = os_strlcpy(buf, "CCMP NONE", buflen); #else /* CONFIG_NO_TKIP */ len = os_strlcpy(buf, "CCMP TKIP NONE", buflen); #endif /* CONFIG_NO_TKIP */ if (len >= buflen) return -1; return len; } for (i = 0; i < ARRAY_SIZE(ciphers); i++) { if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers[i].name); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } static int ctrl_iface_get_capability_group(int res, bool strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; unsigned int i; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; #ifdef CONFIG_WEP #ifdef CONFIG_NO_TKIP len = os_strlcpy(buf, "CCMP WEP104 WEP40", buflen); #else /* CONFIG_NO_TKIP */ len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen); #endif /* CONFIG_NO_TKIP */ #else /* CONFIG_WEP */ #ifdef CONFIG_NO_TKIP len = os_strlcpy(buf, "CCMP", buflen); #else /* CONFIG_NO_TKIP */ len = os_strlcpy(buf, "CCMP TKIP", buflen); #endif /* CONFIG_NO_TKIP */ #endif /* CONFIG_WEP */ if (len >= buflen) return -1; return len; } for (i = 0; i < ARRAY_SIZE(ciphers); i++) { if (capa->enc & ciphers[i].capa) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers[i].name); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } static int ctrl_iface_get_capability_group_mgmt(int res, bool strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; unsigned int i; pos = buf; end = pos + buflen; if (res < 0) return 0; for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) { if (capa->enc & ciphers_group_mgmt[i].capa) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers_group_mgmt[i].name); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } } return pos - buf; } static int iftype_str_to_index(const char *iftype_str) { if (!iftype_str) return WPA_IF_MAX; if (os_strcmp(iftype_str, "STATION") == 0) return WPA_IF_STATION; if (os_strcmp(iftype_str, "AP_VLAN") == 0) return WPA_IF_AP_VLAN; if (os_strcmp(iftype_str, "AP") == 0) return WPA_IF_AP_BSS; if (os_strcmp(iftype_str, "P2P_GO") == 0) return WPA_IF_P2P_GO; if (os_strcmp(iftype_str, "P2P_CLIENT") == 0) return WPA_IF_P2P_CLIENT; if (os_strcmp(iftype_str, "P2P_DEVICE") == 0) return WPA_IF_P2P_DEVICE; if (os_strcmp(iftype_str, "MESH") == 0) return WPA_IF_MESH; if (os_strcmp(iftype_str, "IBSS") == 0) return WPA_IF_IBSS; if (os_strcmp(iftype_str, "NAN") == 0) return WPA_IF_NAN; return WPA_IF_MAX; } static int ctrl_iface_get_capability_key_mgmt(int res, bool strict, struct wpa_driver_capa *capa, const char *iftype_str, char *buf, size_t buflen) { int ret; unsigned int key_mgmt; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE " "NONE", buflen); if (len >= buflen) return -1; return len; } if (iftype_str) { enum wpa_driver_if_type iftype; iftype = iftype_str_to_index(iftype_str); if (iftype == WPA_IF_MAX) return -1; key_mgmt = capa->key_mgmt_iftype[iftype]; } else { key_mgmt = capa->key_mgmt; } ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { ret = os_snprintf(pos, end - pos, " WPA-EAP"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, " WPA-PSK"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, " WPA-NONE"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK) { ret = os_snprintf(pos, end - pos, " WAPI-PSK"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE) { ret = os_snprintf(pos, end - pos, " TPK-HANDSHAKE"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_CCKM) { ret = os_snprintf(pos, end - pos, " CCKM"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_SUITEB if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) { ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) { ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SUITEB192 */ #ifdef CONFIG_OWE if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) { ret = os_snprintf(pos, end - pos, " OWE"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) { ret = os_snprintf(pos, end - pos, " DPP"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_DPP */ #ifdef CONFIG_FILS if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) { ret = os_snprintf(pos, end - pos, " FILS-SHA256"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) { ret = os_snprintf(pos, end - pos, " FILS-SHA384"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_IEEE80211R if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) { ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) { ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, " FT-PSK"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) { ret = os_snprintf(pos, end - pos, " FT-EAP"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_SAE if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, " FT-SAE"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ #ifdef CONFIG_SHA384 if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384) { ret = os_snprintf(pos, end - pos, " FT-EAP-SHA384"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) { ret = os_snprintf(pos, end - pos, " SAE"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ #ifdef CONFIG_SHA256 if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256) { ret = os_snprintf(pos, end - pos, " WPA-EAP-SHA256"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, " WPA-PSK-SHA256"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SHA256 */ #ifdef CONFIG_HS20 if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OSEN) { ret = os_snprintf(pos, end - pos, " OSEN"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_HS20 */ return pos - buf; } static int ctrl_iface_get_capability_proto(int res, bool strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "RSN WPA", buflen); if (len >= buflen) return -1; return len; } if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, "%sRSN", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { ret = os_snprintf(pos, end - pos, "%sWPA", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s, int res, bool strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen); if (len >= buflen) return -1; return len; } if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { ret = os_snprintf(pos, end - pos, "%sOPEN", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) { ret = os_snprintf(pos, end - pos, "%sSHARED", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) { ret = os_snprintf(pos, end - pos, "%sLEAP", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_SAE if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) { ret = os_snprintf(pos, end - pos, "%sSAE", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS if (wpa_is_fils_supported(wpa_s)) { ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_FILS_SK_PFS if (wpa_is_fils_sk_pfs_supported(wpa_s)) { ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_FILS_SK_PFS */ #endif /* CONFIG_FILS */ #ifdef CONFIG_PASN ret = os_snprintf(pos, end - pos, "%sPASN", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #endif /* CONFIG_PASN */ return pos - buf; } static int ctrl_iface_get_capability_modes(int res, bool strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { int ret; char *pos, *end; size_t len; pos = buf; end = pos + buflen; if (res < 0) { if (strict) return 0; len = os_strlcpy(buf, "IBSS AP", buflen); if (len >= buflen) return -1; return len; } if (capa->flags & WPA_DRIVER_FLAGS_IBSS) { ret = os_snprintf(pos, end - pos, "%sIBSS", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->flags & WPA_DRIVER_FLAGS_AP) { ret = os_snprintf(pos, end - pos, "%sAP", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_MESH if (capa->flags & WPA_DRIVER_FLAGS_MESH) { ret = os_snprintf(pos, end - pos, "%sMESH", pos == buf ? "" : " "); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_MESH */ return pos - buf; } static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostapd_channel_data *chnl; int ret, i, j; char *pos, *end, *hmode; pos = buf; end = pos + buflen; for (j = 0; j < wpa_s->hw.num_modes; j++) { switch (wpa_s->hw.modes[j].mode) { case HOSTAPD_MODE_IEEE80211B: hmode = "B"; break; case HOSTAPD_MODE_IEEE80211G: hmode = "G"; break; case HOSTAPD_MODE_IEEE80211A: hmode = "A"; break; case HOSTAPD_MODE_IEEE80211AD: hmode = "AD"; break; default: continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) continue; ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostapd_channel_data *chnl; int ret, i, j; char *pos, *end, *hmode; pos = buf; end = pos + buflen; for (j = 0; j < wpa_s->hw.num_modes; j++) { switch (wpa_s->hw.modes[j].mode) { case HOSTAPD_MODE_IEEE80211B: hmode = "B"; break; case HOSTAPD_MODE_IEEE80211G: hmode = "G"; break; case HOSTAPD_MODE_IEEE80211A: hmode = "A"; break; case HOSTAPD_MODE_IEEE80211AD: hmode = "AD"; break; default: continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n", hmode); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) continue; ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n", chnl[i].chan, chnl[i].freq, chnl[i].flag & HOSTAPD_CHAN_NO_IR ? " (NO_IR)" : "", chnl[i].flag & HOSTAPD_CHAN_RADAR ? " (DFS)" : ""); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } static int wpa_supplicant_ctrl_iface_get_capability( struct wpa_supplicant *wpa_s, const char *_field, char *buf, size_t buflen) { struct wpa_driver_capa capa; int res; char *next_param, *curr_param, *iftype = NULL; bool strict = false; char field[50]; size_t len; /* Determine whether or not strict checking was requested */ len = os_strlcpy(field, _field, sizeof(field)); if (len >= sizeof(field)) return -1; next_param = os_strchr(field, ' '); while (next_param) { *next_param++ = '\0'; curr_param = next_param; next_param = os_strchr(next_param, ' '); if (next_param) *next_param = '\0'; if (os_strcmp(curr_param, "strict") == 0) strict = true; else if (os_strncmp(curr_param, "iftype=", 7) == 0) iftype = curr_param + 7; else return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'%s%s%s", field, iftype ? " iftype=" : "", iftype ? iftype : "", strict ? " strict" : ""); if (os_strcmp(field, "eap") == 0) { return eap_get_names(buf, buflen); } res = wpa_drv_get_capa(wpa_s, &capa); if (os_strcmp(field, "pairwise") == 0) return ctrl_iface_get_capability_pairwise(res, strict, &capa, buf, buflen); if (os_strcmp(field, "group") == 0) return ctrl_iface_get_capability_group(res, strict, &capa, buf, buflen); if (os_strcmp(field, "group_mgmt") == 0) return ctrl_iface_get_capability_group_mgmt(res, strict, &capa, buf, buflen); if (os_strcmp(field, "key_mgmt") == 0) return ctrl_iface_get_capability_key_mgmt(res, strict, &capa, iftype, buf, buflen); if (os_strcmp(field, "proto") == 0) return ctrl_iface_get_capability_proto(res, strict, &capa, buf, buflen); if (os_strcmp(field, "auth_alg") == 0) return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict, &capa, buf, buflen); if (os_strcmp(field, "modes") == 0) return ctrl_iface_get_capability_modes(res, strict, &capa, buf, buflen); if (os_strcmp(field, "channels") == 0) return ctrl_iface_get_capability_channels(wpa_s, buf, buflen); if (os_strcmp(field, "freq") == 0) return ctrl_iface_get_capability_freq(wpa_s, buf, buflen); #ifdef CONFIG_TDLS if (os_strcmp(field, "tdls") == 0) return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen); #endif /* CONFIG_TDLS */ #ifdef CONFIG_ERP if (os_strcmp(field, "erp") == 0) { res = os_snprintf(buf, buflen, "ERP"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_EPR */ #ifdef CONFIG_FIPS if (os_strcmp(field, "fips") == 0) { res = os_snprintf(buf, buflen, "FIPS"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_FIPS */ #ifdef CONFIG_ACS if (os_strcmp(field, "acs") == 0) { res = os_snprintf(buf, buflen, "ACS"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_ACS */ #ifdef CONFIG_FILS if (os_strcmp(field, "fils") == 0) { #ifdef CONFIG_FILS_SK_PFS if (wpa_is_fils_supported(wpa_s) && wpa_is_fils_sk_pfs_supported(wpa_s)) { res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_FILS_SK_PFS */ if (wpa_is_fils_supported(wpa_s)) { res = os_snprintf(buf, buflen, "FILS"); if (os_snprintf_error(buflen, res)) return -1; return res; } } #endif /* CONFIG_FILS */ if (os_strcmp(field, "multibss") == 0 && wpa_s->multi_bss_support) { res = os_snprintf(buf, buflen, "MULTIBSS-STA"); if (os_snprintf_error(buflen, res)) return -1; return res; } #ifdef CONFIG_DPP if (os_strcmp(field, "dpp") == 0) { #ifdef CONFIG_DPP2 res = os_snprintf(buf, buflen, "DPP=2"); #else /* CONFIG_DPP2 */ res = os_snprintf(buf, buflen, "DPP=1"); #endif /* CONFIG_DPP2 */ if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_DPP */ #ifdef CONFIG_SAE if (os_strcmp(field, "sae") == 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { #ifdef CONFIG_SAE_PK res = os_snprintf(buf, buflen, "H2E PK"); #else /* CONFIG_SAE_PK */ res = os_snprintf(buf, buflen, "H2E"); #endif /* CONFIG_SAE_PK */ if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_SAE */ #ifdef CONFIG_OCV if (os_strcmp(field, "ocv") == 0) { if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) res = os_snprintf(buf, buflen, "supported"); else res = os_snprintf(buf, buflen, "not supported"); if (os_snprintf_error(buflen, res)) return -1; return res; } #endif /* CONFIG_OCV */ if (os_strcmp(field, "beacon_prot") == 0) { if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION) || (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT)) res = os_snprintf(buf, buflen, "supported"); else res = os_snprintf(buf, buflen, "not supported"); if (os_snprintf_error(buflen, res)) return -1; return res; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); return -1; } #ifdef CONFIG_INTERWORKING static char * anqp_add_hex(char *pos, char *end, const char *title, struct wpabuf *data) { char *start = pos; size_t i; int ret; const u8 *d; if (data == NULL) return start; ret = os_snprintf(pos, end - pos, "%s=", title); if (os_snprintf_error(end - pos, ret)) return start; pos += ret; d = wpabuf_head_u8(data); for (i = 0; i < wpabuf_len(data); i++) { ret = os_snprintf(pos, end - pos, "%02x", *d++); if (os_snprintf_error(end - pos, ret)) return start; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return start; pos += ret; return pos; } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_FILS static int print_fils_indication(struct wpa_bss *bss, char *pos, char *end) { char *start = pos; const u8 *ie, *ie_end; u16 info, realms; int ret; ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION); if (!ie) return 0; ie_end = ie + 2 + ie[1]; ie += 2; if (ie_end - ie < 2) return -1; info = WPA_GET_LE16(ie); ie += 2; ret = os_snprintf(pos, end - pos, "fils_info=%04x\n", info); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; if (info & BIT(7)) { /* Cache Identifier Included */ if (ie_end - ie < 2) return -1; ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n", ie[0], ie[1]); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie += 2; } if (info & BIT(8)) { /* HESSID Included */ if (ie_end - ie < ETH_ALEN) return -1; ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n", MAC2STR(ie)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie += ETH_ALEN; } realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3; if (realms) { if (ie_end - ie < realms * 2) return -1; ret = os_snprintf(pos, end - pos, "fils_realms="); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ret = wpa_snprintf_hex(pos, end - pos, ie, realms * 2); if (ret <= 0) return 0; pos += ret; ie += realms * 2; ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } return pos - start; } #endif /* CONFIG_FILS */ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, unsigned long mask, char *buf, size_t buflen) { size_t i; int ret; char *pos, *end; const u8 *ie, *ie2, *osen_ie, *mesh, *owe, *rsnxe; pos = buf; end = buf + buflen; if (mask & WPA_BSS_MASK_ID) { ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_BSSID) { ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(bss->bssid)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FREQ) { ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_BEACON_INT) { ret = os_snprintf(pos, end - pos, "beacon_int=%d\n", bss->beacon_int); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_CAPABILITIES) { ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n", bss->caps); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_QUAL) { ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_NOISE) { ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_LEVEL) { ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_TSF) { ret = os_snprintf(pos, end - pos, "tsf=%016llu\n", (unsigned long long) bss->tsf); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_AGE) { struct os_reltime now; os_get_reltime(&now); ret = os_snprintf(pos, end - pos, "age=%d\n", (int) (now.sec - bss->last_update.sec)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_IE) { ret = os_snprintf(pos, end - pos, "ie="); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie = wpa_bss_ie_ptr(bss); for (i = 0; i < bss->ie_len; i++) { ret = os_snprintf(pos, end - pos, "%02x", *ie++); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FLAGS) { ret = os_snprintf(pos, end - pos, "flags="); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) { ret = os_snprintf(pos, end - pos, "[SAE-H2E]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_PK)) { ret = os_snprintf(pos, end - pos, "[SAE-PK]"); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); if (osen_ie) pos = wpa_supplicant_ie_txt(pos, end, "OSEN", osen_ie, 2 + osen_ie[1]); owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (owe) { ret = os_snprintf( pos, end - pos, ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mesh) { ret = os_snprintf(pos, end - pos, "[MESH]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (bss_is_dmg(bss)) { const char *s; ret = os_snprintf(pos, end - pos, "[DMG]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; switch (bss->caps & IEEE80211_CAP_DMG_MASK) { case IEEE80211_CAP_DMG_IBSS: s = "[IBSS]"; break; case IEEE80211_CAP_DMG_AP: s = "[ESS]"; break; case IEEE80211_CAP_DMG_PBSS: s = "[PBSS]"; break; default: s = ""; break; } ret = os_snprintf(pos, end - pos, "%s", s); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } else { if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } } if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[P2P]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[HS20]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #endif /* CONFIG_HS20 */ #ifdef CONFIG_FILS if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) { ret = os_snprintf(pos, end - pos, "[FILS]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #endif /* CONFIG_FILS */ #ifdef CONFIG_FST if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { ret = os_snprintf(pos, end - pos, "[FST]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #endif /* CONFIG_FST */ if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) { ret = os_snprintf(pos, end - pos, "[UTF-8]"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_SSID) { ret = os_snprintf(pos, end - pos, "ssid=%s\n", wpa_ssid_txt(bss->ssid, bss->ssid_len)); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_WPS if (mask & WPA_BSS_MASK_WPS_SCAN) { ie = wpa_bss_ie_ptr(bss); ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); if (ret >= end - pos) return 0; if (ret > 0) pos += ret; } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (mask & WPA_BSS_MASK_P2P_SCAN) { ie = wpa_bss_ie_ptr(bss); ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); if (ret >= end - pos) return 0; if (ret > 0) pos += ret; } #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY if (mask & WPA_BSS_MASK_WIFI_DISPLAY) { struct wpabuf *wfd; ie = wpa_bss_ie_ptr(bss); wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len, WFD_IE_VENDOR_TYPE); if (wfd) { ret = os_snprintf(pos, end - pos, "wfd_subelems="); if (os_snprintf_error(end - pos, ret)) { wpabuf_free(wfd); return 0; } pos += ret; pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(wfd), wpabuf_len(wfd)); wpabuf_free(wfd); ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } } #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { struct wpa_bss_anqp *anqp = bss->anqp; struct wpa_bss_anqp_elem *elem; pos = anqp_add_hex(pos, end, "anqp_capability_list", anqp->capability_list); pos = anqp_add_hex(pos, end, "anqp_venue_name", anqp->venue_name); pos = anqp_add_hex(pos, end, "anqp_network_auth_type", anqp->network_auth_type); pos = anqp_add_hex(pos, end, "anqp_roaming_consortium", anqp->roaming_consortium); pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability", anqp->ip_addr_type_availability); pos = anqp_add_hex(pos, end, "anqp_nai_realm", anqp->nai_realm); pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp); pos = anqp_add_hex(pos, end, "anqp_domain_name", anqp->domain_name); pos = anqp_add_hex(pos, end, "anqp_fils_realm_info", anqp->fils_realm_info); #ifdef CONFIG_HS20 pos = anqp_add_hex(pos, end, "hs20_capability_list", anqp->hs20_capability_list); pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name", anqp->hs20_operator_friendly_name); pos = anqp_add_hex(pos, end, "hs20_wan_metrics", anqp->hs20_wan_metrics); pos = anqp_add_hex(pos, end, "hs20_connection_capability", anqp->hs20_connection_capability); pos = anqp_add_hex(pos, end, "hs20_operating_class", anqp->hs20_operating_class); pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", anqp->hs20_osu_providers_list); pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata", anqp->hs20_operator_icon_metadata); pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list", anqp->hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ dl_list_for_each(elem, &anqp->anqp_elems, struct wpa_bss_anqp_elem, list) { char title[20]; os_snprintf(title, sizeof(title), "anqp[%u]", elem->infoid); pos = anqp_add_hex(pos, end, title, elem->payload); if (elem->protected_response) { ret = os_snprintf(pos, end - pos, "protected-anqp-info[%u]=1\n", elem->infoid); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } } } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_MESH if (mask & WPA_BSS_MASK_MESH_SCAN) { ie = wpa_bss_ie_ptr(bss); ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end); if (ret >= end - pos) return 0; if (ret > 0) pos += ret; } #endif /* CONFIG_MESH */ if (mask & WPA_BSS_MASK_SNR) { ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_EST_THROUGHPUT) { ret = os_snprintf(pos, end - pos, "est_throughput=%d\n", bss->est_throughput); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_FST if (mask & WPA_BSS_MASK_FST) { ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos); if (ret < 0 || ret >= end - pos) return 0; pos += ret; } #endif /* CONFIG_FST */ if (mask & WPA_BSS_MASK_UPDATE_IDX) { ret = os_snprintf(pos, end - pos, "update_idx=%u\n", bss->last_update_idx); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if ((mask & WPA_BSS_MASK_BEACON_IE) && bss->beacon_ie_len) { ret = os_snprintf(pos, end - pos, "beacon_ie="); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie = wpa_bss_ie_ptr(bss); ie += bss->ie_len; for (i = 0; i < bss->beacon_ie_len; i++) { ret = os_snprintf(pos, end - pos, "%02x", *ie++); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_FILS if (mask & WPA_BSS_MASK_FILS_INDICATION) { ret = print_fils_indication(bss, pos, end); if (ret < 0) return 0; pos += ret; } #endif /* CONFIG_FILS */ if (mask & WPA_BSS_MASK_DELIM) { ret = os_snprintf(pos, end - pos, "====\n"); if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } return pos - buf; } static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN]; size_t i; struct wpa_bss *bss; struct wpa_bss *bsslast = NULL; struct dl_list *next; int ret = 0; int len; char *ctmp, *end = buf + buflen; unsigned long mask = WPA_BSS_MASK_ALL; if (os_strncmp(cmd, "RANGE=", 6) == 0) { if (os_strncmp(cmd + 6, "ALL", 3) == 0) { bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); } else { /* N1-N2 */ unsigned int id1, id2; if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) { wpa_printf(MSG_INFO, "Wrong BSS range " "format"); return 0; } if (*(cmd + 6) == '-') id1 = 0; else id1 = atoi(cmd + 6); ctmp++; if (*ctmp >= '0' && *ctmp <= '9') id2 = atoi(ctmp); else id2 = (unsigned int) -1; bss = wpa_bss_get_id_range(wpa_s, id1, id2); if (id2 == (unsigned int) -1) bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); else { bsslast = wpa_bss_get_id(wpa_s, id2); if (bsslast == NULL && bss && id2 > id1) { struct wpa_bss *tmp = bss; for (;;) { next = tmp->list_id.next; if (next == &wpa_s->bss_id) break; tmp = dl_list_entry( next, struct wpa_bss, list_id); if (tmp->id > id2) break; bsslast = tmp; } } } } } else if (os_strncmp(cmd, "FIRST", 5) == 0) bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); else if (os_strncmp(cmd, "LAST", 4) == 0) bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); else if (os_strncmp(cmd, "ID-", 3) == 0) { i = atoi(cmd + 3); bss = wpa_bss_get_id(wpa_s, i); } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { i = atoi(cmd + 5); bss = wpa_bss_get_id(wpa_s, i); if (bss) { next = bss->list_id.next; if (next == &wpa_s->bss_id) bss = NULL; else bss = dl_list_entry(next, struct wpa_bss, list_id); } } else if (os_strncmp(cmd, "CURRENT", 7) == 0) { bss = wpa_s->current_bss; #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { if (hwaddr_aton(cmd + 13, bssid) == 0) bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid); else bss = NULL; #endif /* CONFIG_P2P */ } else if (hwaddr_aton(cmd, bssid) == 0) bss = wpa_bss_get_bssid(wpa_s, bssid); else { struct wpa_bss *tmp; i = atoi(cmd); bss = NULL; dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) { if (i == 0) { bss = tmp; break; } i--; } } if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) { mask = strtoul(ctmp + 5, NULL, 0x10); if (mask == 0) mask = WPA_BSS_MASK_ALL; } if (bss == NULL) return 0; if (bsslast == NULL) bsslast = bss; do { len = print_bss_info(wpa_s, bss, mask, buf, buflen); ret += len; buf += len; buflen -= len; if (bss == bsslast) { if ((mask & WPA_BSS_MASK_DELIM) && len && (bss == dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id))) { int res; res = os_snprintf(buf - 5, end - buf + 5, "####\n"); if (os_snprintf_error(end - buf + 5, res)) { wpa_printf(MSG_DEBUG, "Could not add end delim"); } } break; } next = bss->list_id.next; if (next == &wpa_s->bss_id) break; bss = dl_list_entry(next, struct wpa_bss, list_id); } while (bss && len); return ret; } static int wpa_supplicant_ctrl_iface_ap_scan( struct wpa_supplicant *wpa_s, char *cmd) { int ap_scan = atoi(cmd); return wpa_supplicant_set_ap_scan(wpa_s, ap_scan); } static int wpa_supplicant_ctrl_iface_scan_interval( struct wpa_supplicant *wpa_s, char *cmd) { int scan_int = atoi(cmd); return wpa_supplicant_set_scan_interval(wpa_s, scan_int); } static int wpa_supplicant_ctrl_iface_bss_expire_age( struct wpa_supplicant *wpa_s, char *cmd) { int expire_age = atoi(cmd); return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age); } static int wpa_supplicant_ctrl_iface_bss_expire_count( struct wpa_supplicant *wpa_s, char *cmd) { int expire_count = atoi(cmd); return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count); } static void wpa_supplicant_ctrl_iface_bss_flush( struct wpa_supplicant *wpa_s, char *cmd) { int flush_age = atoi(cmd); if (flush_age == 0) wpa_bss_flush(wpa_s); else wpa_bss_flush_by_age(wpa_s, flush_age); } #ifdef CONFIG_TESTING_OPTIONS static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication"); /* MLME-DELETEKEYS.request */ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); if (wpa_sm_ext_key_id(wpa_s->wpa)) wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); /* MLME-SETPROTECTION.request(None) */ wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid, MLME_SETPROTECTION_PROTECT_TYPE_NONE, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); wpa_sm_drop_sa(wpa_s->wpa); } #endif /* CONFIG_TESTING_OPTIONS */ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, char *addr) { #ifdef CONFIG_NO_SCAN_PROCESSING return -1; #else /* CONFIG_NO_SCAN_PROCESSING */ u8 bssid[ETH_ALEN]; struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_radio_work *already_connecting; if (hwaddr_aton(addr, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid " "address '%s'", addr); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid)); if (!ssid) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " "configuration known for the target AP"); return -1; } bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " "from BSS table"); return -1; } /* * TODO: Find best network configuration block from configuration to * allow roaming to other networks */ already_connecting = radio_work_pending(wpa_s, "sme-connect"); wpa_s->reassociate = 1; wpa_supplicant_connect(wpa_s, bss, ssid); /* * Indicate that an explicitly requested roam is in progress so scan * results that come in before the 'sme-connect' radio work gets * executed do not override the original connection attempt. */ if (!already_connecting && radio_work_pending(wpa_s, "sme-connect")) wpa_s->roam_in_progress = true; return 0; #endif /* CONFIG_NO_SCAN_PROCESSING */ } #ifdef CONFIG_P2P static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) { unsigned int timeout = atoi(cmd); enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; u8 dev_id[ETH_ALEN], *_dev_id = NULL; u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL; char *pos; unsigned int search_delay; const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL; u8 seek_count = 0; int freq = 0; + bool include_6ghz = false; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_INFO, "Reject P2P_FIND since interface is disabled"); return -1; } + + if (os_strstr(cmd, " include_6ghz")) + include_6ghz = true; if (os_strstr(cmd, "type=social")) type = P2P_FIND_ONLY_SOCIAL; else if (os_strstr(cmd, "type=progressive")) type = P2P_FIND_PROGRESSIVE; pos = os_strstr(cmd, "dev_id="); if (pos) { pos += 7; if (hwaddr_aton(pos, dev_id)) return -1; _dev_id = dev_id; } pos = os_strstr(cmd, "dev_type="); if (pos) { pos += 9; if (wps_dev_type_str2bin(pos, dev_type) < 0) return -1; _dev_type = dev_type; } pos = os_strstr(cmd, "delay="); if (pos) { pos += 6; search_delay = atoi(pos); } else search_delay = wpas_p2p_search_delay(wpa_s); pos = os_strstr(cmd, "freq="); if (pos) { pos += 5; freq = atoi(pos); if (freq <= 0) return -1; } /* Must be searched for last, because it adds nul termination */ pos = os_strstr(cmd, " seek="); if (pos) pos += 6; while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { char *term; _seek[seek_count++] = pos; seek = _seek; term = os_strchr(pos, ' '); if (!term) break; *term = '\0'; pos = os_strstr(term + 1, "seek="); if (pos) pos += 5; } if (seek_count > P2P_MAX_QUERY_HASH) { seek[0] = NULL; seek_count = 1; } return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, - _dev_id, search_delay, seek_count, seek, freq); + _dev_id, search_delay, seek_count, seek, freq, + include_6ghz); } static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt) { const char *last = NULL; const char *token; long int token_len; unsigned int i; /* Expected predefined CPT names delimited by ':' */ for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) { if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) { wpa_printf(MSG_ERROR, "P2PS: CPT name list is too long, expected up to %d names", P2PS_FEATURE_CAPAB_CPT_MAX); cpt[0] = 0; return -1; } token_len = last - token; if (token_len == 3 && os_memcmp(token, "UDP", token_len) == 0) { cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; } else if (token_len == 3 && os_memcmp(token, "MAC", token_len) == 0) { cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT; } else { wpa_printf(MSG_ERROR, "P2PS: Unsupported CPT name '%s'", token); cpt[0] = 0; return -1; } if (isblank((unsigned char) *last)) { i++; break; } } cpt[i] = 0; return 0; } static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) { struct p2ps_provision *p2ps_prov; char *pos; size_t info_len = 0; char *info = NULL; u8 role = P2PS_SETUP_NONE; long long unsigned val; int i; pos = os_strstr(cmd, "info="); if (pos) { pos += 5; info_len = os_strlen(pos); if (info_len) { info = os_malloc(info_len + 1); if (info) { info_len = utf8_unescape(pos, info_len, info, info_len + 1); } else info_len = 0; } } p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1); if (p2ps_prov == NULL) { os_free(info); return NULL; } if (info) { os_memcpy(p2ps_prov->info, info, info_len); p2ps_prov->info[info_len] = '\0'; os_free(info); } pos = os_strstr(cmd, "status="); if (pos) p2ps_prov->status = atoi(pos + 7); else p2ps_prov->status = -1; pos = os_strstr(cmd, "adv_id="); if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL) goto invalid_args; p2ps_prov->adv_id = val; pos = os_strstr(cmd, "method="); if (pos) p2ps_prov->method = strtol(pos + 7, NULL, 16); else p2ps_prov->method = 0; pos = os_strstr(cmd, "session="); if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL) goto invalid_args; p2ps_prov->session_id = val; pos = os_strstr(cmd, "adv_mac="); if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac)) goto invalid_args; pos = os_strstr(cmd, "session_mac="); if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac)) goto invalid_args; pos = os_strstr(cmd, "cpt="); if (pos) { if (p2ps_ctrl_parse_cpt_priority(pos + 4, p2ps_prov->cpt_priority)) goto invalid_args; } else { p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; } for (i = 0; p2ps_prov->cpt_priority[i]; i++) p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i]; /* force conncap with tstCap (no sanity checks) */ pos = os_strstr(cmd, "tstCap="); if (pos) { role = strtol(pos + 7, NULL, 16); } else { pos = os_strstr(cmd, "role="); if (pos) { role = strtol(pos + 5, NULL, 16); if (role != P2PS_SETUP_CLIENT && role != P2PS_SETUP_GROUP_OWNER) role = P2PS_SETUP_NONE; } } p2ps_prov->role = role; return p2ps_prov; invalid_args: os_free(p2ps_prov); return NULL; } static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; struct p2ps_provision *p2ps_prov; char *pos; /* id= [role=] [info=] */ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; p2ps_prov = p2p_parse_asp_provision_cmd(pos); if (!p2ps_prov) return -1; if (p2ps_prov->status < 0) { os_free(p2ps_prov); return -1; } return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; struct p2ps_provision *p2ps_prov; char *pos; /* id= adv_mac= conncap= * session= mac= [info=] */ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; p2ps_prov = p2p_parse_asp_provision_cmd(pos); if (!p2ps_prov) return -1; p2ps_prov->pd_seeker = 1; return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } static int parse_freq(int chwidth, int freq2) { if (freq2 < 0) return -1; if (freq2) return CHANWIDTH_80P80MHZ; switch (chwidth) { case 0: case 20: case 40: return CHANWIDTH_USE_HT; case 80: return CHANWIDTH_80MHZ; case 160: return CHANWIDTH_160MHZ; default: wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d", chwidth); return -1; } } static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; char *pos, *pos2; char *pin = NULL; enum p2p_wps_method wps_method; int new_pin; int ret; int persistent_group, persistent_id = -1; int join; int auth; int automatic; int go_intent = -1; int freq = 0; int pd; int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; int edmg; u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL; size_t group_ssid_len = 0; int he; + bool allow_6ghz; if (!wpa_s->global->p2p_init_wpa_s) return -1; if (wpa_s->global->p2p_init_wpa_s != wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s", wpa_s->global->p2p_init_wpa_s->ifname); wpa_s = wpa_s->global->p2p_init_wpa_s; } /* <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=] * [join] [auth] [go_intent=<0..15>] [freq=] [provdisc] * [ht40] [vht] [he] [edmg] [auto] [ssid=] */ if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; pos++; persistent_group = os_strstr(pos, " persistent") != NULL; pos2 = os_strstr(pos, " persistent="); if (pos2) { struct wpa_ssid *ssid; persistent_id = atoi(pos2 + 12); ssid = wpa_config_get_network(wpa_s->conf, persistent_id); if (ssid == NULL || ssid->disabled != 2 || ssid->mode != WPAS_MODE_P2P_GO) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "SSID id=%d for persistent P2P group (GO)", persistent_id); return -1; } } join = os_strstr(pos, " join") != NULL; + allow_6ghz = os_strstr(pos, " allow_6ghz") != NULL; auth = os_strstr(pos, " auth") != NULL; automatic = os_strstr(pos, " auto") != NULL; pd = os_strstr(pos, " provdisc") != NULL; vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he; edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg; pos2 = os_strstr(pos, " go_intent="); if (pos2) { pos2 += 11; go_intent = atoi(pos2); if (go_intent < 0 || go_intent > 15) return -1; } pos2 = os_strstr(pos, " freq="); if (pos2) { pos2 += 6; freq = atoi(pos2); if (freq <= 0) return -1; } pos2 = os_strstr(pos, " freq2="); if (pos2) freq2 = atoi(pos2 + 7); pos2 = os_strstr(pos, " max_oper_chwidth="); if (pos2) chwidth = atoi(pos2 + 18); max_oper_chwidth = parse_freq(chwidth, freq2); if (max_oper_chwidth < 0) return -1; pos2 = os_strstr(pos, " ssid="); if (pos2) { char *end; pos2 += 6; end = os_strchr(pos2, ' '); if (!end) group_ssid_len = os_strlen(pos2) / 2; else group_ssid_len = (end - pos2) / 2; if (group_ssid_len == 0 || group_ssid_len > SSID_MAX_LEN || hexstr2bin(pos2, _group_ssid, group_ssid_len) < 0) return -1; group_ssid = _group_ssid; } if (os_strncmp(pos, "pin", 3) == 0) { /* Request random PIN (to be displayed) and enable the PIN */ wps_method = WPS_PIN_DISPLAY; } else if (os_strncmp(pos, "pbc", 3) == 0) { wps_method = WPS_PBC; } else if (os_strstr(pos, "p2ps") != NULL) { wps_method = WPS_P2PS; } else { pin = pos; pos = os_strchr(pin, ' '); wps_method = WPS_PIN_KEYPAD; if (pos) { *pos++ = '\0'; if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; } if (!wps_pin_str_valid(pin)) { os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); return 17; } } new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, auth, go_intent, freq, freq2, persistent_id, pd, ht40, vht, max_oper_chwidth, he, edmg, - group_ssid, group_ssid_len); + group_ssid, group_ssid_len, allow_6ghz); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; } if (new_pin == -3) { os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25); return 25; } if (new_pin < 0) return -1; if (wps_method == WPS_PIN_DISPLAY && pin == NULL) { ret = os_snprintf(buf, buflen, "%08d", new_pin); if (os_snprintf_error(buflen, ret)) return -1; return ret; } os_memcpy(buf, "OK\n", 3); return 3; } static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd) { unsigned int timeout = atoi(cmd); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_INFO, "Reject P2P_LISTEN since interface is disabled"); return -1; } return wpas_p2p_listen(wpa_s, timeout); } static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; char *pos; enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG; /* [join|auto] */ if (hwaddr_aton(cmd, addr)) return -1; pos = cmd + 17; if (*pos != ' ') return -1; pos++; if (os_strstr(pos, " join") != NULL) use = WPAS_P2P_PD_FOR_JOIN; else if (os_strstr(pos, " auto") != NULL) use = WPAS_P2P_PD_AUTO; return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL); } static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || ssid->passphrase == NULL) return -1; os_strlcpy(buf, ssid->passphrase, buflen); return os_strlen(buf); } static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u64 ref; int res; u8 dst_buf[ETH_ALEN], *dst; struct wpabuf *tlvs; char *pos; size_t len; if (hwaddr_aton(cmd, dst_buf)) return -1; dst = dst_buf; if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && dst[3] == 0 && dst[4] == 0 && dst[5] == 0) dst = NULL; pos = cmd + 17; if (*pos != ' ') return -1; pos++; if (os_strncmp(pos, "upnp ", 5) == 0) { u8 version; pos += 5; if (hexstr2bin(pos, &version, 1) < 0) return -1; pos += 2; if (*pos != ' ') return -1; pos++; ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(pos, "wifi-display ", 13) == 0) { ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13); #endif /* CONFIG_WIFI_DISPLAY */ } else if (os_strncmp(pos, "asp ", 4) == 0) { char *svc_str; char *svc_info = NULL; u32 id; pos += 4; if (sscanf(pos, "%x", &id) != 1 || id > 0xff) return -1; pos = os_strchr(pos, ' '); if (pos == NULL || pos[1] == '\0' || pos[1] == ' ') return -1; svc_str = pos + 1; pos = os_strchr(svc_str, ' '); if (pos) *pos++ = '\0'; /* All remaining data is the svc_info string */ if (pos && pos[0] && pos[0] != ' ') { len = os_strlen(pos); /* Unescape in place */ len = utf8_unescape(pos, len, pos, len); if (len > 0xff) return -1; svc_info = pos; } ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id, svc_str, svc_info); } else { len = os_strlen(pos); if (len & 1) return -1; len /= 2; tlvs = wpabuf_alloc(len); if (tlvs == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) { wpabuf_free(tlvs); return -1; } ref = wpas_p2p_sd_request(wpa_s, dst, tlvs); wpabuf_free(tlvs); } if (ref == 0) return -1; res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); if (os_snprintf_error(buflen, res)) return -1; return res; } static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s, char *cmd) { long long unsigned val; u64 req; if (sscanf(cmd, "%llx", &val) != 1) return -1; req = val; return wpas_p2p_sd_cancel_request(wpa_s, req); } static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd) { int freq; u8 dst[ETH_ALEN]; u8 dialog_token; struct wpabuf *resp_tlvs; char *pos, *pos2; size_t len; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; freq = atoi(cmd); if (freq == 0) return -1; if (hwaddr_aton(pos, dst)) return -1; pos += 17; if (*pos != ' ') return -1; pos++; pos2 = os_strchr(pos, ' '); if (pos2 == NULL) return -1; *pos2++ = '\0'; dialog_token = atoi(pos); len = os_strlen(pos2); if (len & 1) return -1; len /= 2; resp_tlvs = wpabuf_alloc(len); if (resp_tlvs == NULL) return -1; if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) { wpabuf_free(resp_tlvs); return -1; } wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs); wpabuf_free(resp_tlvs); return 0; } static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s, char *cmd) { if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1")) return -1; wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd); return 0; } static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; size_t len; struct wpabuf *query, *resp; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; len = os_strlen(cmd); if (len & 1) return -1; len /= 2; query = wpabuf_alloc(len); if (query == NULL) return -1; if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { wpabuf_free(query); return -1; } len = os_strlen(pos); if (len & 1) { wpabuf_free(query); return -1; } len /= 2; resp = wpabuf_alloc(len); if (resp == NULL) { wpabuf_free(query); return -1; } if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) { wpabuf_free(query); wpabuf_free(resp); return -1; } if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) { wpabuf_free(query); wpabuf_free(resp); return -1; } return 0; } static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 version; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (hexstr2bin(cmd, &version, 1) < 0) return -1; return wpas_p2p_service_add_upnp(wpa_s, version, pos); } static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, u8 replace, char *cmd) { char *pos; char *adv_str; u32 auto_accept, adv_id, svc_state, config_methods; char *svc_info = NULL; char *cpt_prio_str; u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; /* Auto-Accept value is mandatory, and must be one of the * single values (0, 1, 2, 4) */ auto_accept = atoi(cmd); switch (auto_accept) { case P2PS_SETUP_NONE: /* No auto-accept */ case P2PS_SETUP_NEW: case P2PS_SETUP_CLIENT: case P2PS_SETUP_GROUP_OWNER: break; default: return -1; } /* Advertisement ID is mandatory */ cmd = pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */ if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0) return -1; /* Only allow replacements if exist, and adds if not */ if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) { if (!replace) return -1; } else { if (replace) return -1; } /* svc_state between 0 - 0xff is mandatory */ if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff) return -1; pos = os_strchr(pos, ' '); if (pos == NULL) return -1; /* config_methods is mandatory */ pos++; if (sscanf(pos, "%x", &config_methods) != 1) return -1; if (!(config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS))) return -1; pos = os_strchr(pos, ' '); if (pos == NULL) return -1; pos++; adv_str = pos; /* Advertisement string is mandatory */ if (!pos[0] || pos[0] == ' ') return -1; /* Terminate svc string */ pos = os_strchr(pos, ' '); if (pos != NULL) *pos++ = '\0'; cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL; if (cpt_prio_str) { pos = os_strchr(pos, ' '); if (pos != NULL) *pos++ = '\0'; if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio)) return -1; } else { cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; cpt_prio[1] = 0; } /* Service and Response Information are optional */ if (pos && pos[0]) { size_t len; /* Note the bare ' included, which cannot exist legally * in unescaped string. */ svc_info = os_strstr(pos, "svc_info='"); if (svc_info) { svc_info += 9; len = os_strlen(svc_info); utf8_unescape(svc_info, len, svc_info, len); } } return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str, (u8) svc_state, (u16) config_methods, svc_info, cpt_prio); } static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "bonjour") == 0) return p2p_ctrl_service_add_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_add_upnp(wpa_s, pos); if (os_strcmp(cmd, "asp") == 0) return p2p_ctrl_service_add_asp(wpa_s, 0, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s, char *cmd) { size_t len; struct wpabuf *query; int ret; len = os_strlen(cmd); if (len & 1) return -1; len /= 2; query = wpabuf_alloc(len); if (query == NULL) return -1; if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { wpabuf_free(query); return -1; } ret = wpas_p2p_service_del_bonjour(wpa_s, query); wpabuf_free(query); return ret; } static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 version; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (hexstr2bin(cmd, &version, 1) < 0) return -1; return wpas_p2p_service_del_upnp(wpa_s, version, pos); } static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd) { u32 adv_id; if (os_strcmp(cmd, "all") == 0) { wpas_p2p_service_flush_asp(wpa_s); return 0; } if (sscanf(cmd, "%x", &adv_id) != 1) return -1; return wpas_p2p_service_del_asp(wpa_s, adv_id); } static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "bonjour") == 0) return p2p_ctrl_service_del_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_del_upnp(wpa_s, pos); if (os_strcmp(cmd, "asp") == 0) return p2p_ctrl_service_del_asp(wpa_s, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; if (os_strcmp(cmd, "asp") == 0) return p2p_ctrl_service_add_asp(wpa_s, 1, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; /* */ if (hwaddr_aton(cmd, addr)) return -1; return wpas_p2p_reject(wpa_s, addr); } static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; int id; struct wpa_ssid *ssid; u8 *_peer = NULL, peer[ETH_ALEN]; int freq = 0, pref_freq = 0; int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0; int edmg; + bool allow_6ghz; id = atoi(cmd); pos = os_strstr(cmd, " peer="); if (pos) { pos += 6; if (hwaddr_aton(pos, peer)) return -1; _peer = peer; } ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "for persistent P2P group", id); return -1; } pos = os_strstr(cmd, " freq="); if (pos) { pos += 6; freq = atoi(pos); if (freq <= 0) return -1; } pos = os_strstr(cmd, " pref="); if (pos) { pos += 6; pref_freq = atoi(pos); if (pref_freq <= 0) return -1; } vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he; edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg; pos = os_strstr(cmd, "freq2="); if (pos) freq2 = atoi(pos + 6); pos = os_strstr(cmd, " max_oper_chwidth="); if (pos) chwidth = atoi(pos + 18); max_oper_chwidth = parse_freq(chwidth, freq2); if (max_oper_chwidth < 0) return -1; + allow_6ghz = os_strstr(cmd, " allow_6ghz") != NULL; + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht, - max_oper_chwidth, pref_freq, he, edmg); + max_oper_chwidth, pref_freq, he, edmg, + allow_6ghz); } static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL; + bool allow_6ghz; pos = os_strstr(cmd, " peer="); if (!pos) return -1; *pos = '\0'; pos += 6; if (hwaddr_aton(pos, peer)) { wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos); return -1; } + allow_6ghz = os_strstr(pos, " allow_6ghz") != NULL; + pos = os_strstr(pos, " go_dev_addr="); if (pos) { pos += 13; if (hwaddr_aton(pos, go_dev_addr)) { wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos); return -1; } go_dev = go_dev_addr; } - return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev); + return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev, allow_6ghz); } static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) { if (os_strncmp(cmd, "persistent=", 11) == 0) return p2p_ctrl_invite_persistent(wpa_s, cmd + 11); if (os_strncmp(cmd, "group=", 6) == 0) return p2p_ctrl_invite_group(wpa_s, cmd + 6); return -1; } static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, int id, int freq, int vht_center_freq2, int ht40, int vht, int vht_chwidth, - int he, int edmg) + int he, int edmg, bool allow_6ghz) { struct wpa_ssid *ssid; ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " "for persistent P2P group", id); return -1; } return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, vht_center_freq2, 0, ht40, vht, vht_chwidth, he, edmg, - NULL, 0, 0); + NULL, 0, 0, allow_6ghz); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { int freq = 0, persistent = 0, group_id = -1; + bool allow_6ghz = false; int vht = wpa_s->conf->p2p_go_vht; int ht40 = wpa_s->conf->p2p_go_ht40 || vht; int he = wpa_s->conf->p2p_go_he; int edmg = wpa_s->conf->p2p_go_edmg; int max_oper_chwidth, chwidth = 0, freq2 = 0; char *token, *context = NULL; #ifdef CONFIG_ACS int acs = 0; #endif /* CONFIG_ACS */ while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "freq2=%d", &freq2) == 1 || sscanf(token, "persistent=%d", &group_id) == 1 || sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) { continue; #ifdef CONFIG_ACS } else if (os_strcmp(token, "freq=acs") == 0) { acs = 1; #endif /* CONFIG_ACS */ } else if (sscanf(token, "freq=%d", &freq) == 1) { continue; } else if (os_strcmp(token, "ht40") == 0) { ht40 = 1; } else if (os_strcmp(token, "vht") == 0) { vht = 1; ht40 = 1; } else if (os_strcmp(token, "he") == 0) { he = 1; } else if (os_strcmp(token, "edmg") == 0) { edmg = 1; } else if (os_strcmp(token, "persistent") == 0) { persistent = 1; + } else if (os_strcmp(token, "allow_6ghz") == 0) { + allow_6ghz = true; } else { wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'", token); return -1; } } #ifdef CONFIG_ACS if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && (acs || freq == 2 || freq == 5)) { if (freq == 2 && wpa_s->best_24_freq <= 0) { wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211G; wpa_s->p2p_go_do_acs = 1; freq = 0; } else if (freq == 5 && wpa_s->best_5_freq <= 0) { wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211A; wpa_s->p2p_go_do_acs = 1; freq = 0; } else { wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY; wpa_s->p2p_go_do_acs = 1; } } else { wpa_s->p2p_go_do_acs = 0; } #endif /* CONFIG_ACS */ max_oper_chwidth = parse_freq(chwidth, freq2); if (max_oper_chwidth < 0) return -1; if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, freq, freq2, ht40, vht, max_oper_chwidth, he, - edmg); + edmg, allow_6ghz); return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht, - max_oper_chwidth, he, edmg); + max_oper_chwidth, he, edmg, allow_6ghz); } static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) { u8 dev_addr[ETH_ALEN]; struct wpa_ssid *ssid; int res; const u8 *iaddr; ssid = wpa_s->current_ssid; if (!wpa_s->global->p2p || !ssid || ssid->mode != WPAS_MODE_P2P_GO || hwaddr_aton(cmd, dev_addr)) return -1; iaddr = p2p_group_get_client_interface_addr(wpa_s->p2p_group, dev_addr); if (!iaddr) return -1; res = os_snprintf(buf, buflen, MACSTR, MAC2STR(iaddr)); if (os_snprintf_error(buflen, res)) return -1; return res; } static int wpas_find_p2p_dev_addr_bss(struct wpa_global *global, const u8 *p2p_dev_addr) { struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_bss_get_p2p_dev_addr(wpa_s, p2p_dev_addr)) return 1; } return 0; } static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 addr[ETH_ALEN], *addr_ptr, group_capab; int next, res; const struct p2p_peer_info *info; char *pos, *end; char devtype[WPS_DEV_TYPE_BUFSIZE]; struct wpa_ssid *ssid; size_t i; if (!wpa_s->global->p2p) return -1; if (os_strcmp(cmd, "FIRST") == 0) { addr_ptr = NULL; next = 0; } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { if (hwaddr_aton(cmd + 5, addr) < 0) return -1; addr_ptr = addr; next = 1; } else { if (hwaddr_aton(cmd, addr) < 0) return -1; addr_ptr = addr; next = 0; } info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next); if (info == NULL) return -1; group_capab = info->group_capab; if (group_capab && !wpas_find_p2p_dev_addr_bss(wpa_s->global, info->p2p_device_addr)) { wpa_printf(MSG_DEBUG, "P2P: Could not find any BSS with p2p_dev_addr " MACSTR ", hence override group_capab from 0x%x to 0", MAC2STR(info->p2p_device_addr), group_capab); group_capab = 0; } pos = buf; end = buf + buflen; res = os_snprintf(pos, end - pos, MACSTR "\n" "pri_dev_type=%s\n" "device_name=%s\n" "manufacturer=%s\n" "model_name=%s\n" "model_number=%s\n" "serial_number=%s\n" "config_methods=0x%x\n" "dev_capab=0x%x\n" "group_capab=0x%x\n" "level=%d\n", MAC2STR(info->p2p_device_addr), wps_dev_type_bin2str(info->pri_dev_type, devtype, sizeof(devtype)), info->device_name, info->manufacturer, info->model_name, info->model_number, info->serial_number, info->config_methods, info->dev_capab, group_capab, info->level); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++) { const u8 *t; t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN]; res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n", wps_dev_type_bin2str(t, devtype, sizeof(devtype))); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0); if (ssid) { res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } res = p2p_get_peer_info_txt(info, pos, end - pos); if (res < 0) return pos - buf; pos += res; if (info->vendor_elems) { res = os_snprintf(pos, end - pos, "vendor_elems="); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(info->vendor_elems), wpabuf_len(info->vendor_elems)); res = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } return pos - buf; } static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, const char *param) { unsigned int i; if (wpa_s->global->p2p == NULL) return -1; if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0) return -1; for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) { struct wpa_freq_range *freq; freq = &wpa_s->global->p2p_disallow_freq.range[i]; wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u", freq->min, freq->max); } wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW); return 0; } static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) { char *param; if (wpa_s->global->p2p == NULL) return -1; param = os_strchr(cmd, ' '); if (param == NULL) return -1; *param++ = '\0'; if (os_strcmp(cmd, "discoverability") == 0) { p2p_set_client_discoverability(wpa_s->global->p2p, atoi(param)); return 0; } if (os_strcmp(cmd, "managed") == 0) { p2p_set_managed_oper(wpa_s->global->p2p, atoi(param)); return 0; } if (os_strcmp(cmd, "listen_channel") == 0) { char *pos; u8 channel, op_class; channel = atoi(param); pos = os_strchr(param, ' '); op_class = pos ? atoi(pos) : 81; return p2p_set_listen_channel(wpa_s->global->p2p, op_class, channel, 1); } if (os_strcmp(cmd, "ssid_postfix") == 0) { return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param, os_strlen(param)); } if (os_strcmp(cmd, "noa") == 0) { char *pos; int count, start, duration; /* GO NoA parameters: count,start_offset(ms),duration(ms) */ count = atoi(param); pos = os_strchr(param, ','); if (pos == NULL) return -1; pos++; start = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; duration = atoi(pos); if (count < 0 || count > 255 || start < 0 || duration < 0) return -1; if (count == 0 && duration > 0) return -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d " "start=%d duration=%d", count, start, duration); return wpas_p2p_set_noa(wpa_s, count, start, duration); } if (os_strcmp(cmd, "ps") == 0) return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1); if (os_strcmp(cmd, "oppps") == 0) return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1); if (os_strcmp(cmd, "ctwindow") == 0) return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param)); if (os_strcmp(cmd, "disabled") == 0) { wpa_s->global->p2p_disabled = atoi(param); wpa_printf(MSG_DEBUG, "P2P functionality %s", wpa_s->global->p2p_disabled ? "disabled" : "enabled"); if (wpa_s->global->p2p_disabled) { wpas_p2p_stop_find(wpa_s); os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); p2p_flush(wpa_s->global->p2p); } return 0; } if (os_strcmp(cmd, "conc_pref") == 0) { if (os_strcmp(param, "sta") == 0) wpa_s->global->conc_pref = WPA_CONC_PREF_STA; else if (os_strcmp(param, "p2p") == 0) wpa_s->global->conc_pref = WPA_CONC_PREF_P2P; else { wpa_printf(MSG_INFO, "Invalid conc_pref value"); return -1; } wpa_printf(MSG_DEBUG, "Single channel concurrency preference: " "%s", param); return 0; } if (os_strcmp(cmd, "force_long_sd") == 0) { wpa_s->force_long_sd = atoi(param); return 0; } if (os_strcmp(cmd, "peer_filter") == 0) { u8 addr[ETH_ALEN]; if (hwaddr_aton(param, addr)) return -1; p2p_set_peer_filter(wpa_s->global->p2p, addr); return 0; } if (os_strcmp(cmd, "cross_connect") == 0) return wpas_p2p_set_cross_connect(wpa_s, atoi(param)); if (os_strcmp(cmd, "go_apsd") == 0) { if (os_strcmp(param, "disable") == 0) wpa_s->set_ap_uapsd = 0; else { wpa_s->set_ap_uapsd = 1; wpa_s->ap_uapsd = atoi(param); } return 0; } if (os_strcmp(cmd, "client_apsd") == 0) { if (os_strcmp(param, "disable") == 0) wpa_s->set_sta_uapsd = 0; else { int be, bk, vi, vo; char *pos; /* format: BE,BK,VI,VO;max SP Length */ be = atoi(param); pos = os_strchr(param, ','); if (pos == NULL) return -1; pos++; bk = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vi = atoi(pos); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; vo = atoi(pos); /* ignore max SP Length for now */ wpa_s->set_sta_uapsd = 1; wpa_s->sta_uapsd = 0; if (be) wpa_s->sta_uapsd |= BIT(0); if (bk) wpa_s->sta_uapsd |= BIT(1); if (vi) wpa_s->sta_uapsd |= BIT(2); if (vo) wpa_s->sta_uapsd |= BIT(3); } return 0; } if (os_strcmp(cmd, "disallow_freq") == 0) return p2p_ctrl_disallow_freq(wpa_s, param); if (os_strcmp(cmd, "disc_int") == 0) { int min_disc_int, max_disc_int, max_disc_tu; char *pos; pos = param; min_disc_int = atoi(pos); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; *pos++ = '\0'; max_disc_int = atoi(pos); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; *pos++ = '\0'; max_disc_tu = atoi(pos); return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int, max_disc_int, max_disc_tu); } if (os_strcmp(cmd, "per_sta_psk") == 0) { wpa_s->global->p2p_per_sta_psk = !!atoi(param); return 0; } #ifdef CONFIG_WPS_NFC if (os_strcmp(cmd, "nfc_tag") == 0) return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param)); #endif /* CONFIG_WPS_NFC */ if (os_strcmp(cmd, "disable_ip_addr_req") == 0) { wpa_s->p2p_disable_ip_addr_req = !!atoi(param); return 0; } if (os_strcmp(cmd, "override_pref_op_chan") == 0) { int op_class, chan; op_class = atoi(param); param = os_strchr(param, ':'); if (!param) return -1; param++; chan = atoi(param); p2p_set_override_pref_op_chan(wpa_s->global->p2p, op_class, chan); return 0; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); return -1; } static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) { os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; #ifdef CONFIG_TESTING_OPTIONS os_free(wpa_s->get_pref_freq_list_override); wpa_s->get_pref_freq_list_override = NULL; #endif /* CONFIG_TESTING_OPTIONS */ wpas_p2p_stop_find(wpa_s); wpa_s->parent->p2ps_method_config_any = 0; if (wpa_s->global->p2p) p2p_flush(wpa_s->global->p2p); } static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *pos2; unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0; if (cmd[0]) { pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; dur1 = atoi(cmd); pos2 = os_strchr(pos, ' '); if (pos2) *pos2++ = '\0'; int1 = atoi(pos); } else pos2 = NULL; if (pos2) { pos = os_strchr(pos2, ' '); if (pos == NULL) return -1; *pos++ = '\0'; dur2 = atoi(pos2); int2 = atoi(pos); } return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2); } static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; unsigned int period = 0, interval = 0; if (cmd[0]) { pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; period = atoi(cmd); interval = atoi(pos); } return wpas_p2p_ext_listen(wpa_s, period, interval); } static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) { const char *pos; u8 peer[ETH_ALEN]; int iface_addr = 0; pos = cmd; if (os_strncmp(pos, "iface=", 6) == 0) { iface_addr = 1; pos += 6; } if (hwaddr_aton(pos, peer)) return -1; wpas_p2p_remove_client(wpa_s, peer, iface_addr); return 0; } static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd) { int freq = 0, period = 0, interval = 0, count = 0; if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4) { wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P LO Start parameter: '%s'", cmd); return -1; } return wpas_p2p_lo_start(wpa_s, freq, period, interval, count); } #endif /* CONFIG_P2P */ static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val) { struct wpa_freq_range_list ranges; int *freqs = NULL; struct hostapd_hw_modes *mode; u16 i; if (wpa_s->hw.modes == NULL) return NULL; os_memset(&ranges, 0, sizeof(ranges)); if (freq_range_list_parse(&ranges, val) < 0) return NULL; for (i = 0; i < wpa_s->hw.num_modes; i++) { int j; mode = &wpa_s->hw.modes[i]; for (j = 0; j < mode->num_channels; j++) { unsigned int freq; if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; freq = mode->channels[j].freq; if (!freq_range_list_includes(&ranges, freq)) continue; int_array_add_unique(&freqs, freq); } } os_free(ranges.range); return freqs; } #ifdef CONFIG_INTERWORKING static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param) { int auto_sel = 0; int *freqs = NULL; if (param) { char *pos; auto_sel = os_strstr(param, "auto") != NULL; pos = os_strstr(param, "freq="); if (pos) { freqs = freq_range_to_channel_list(wpa_s, pos + 5); if (freqs == NULL) return -1; } } return interworking_select(wpa_s, auto_sel, freqs); } static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst, int only_add) { u8 bssid[ETH_ALEN]; struct wpa_bss *bss; if (hwaddr_aton(dst, bssid)) { wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst); return -1; } bss = wpa_bss_get_bssid_latest(wpa_s, bssid); if (bss == NULL) { wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR, MAC2STR(bssid)); return -1; } if (bss->ssid_len == 0) { int found = 0; wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR " does not have SSID information", MAC2STR(bssid)); dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && bss->ssid_len > 0) { found = 1; break; } } if (!found) return -1; wpa_printf(MSG_DEBUG, "Found another matching BSS entry with SSID"); } return interworking_connect(wpa_s, bss, only_add); } static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) { u8 dst_addr[ETH_ALEN]; int used, freq = 0; char *pos; #define MAX_ANQP_INFO_ID 100 u16 id[MAX_ANQP_INFO_ID]; size_t num_id = 0; u32 subtypes = 0; u32 mbo_subtypes = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; pos = dst + used; if (*pos == ' ') pos++; if (os_strncmp(pos, "freq=", 5) == 0) { freq = atoi(pos + 5); pos = os_strchr(pos, ' '); if (!pos) return -1; pos++; } while (num_id < MAX_ANQP_INFO_ID) { if (os_strncmp(pos, "hs20:", 5) == 0) { #ifdef CONFIG_HS20 int num = atoi(pos + 5); if (num <= 0 || num > 31) return -1; subtypes |= BIT(num); #else /* CONFIG_HS20 */ return -1; #endif /* CONFIG_HS20 */ } else if (os_strncmp(pos, "mbo:", 4) == 0) { #ifdef CONFIG_MBO int num = atoi(pos + 4); if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) return -1; mbo_subtypes |= BIT(num); #else /* CONFIG_MBO */ return -1; #endif /* CONFIG_MBO */ } else { id[num_id] = atoi(pos); if (id[num_id]) num_id++; } pos = os_strchr(pos + 1, ','); if (pos == NULL) break; pos++; } if (num_id == 0 && !subtypes && !mbo_subtypes) return -1; return anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes, mbo_subtypes); } static int gas_request(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst_addr[ETH_ALEN]; struct wpabuf *advproto, *query = NULL; int used, ret = -1; char *pos, *end; size_t len; used = hwaddr_aton2(cmd, dst_addr); if (used < 0) return -1; pos = cmd + used; while (*pos == ' ') pos++; /* Advertisement Protocol ID */ end = os_strchr(pos, ' '); if (end) len = end - pos; else len = os_strlen(pos); if (len & 0x01) return -1; len /= 2; if (len == 0) return -1; advproto = wpabuf_alloc(len); if (advproto == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0) goto fail; if (end) { /* Optional Query Request */ pos = end + 1; while (*pos == ' ') pos++; len = os_strlen(pos); if (len) { if (len & 0x01) goto fail; len /= 2; if (len == 0) goto fail; query = wpabuf_alloc(len); if (query == NULL) goto fail; if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0) goto fail; } } ret = gas_send_request(wpa_s, dst_addr, advproto, query); fail: wpabuf_free(advproto); wpabuf_free(query); return ret; } static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; int dialog_token; int used; char *pos; size_t resp_len, start, requested_len; struct wpabuf *resp; int ret; used = hwaddr_aton2(cmd, addr); if (used < 0) return -1; pos = cmd + used; while (*pos == ' ') pos++; dialog_token = atoi(pos); if (wpa_s->last_gas_resp && os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 && dialog_token == wpa_s->last_gas_dialog_token) resp = wpa_s->last_gas_resp; else if (wpa_s->prev_gas_resp && os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 && dialog_token == wpa_s->prev_gas_dialog_token) resp = wpa_s->prev_gas_resp; else return -1; resp_len = wpabuf_len(resp); start = 0; requested_len = resp_len; pos = os_strchr(pos, ' '); if (pos) { start = atoi(pos); if (start > resp_len) return os_snprintf(buf, buflen, "FAIL-Invalid range"); pos = os_strchr(pos, ','); if (pos == NULL) return -1; pos++; requested_len = atoi(pos); if (start + requested_len > resp_len) return os_snprintf(buf, buflen, "FAIL-Invalid range"); } if (requested_len * 2 + 1 > buflen) return os_snprintf(buf, buflen, "FAIL-Too long response"); ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start, requested_len); if (start + requested_len == resp_len) { /* * Free memory by dropping the response after it has been * fetched. */ if (resp == wpa_s->prev_gas_resp) { wpabuf_free(wpa_s->prev_gas_resp); wpa_s->prev_gas_resp = NULL; } else { wpabuf_free(wpa_s->last_gas_resp); wpa_s->last_gas_resp = NULL; } } return ret; } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) { u8 dst_addr[ETH_ALEN]; int used; char *pos; u32 subtypes = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; pos = dst + used; if (*pos == ' ') pos++; for (;;) { int num = atoi(pos); if (num <= 0 || num > 31) return -1; subtypes |= BIT(num); pos = os_strchr(pos + 1, ','); if (pos == NULL) break; pos++; } if (subtypes == 0) return -1; return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0); } static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, const u8 *addr, const char *realm) { u8 *buf; size_t rlen, len; int ret; rlen = os_strlen(realm); len = 3 + rlen; buf = os_malloc(len); if (buf == NULL) return -1; buf[0] = 1; /* NAI Home Realm Count */ buf[1] = 0; /* Formatted in accordance with RFC 4282 */ buf[2] = rlen; os_memcpy(buf + 3, realm, rlen); ret = hs20_anqp_send_req(wpa_s, addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), buf, len, 0); os_free(buf); return ret; } static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, char *dst) { struct wpa_cred *cred = wpa_s->conf->cred; u8 dst_addr[ETH_ALEN]; int used; u8 *buf; size_t len; int ret; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; while (dst[used] == ' ') used++; if (os_strncmp(dst + used, "realm=", 6) == 0) return hs20_nai_home_realm_list(wpa_s, dst_addr, dst + used + 6); len = os_strlen(dst + used); if (len == 0 && cred && cred->realm) return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(dst + used, buf, len) < 0) { os_free(buf); return -1; } ret = hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), buf, len, 0); os_free(buf); return ret; } static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply, int buflen) { u8 dst_addr[ETH_ALEN]; int used; char *ctx = NULL, *icon, *poffset, *psize; used = hwaddr_aton2(cmd, dst_addr); if (used < 0) return -1; cmd += used; icon = str_token(cmd, " ", &ctx); poffset = str_token(cmd, " ", &ctx); psize = str_token(cmd, " ", &ctx); if (!icon || !poffset || !psize) return -1; wpa_s->fetch_osu_icon_in_progress = 0; return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize), reply, buflen); } static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst_addr[ETH_ALEN]; int used; char *icon; if (!cmd[0]) return hs20_del_icon(wpa_s, NULL, NULL); used = hwaddr_aton2(cmd, dst_addr); if (used < 0) return -1; while (cmd[used] == ' ') used++; icon = cmd[used] ? &cmd[used] : NULL; return hs20_del_icon(wpa_s, dst_addr, icon); } static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem) { u8 dst_addr[ETH_ALEN]; int used; char *icon; used = hwaddr_aton2(cmd, dst_addr); if (used < 0) return -1; while (cmd[used] == ' ') used++; icon = &cmd[used]; wpa_s->fetch_osu_icon_in_progress = 0; return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST), (u8 *) icon, os_strlen(icon), inmem); } #endif /* CONFIG_HS20 */ #ifdef CONFIG_AUTOSCAN static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s, char *cmd) { enum wpa_states state = wpa_s->wpa_state; char *new_params = NULL; if (os_strlen(cmd) > 0) { new_params = os_strdup(cmd); if (new_params == NULL) return -1; } os_free(wpa_s->conf->autoscan); wpa_s->conf->autoscan = new_params; if (wpa_s->conf->autoscan == NULL) autoscan_deinit(wpa_s); else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) autoscan_init(wpa_s, 1); else if (state == WPA_SCANNING) wpa_supplicant_reinit_autoscan(wpa_s); else wpa_printf(MSG_DEBUG, "No autoscan update in state %s", wpa_supplicant_state_txt(state)); return 0; } #endif /* CONFIG_AUTOSCAN */ #ifdef CONFIG_WNM static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) { int enter; int intval = 0; char *pos; int ret; struct wpabuf *tfs_req = NULL; if (os_strncmp(cmd, "enter", 5) == 0) enter = 1; else if (os_strncmp(cmd, "exit", 4) == 0) enter = 0; else return -1; pos = os_strstr(cmd, " interval="); if (pos) intval = atoi(pos + 10); pos = os_strstr(cmd, " tfs_req="); if (pos) { char *end; size_t len; pos += 9; end = os_strchr(pos, ' '); if (end) len = end - pos; else len = os_strlen(pos); if (len & 1) return -1; len /= 2; tfs_req = wpabuf_alloc(len); if (tfs_req == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) { wpabuf_free(tfs_req); return -1; } } ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER : WNM_SLEEP_MODE_EXIT, intval, tfs_req); wpabuf_free(tfs_req); return ret; } static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) { int query_reason, list = 0; char *btm_candidates = NULL; query_reason = atoi(cmd); cmd = os_strchr(cmd, ' '); if (cmd) { if (os_strncmp(cmd, " list", 5) == 0) list = 1; else btm_candidates = cmd; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s", query_reason, list ? " candidate list" : ""); return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, btm_candidates, list); } static int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s, char *cmd) { struct wpabuf *elems; int ret; elems = wpabuf_parse_bin(cmd); if (!elems) return -1; ret = wnm_send_coloc_intf_report(wpa_s, 0, elems); wpabuf_free(elems); return ret; } #endif /* CONFIG_WNM */ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_signal_info si; int ret; char *pos, *end; ret = wpa_drv_signal_poll(wpa_s, &si); if (ret) return -1; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n" "NOISE=%d\nFREQUENCY=%u\n", si.current_signal, si.current_txrate / 1000, si.current_noise, si.frequency); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", channel_width_to_string(si.chanwidth)); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.center_frq1 > 0) { ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n", si.center_frq1); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.center_frq2 > 0) { ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n", si.center_frq2); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.avg_signal) { ret = os_snprintf(pos, end - pos, "AVG_RSSI=%d\n", si.avg_signal); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (si.avg_beacon_signal) { ret = os_snprintf(pos, end - pos, "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } return pos - buf; } static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s, const char *cmd) { const char *pos; int threshold = 0; int hysteresis = 0; if (wpa_s->bgscan && wpa_s->bgscan_priv) { wpa_printf(MSG_DEBUG, "Reject SIGNAL_MONITOR command - bgscan is active"); return -1; } pos = os_strstr(cmd, "THRESHOLD="); if (pos) threshold = atoi(pos + 10); pos = os_strstr(cmd, "HYSTERESIS="); if (pos) hysteresis = atoi(pos + 11); return wpa_drv_signal_monitor(wpa_s, threshold, hysteresis); } #ifdef CONFIG_TESTING_OPTIONS int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type if_type, unsigned int *num, unsigned int *freq_list) { char *pos = wpa_s->get_pref_freq_list_override; char *end; unsigned int count = 0; /* Override string format: * :,,... :... */ while (pos) { if (atoi(pos) == (int) if_type) break; pos = os_strchr(pos, ' '); if (pos) pos++; } if (!pos) return -1; pos = os_strchr(pos, ':'); if (!pos) return -1; pos++; end = os_strchr(pos, ' '); while (pos && (!end || pos < end) && count < *num) { freq_list[count++] = atoi(pos); pos = os_strchr(pos, ','); if (pos) pos++; } *num = count; return 0; } #endif /* CONFIG_TESTING_OPTIONS */ static int wpas_ctrl_iface_get_pref_freq_list( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { unsigned int freq_list[100], num = 100, i; int ret; enum wpa_driver_if_type iface_type; char *pos, *end; pos = buf; end = buf + buflen; /* buf: "" */ if (os_strcmp(cmd, "STATION") == 0) iface_type = WPA_IF_STATION; else if (os_strcmp(cmd, "AP") == 0) iface_type = WPA_IF_AP_BSS; else if (os_strcmp(cmd, "P2P_GO") == 0) iface_type = WPA_IF_P2P_GO; else if (os_strcmp(cmd, "P2P_CLIENT") == 0) iface_type = WPA_IF_P2P_CLIENT; else if (os_strcmp(cmd, "IBSS") == 0) iface_type = WPA_IF_IBSS; else if (os_strcmp(cmd, "TDLS") == 0) iface_type = WPA_IF_TDLS; else return -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", iface_type, cmd); ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); if (ret) return -1; for (i = 0; i < num; i++) { ret = os_snprintf(pos, end - pos, "%s%u", i > 0 ? "," : "", freq_list[i]); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } return pos - buf; } static int wpas_ctrl_iface_driver_flags(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { int ret, i; char *pos, *end; ret = os_snprintf(buf, buflen, "%016llX:\n", (long long unsigned) wpa_s->drv_flags); if (os_snprintf_error(buflen, ret)) return -1; pos = buf + ret; end = buf + buflen; for (i = 0; i < 64; i++) { if (wpa_s->drv_flags & (1LLU << i)) { ret = os_snprintf(pos, end - pos, "%s\n", driver_flag_to_string(1LLU << i)); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } } return pos - buf; } static int wpas_ctrl_iface_driver_flags2(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { int ret, i; char *pos, *end; ret = os_snprintf(buf, buflen, "%016llX:\n", (long long unsigned) wpa_s->drv_flags2); if (os_snprintf_error(buflen, ret)) return -1; pos = buf + ret; end = buf + buflen; for (i = 0; i < 64; i++) { if (wpa_s->drv_flags2 & (1LLU << i)) { ret = os_snprintf(pos, end - pos, "%s\n", driver_flag2_to_string(1LLU << i)); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } } return pos - buf; } static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct hostap_sta_driver_data sta; int ret; ret = wpa_drv_pktcnt_poll(wpa_s, &sta); if (ret) return -1; ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n", sta.tx_packets, sta.tx_retry_failed, sta.rx_packets); if (os_snprintf_error(buflen, ret)) return -1; return ret; } #ifdef ANDROID static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int ret; ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); if (ret == 0) { if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { struct p2p_data *p2p = wpa_s->global->p2p; if (p2p) { char country[3]; country[0] = cmd[8]; country[1] = cmd[9]; country[2] = 0x04; p2p_set_country(p2p, country); } } ret = os_snprintf(buf, buflen, "%s\n", "OK"); if (os_snprintf_error(buflen, ret)) ret = -1; } return ret; } #endif /* ANDROID */ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int ret; char *pos, *temp = NULL; u8 *data = NULL; unsigned int vendor_id, subcmd; enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED; struct wpabuf *reply; size_t data_len = 0; /** * cmd: [] * [nested=<0|1>] */ vendor_id = strtoul(cmd, &pos, 16); if (!isblank((unsigned char) *pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { if (!isblank((unsigned char) *pos++)) return -EINVAL; temp = os_strchr(pos, ' '); data_len = temp ? (size_t) (temp - pos) : os_strlen(pos); } if (data_len) { data_len /= 2; data = os_malloc(data_len); if (!data) return -1; if (hexstr2bin(pos, data, data_len)) { wpa_printf(MSG_DEBUG, "Vendor command: wrong parameter format"); os_free(data); return -EINVAL; } } pos = os_strstr(cmd, "nested="); if (pos) nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED : NESTED_ATTR_NOT_USED; reply = wpabuf_alloc((buflen - 1) / 2); if (!reply) { os_free(data); return -1; } ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len, nested_attr_flag, reply); if (ret == 0) ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply), wpabuf_len(reply)); wpabuf_free(reply); os_free(data); return ret; } static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_P2P struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ? wpa_s->global->p2p_init_wpa_s : wpa_s; #endif /* CONFIG_P2P */ wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); if (wpas_abort_ongoing_scan(wpa_s) == 0) wpa_s->ignore_post_flush_scan_res = 1; if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* * Avoid possible auto connect re-connection on getting * disconnected due to state flush. */ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); } #ifdef CONFIG_P2P wpas_p2p_group_remove(p2p_wpa_s, "*"); wpas_p2p_cancel(p2p_wpa_s); p2p_ctrl_flush(p2p_wpa_s); wpas_p2p_service_flush(p2p_wpa_s); p2p_wpa_s->global->p2p_disabled = 0; p2p_wpa_s->global->p2p_per_sta_psk = 0; p2p_wpa_s->conf->num_sec_device_types = 0; p2p_wpa_s->p2p_disable_ip_addr_req = 0; os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range); p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; p2p_wpa_s->global->p2p_go_avoid_freq.num = 0; p2p_wpa_s->global->pending_p2ps_group = 0; p2p_wpa_s->global->pending_p2ps_group_freq = 0; #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS_TESTING wps_version_number = 0x20; wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; wps_force_auth_types_in_use = 0; wps_force_encr_types_in_use = 0; #endif /* CONFIG_WPS_TESTING */ #ifdef CONFIG_WPS wpa_s->wps_fragment_size = 0; wpas_wps_cancel(wpa_s); wps_registrar_flush(wpa_s->wps->registrar); #endif /* CONFIG_WPS */ wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; #ifdef CONFIG_DPP wpas_dpp_deinit(wpa_s); wpa_s->dpp_init_max_tries = 0; wpa_s->dpp_init_retry_time = 0; wpa_s->dpp_resp_wait_time = 0; wpa_s->dpp_resp_max_tries = 0; wpa_s->dpp_resp_retry_time = 0; #ifdef CONFIG_DPP2 wpas_dpp_chirp_stop(wpa_s); wpa_s->dpp_pfs_fallback = 0; #endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN); os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN); dpp_pkex_ephemeral_key_override_len = 0; dpp_protocol_key_override_len = 0; dpp_nonce_override_len = 0; #ifdef CONFIG_DPP2 dpp_version_override = 2; #else /* CONFIG_DPP2 */ dpp_version_override = 1; #endif /* CONFIG_DPP2 */ #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_DPP */ #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL); wpa_tdls_enable(wpa_s->wpa, 1); #endif /* CONFIG_TDLS */ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); wpa_supplicant_stop_countermeasures(wpa_s, NULL); wpa_s->last_michael_mic_error.sec = 0; wpa_s->no_keep_alive = 0; wpa_s->own_disconnect_req = 0; wpa_s->own_reconnect_req = 0; wpa_s->deny_ptk0_rekey = 0; os_free(wpa_s->disallow_aps_bssid); wpa_s->disallow_aps_bssid = NULL; wpa_s->disallow_aps_bssid_count = 0; os_free(wpa_s->disallow_aps_ssid); wpa_s->disallow_aps_ssid = NULL; wpa_s->disallow_aps_ssid_count = 0; wpa_s->set_sta_uapsd = 0; wpa_s->sta_uapsd = 0; wpa_s->consecutive_conn_failures = 0; wpa_drv_radio_disable(wpa_s, 0); wpa_bssid_ignore_clear(wpa_s); wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all"); wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all"); wpa_config_flush_blobs(wpa_s->conf); wpa_s->conf->auto_interworking = 0; wpa_s->conf->okc = 0; ptksa_cache_flush(wpa_s->ptksa, NULL, WPA_CIPHER_NONE); wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); rsn_preauth_deinit(wpa_s->wpa); wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200); wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70); wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60); eapol_sm_notify_logoff(wpa_s->eapol, false); radio_remove_works(wpa_s, NULL, 1); wpa_s->ext_work_in_progress = 0; wpa_s->next_ssid = NULL; #ifdef CONFIG_INTERWORKING #ifdef CONFIG_HS20 hs20_cancel_fetch_osu(wpa_s); hs20_del_icon(wpa_s, NULL, NULL); #endif /* CONFIG_HS20 */ #endif /* CONFIG_INTERWORKING */ wpa_s->ext_mgmt_frame_handling = 0; wpa_s->ext_eapol_frame_io = 0; #ifdef CONFIG_TESTING_OPTIONS wpa_s->extra_roc_dur = 0; wpa_s->test_failure = WPAS_TEST_FAILURE_NONE; wpa_s->p2p_go_csa_on_inv = 0; wpa_s->ignore_auth_resp = 0; wpa_s->ignore_assoc_disallow = 0; wpa_s->disable_sa_query = 0; wpa_s->testing_resend_assoc = 0; wpa_s->ignore_sae_h2e_only = 0; wpa_s->ft_rsnxe_used = 0; wpa_s->reject_btm_req_reason = 0; wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL); os_free(wpa_s->get_pref_freq_list_override); wpa_s->get_pref_freq_list_override = NULL; wpabuf_free(wpa_s->sae_commit_override); wpa_s->sae_commit_override = NULL; os_free(wpa_s->extra_sae_rejected_groups); wpa_s->extra_sae_rejected_groups = NULL; wpabuf_free(wpa_s->rsne_override_eapol); wpa_s->rsne_override_eapol = NULL; wpabuf_free(wpa_s->rsnxe_override_assoc); wpa_s->rsnxe_override_assoc = NULL; wpabuf_free(wpa_s->rsnxe_override_eapol); wpa_s->rsnxe_override_eapol = NULL; wpas_clear_driver_signal_override(wpa_s); wpa_s->oci_freq_override_eapol = 0; wpa_s->oci_freq_override_saquery_req = 0; wpa_s->oci_freq_override_saquery_resp = 0; wpa_s->oci_freq_override_eapol_g2 = 0; wpa_s->oci_freq_override_ft_assoc = 0; wpa_s->oci_freq_override_fils_assoc = 0; wpa_s->oci_freq_override_wnm_sleep = 0; #ifdef CONFIG_DPP os_free(wpa_s->dpp_config_obj_override); wpa_s->dpp_config_obj_override = NULL; os_free(wpa_s->dpp_discovery_override); wpa_s->dpp_discovery_override = NULL; os_free(wpa_s->dpp_groups_override); wpa_s->dpp_groups_override = NULL; dpp_test = DPP_TEST_DISABLED; #endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->disconnected = 0; os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN); wpa_s->next_scan_bssid_wildcard_ssid = 0; os_free(wpa_s->select_network_scan_freqs); wpa_s->select_network_scan_freqs = NULL; os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data)); wpa_bss_flush(wpa_s); if (!dl_list_empty(&wpa_s->bss)) { wpa_printf(MSG_DEBUG, "BSS table not empty after flush: %u entries, current_bss=%p bssid=" MACSTR " pending_bssid=" MACSTR, dl_list_len(&wpa_s->bss), wpa_s->current_bss, MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid)); } eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); wpa_s->wnmsleep_used = 0; #ifdef CONFIG_SME wpa_s->sme.last_unprot_disconnect.sec = 0; wpa_s->sme.auth_alg = 0; #endif /* CONFIG_SME */ wpabuf_free(wpa_s->ric_ies); wpa_s->ric_ies = NULL; wpa_supplicant_update_channel_list(wpa_s, NULL); free_bss_tmp_disallowed(wpa_s); os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data)); #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); #endif /* CONFIG_PASN */ if (wpa_s->mac_addr_changed && wpa_s->conf->mac_addr == 0) wpas_restore_permanent_mac_addr(wpa_s); } static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_radio_work *work; char *pos, *end; struct os_reltime now, diff; pos = buf; end = buf + buflen; os_get_reltime(&now); dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) { int ret; os_reltime_sub(&now, &work->time, &diff); ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n", work->type, work->wpa_s->ifname, work->freq, work->started, diff.sec, diff.usec); if (os_snprintf_error(end - pos, ret)) break; pos += ret; } return pos - buf; } static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_radio_work *work = eloop_ctx; struct wpa_external_work *ework = work->ctx; wpa_dbg(work->wpa_s, MSG_DEBUG, "Timing out external radio work %u (%s)", ework->id, work->type); wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id); work->wpa_s->ext_work_in_progress = 0; radio_work_done(work); os_free(ework); } static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) { struct wpa_external_work *ework = work->ctx; if (deinit) { if (work->started) eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); /* * work->type points to a buffer in ework, so need to replace * that here with a fixed string to avoid use of freed memory * in debug prints. */ work->type = "freed-ext-work"; work->ctx = NULL; os_free(ework); return; } wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)", ework->id, ework->type); wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id); work->wpa_s->ext_work_in_progress = 1; if (!ework->timeout) ework->timeout = 10; eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout, work, NULL); } static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { struct wpa_external_work *ework; char *pos, *pos2; size_t type_len; int ret; unsigned int freq = 0; /* format: [freq=] [timeout=] */ ework = os_zalloc(sizeof(*ework)); if (ework == NULL) return -1; pos = os_strchr(cmd, ' '); if (pos) { type_len = pos - cmd; pos++; pos2 = os_strstr(pos, "freq="); if (pos2) freq = atoi(pos2 + 5); pos2 = os_strstr(pos, "timeout="); if (pos2) ework->timeout = atoi(pos2 + 8); } else { type_len = os_strlen(cmd); } if (4 + type_len >= sizeof(ework->type)) type_len = sizeof(ework->type) - 4 - 1; os_strlcpy(ework->type, "ext:", sizeof(ework->type)); os_memcpy(ework->type + 4, cmd, type_len); ework->type[4 + type_len] = '\0'; wpa_s->ext_work_id++; if (wpa_s->ext_work_id == 0) wpa_s->ext_work_id++; ework->id = wpa_s->ext_work_id; if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb, ework) < 0) { os_free(ework); return -1; } ret = os_snprintf(buf, buflen, "%u", ework->id); if (os_snprintf_error(buflen, ret)) return -1; return ret; } static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd) { struct wpa_radio_work *work; unsigned int id = atoi(cmd); dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) { struct wpa_external_work *ework; if (os_strncmp(work->type, "ext:", 4) != 0) continue; ework = work->ctx; if (id && ework->id != id) continue; wpa_dbg(wpa_s, MSG_DEBUG, "Completed external radio work %u (%s)", ework->id, ework->type); eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); wpa_s->ext_work_in_progress = 0; radio_work_done(work); os_free(ework); return 3; /* "OK\n" */ } return -1; } static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { if (os_strcmp(cmd, "show") == 0) return wpas_ctrl_radio_work_show(wpa_s, buf, buflen); if (os_strncmp(cmd, "add ", 4) == 0) return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen); if (os_strncmp(cmd, "done ", 5) == 0) return wpas_ctrl_radio_work_done(wpa_s, cmd + 4); return -1; } void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s) { struct wpa_radio_work *work, *tmp; if (!wpa_s || !wpa_s->radio) return; dl_list_for_each_safe(work, tmp, &wpa_s->radio->work, struct wpa_radio_work, list) { struct wpa_external_work *ework; if (os_strncmp(work->type, "ext:", 4) != 0) continue; ework = work->ctx; wpa_dbg(wpa_s, MSG_DEBUG, "Flushing%s external radio work %u (%s)", work->started ? " started" : "", ework->id, ework->type); if (work->started) eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); radio_work_done(work); os_free(ework); } } static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; eapol_sm_notify_ctrl_response(wpa_s->eapol); } static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value, unsigned int *scan_id_count, int scan_id[]) { const char *pos = value; while (pos) { if (*pos == ' ' || *pos == '\0') break; if (*scan_id_count == MAX_SCAN_ID) return -1; scan_id[(*scan_id_count)++] = atoi(pos); pos = os_strchr(pos, ','); if (pos) pos++; } return 0; } static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { char *pos; unsigned int manual_scan_passive = 0; unsigned int manual_scan_use_id = 0; unsigned int manual_scan_only_new = 0; unsigned int scan_only = 0; unsigned int scan_id_count = 0; int scan_id[MAX_SCAN_ID]; void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); int *manual_scan_freqs = NULL; struct wpa_ssid_value *ssid = NULL, *ns; unsigned int ssid_count = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { *reply_len = -1; return; } if (radio_work_pending(wpa_s, "scan")) { wpa_printf(MSG_DEBUG, "Pending scan scheduled - reject new request"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); return; } #ifdef CONFIG_INTERWORKING if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { wpa_printf(MSG_DEBUG, "Interworking select in progress - reject new scan"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); return; } #endif /* CONFIG_INTERWORKING */ if (params) { if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) scan_only = 1; pos = os_strstr(params, "freq="); if (pos) { manual_scan_freqs = freq_range_to_channel_list(wpa_s, pos + 5); if (manual_scan_freqs == NULL) { *reply_len = -1; goto done; } } pos = os_strstr(params, "passive="); if (pos) manual_scan_passive = !!atoi(pos + 8); pos = os_strstr(params, "use_id="); if (pos) manual_scan_use_id = atoi(pos + 7); pos = os_strstr(params, "only_new=1"); if (pos) manual_scan_only_new = 1; pos = os_strstr(params, "scan_id="); if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count, scan_id) < 0) { *reply_len = -1; goto done; } pos = os_strstr(params, "bssid="); if (pos) { u8 bssid[ETH_ALEN]; pos += 6; if (hwaddr_aton(pos, bssid)) { wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos); *reply_len = -1; goto done; } os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN); wpa_s->next_scan_bssid_wildcard_ssid = os_strstr(params, "wildcard_ssid=1") != NULL; } pos = params; while (pos && *pos != '\0') { if (os_strncmp(pos, "ssid ", 5) == 0) { char *end; pos += 5; end = pos; while (*end) { if (*end == '\0' || *end == ' ') break; end++; } ns = os_realloc_array( ssid, ssid_count + 1, sizeof(struct wpa_ssid_value)); if (ns == NULL) { *reply_len = -1; goto done; } ssid = ns; if ((end - pos) & 0x01 || end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { wpa_printf(MSG_DEBUG, "Invalid SSID value '%s'", pos); *reply_len = -1; goto done; } ssid[ssid_count].ssid_len = (end - pos) / 2; wpa_hexdump_ascii(MSG_DEBUG, "scan SSID", ssid[ssid_count].ssid, ssid[ssid_count].ssid_len); ssid_count++; pos = end; } pos = os_strchr(pos, ' '); if (pos) pos++; } } wpa_s->num_ssids_from_scan_req = ssid_count; os_free(wpa_s->ssids_from_scan_req); if (ssid_count) { wpa_s->ssids_from_scan_req = ssid; ssid = NULL; } else { wpa_s->ssids_from_scan_req = NULL; } if (scan_only) scan_res_handler = scan_only_handler; else if (wpa_s->scan_res_handler == scan_only_handler) scan_res_handler = NULL; else scan_res_handler = wpa_s->scan_res_handler; if (!wpa_s->sched_scanning && !wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) || (wpa_s->wpa_state == WPA_COMPLETED))) { wpa_s->manual_scan_passive = manual_scan_passive; wpa_s->manual_scan_use_id = manual_scan_use_id; wpa_s->manual_scan_only_new = manual_scan_only_new; wpa_s->scan_id_count = scan_id_count; os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); wpa_s->scan_res_handler = scan_res_handler; os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = manual_scan_freqs; manual_scan_freqs = NULL; wpa_s->normal_scans = 0; wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); if (wpa_s->manual_scan_use_id) { wpa_s->manual_scan_id++; wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", wpa_s->manual_scan_id); *reply_len = os_snprintf(reply, reply_size, "%u\n", wpa_s->manual_scan_id); } } else if (wpa_s->sched_scanning) { wpa_s->manual_scan_passive = manual_scan_passive; wpa_s->manual_scan_use_id = manual_scan_use_id; wpa_s->manual_scan_only_new = manual_scan_only_new; wpa_s->scan_id_count = scan_id_count; os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); wpa_s->scan_res_handler = scan_res_handler; os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = manual_scan_freqs; manual_scan_freqs = NULL; wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); if (wpa_s->manual_scan_use_id) { wpa_s->manual_scan_id++; *reply_len = os_snprintf(reply, reply_size, "%u\n", wpa_s->manual_scan_id); wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", wpa_s->manual_scan_id); } } else { wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); } done: os_free(manual_scan_freqs); os_free(ssid); } #ifdef CONFIG_TESTING_OPTIONS static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result) { wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR " src=" MACSTR " bssid=" MACSTR " result=%s", freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "NO_ACK" : "FAILED")); } static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; size_t len; u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN]; int res, used; int freq = 0, no_cck = 0, wait_time = 0; /* [freq=] [wait_time=] [no_cck=1] * */ wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); pos = cmd; used = hwaddr_aton2(pos, da); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; used = hwaddr_aton2(pos, bssid); if (used < 0) return -1; pos += used; param = os_strstr(pos, " freq="); if (param) { param += 6; freq = atoi(param); } param = os_strstr(pos, " no_cck="); if (param) { param += 8; no_cck = atoi(param); } param = os_strstr(pos, " wait_time="); if (param) { param += 11; wait_time = atoi(param); } param = os_strstr(pos, " action="); if (param == NULL) return -1; param += 8; len = os_strlen(param); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(param, buf, len) < 0) { os_free(buf); return -1; } res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid, buf, len, wait_time, wpas_ctrl_iface_mgmt_tx_cb, no_cck); os_free(buf); return res; } static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting"); offchannel_send_action_done(wpa_s); } static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; size_t len; u8 *buf; int freq = 0, datarate = 0, ssi_signal = 0; union wpa_event_data event; if (!wpa_s->ext_mgmt_frame_handling) return -1; /* freq= datarate= ssi_signal= frame= */ wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); pos = cmd; param = os_strstr(pos, "freq="); if (param) { param += 5; freq = atoi(param); } param = os_strstr(pos, " datarate="); if (param) { param += 10; datarate = atoi(param); } param = os_strstr(pos, " ssi_signal="); if (param) { param += 12; ssi_signal = atoi(param); } param = os_strstr(pos, " frame="); if (param == NULL) return -1; param += 7; len = os_strlen(param); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(param, buf, len) < 0) { os_free(buf); return -1; } os_memset(&event, 0, sizeof(event)); event.rx_mgmt.freq = freq; event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = len; event.rx_mgmt.ssi_signal = ssi_signal; event.rx_mgmt.datarate = datarate; wpa_s->ext_mgmt_frame_handling = 0; wpa_supplicant_event(wpa_s, EVENT_RX_MGMT, &event); wpa_s->ext_mgmt_frame_handling = 1; os_free(buf); return 0; } static int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s, char *param) { struct wpa_scan_res *res; struct os_reltime now; char *pos, *end; int ret = -1; if (!param) return -1; if (os_strcmp(param, "START") == 0) { wpa_bss_update_start(wpa_s); return 0; } if (os_strcmp(param, "END") == 0) { wpa_bss_update_end(wpa_s, NULL, 1); return 0; } if (os_strncmp(param, "BSS ", 4) != 0) return -1; param += 3; res = os_zalloc(sizeof(*res) + os_strlen(param) / 2); if (!res) return -1; pos = os_strstr(param, " flags="); if (pos) res->flags = strtol(pos + 7, NULL, 16); pos = os_strstr(param, " bssid="); if (pos && hwaddr_aton(pos + 7, res->bssid)) goto fail; pos = os_strstr(param, " freq="); if (pos) res->freq = atoi(pos + 6); pos = os_strstr(param, " beacon_int="); if (pos) res->beacon_int = atoi(pos + 12); pos = os_strstr(param, " caps="); if (pos) res->caps = strtol(pos + 6, NULL, 16); pos = os_strstr(param, " qual="); if (pos) res->qual = atoi(pos + 6); pos = os_strstr(param, " noise="); if (pos) res->noise = atoi(pos + 7); pos = os_strstr(param, " level="); if (pos) res->level = atoi(pos + 7); pos = os_strstr(param, " tsf="); if (pos) res->tsf = strtoll(pos + 5, NULL, 16); pos = os_strstr(param, " age="); if (pos) res->age = atoi(pos + 5); pos = os_strstr(param, " est_throughput="); if (pos) res->est_throughput = atoi(pos + 16); pos = os_strstr(param, " snr="); if (pos) res->snr = atoi(pos + 5); pos = os_strstr(param, " parent_tsf="); if (pos) res->parent_tsf = strtoll(pos + 7, NULL, 16); pos = os_strstr(param, " tsf_bssid="); if (pos && hwaddr_aton(pos + 11, res->tsf_bssid)) goto fail; pos = os_strstr(param, " ie="); if (pos) { pos += 4; end = os_strchr(pos, ' '); if (!end) end = pos + os_strlen(pos); res->ie_len = (end - pos) / 2; if (hexstr2bin(pos, (u8 *) (res + 1), res->ie_len)) goto fail; } pos = os_strstr(param, " beacon_ie="); if (pos) { pos += 11; end = os_strchr(pos, ' '); if (!end) end = pos + os_strlen(pos); res->beacon_ie_len = (end - pos) / 2; if (hexstr2bin(pos, ((u8 *) (res + 1)) + res->ie_len, res->beacon_ie_len)) goto fail; } os_get_reltime(&now); wpa_bss_update_scan_res(wpa_s, res, &now); ret = 0; fail: os_free(res); return ret; } static int wpas_ctrl_iface_driver_event_assoc(struct wpa_supplicant *wpa_s, char *param) { union wpa_event_data event; struct assoc_info *ai; char *ctx = NULL; int ret = -1; struct wpabuf *req_ies = NULL; struct wpabuf *resp_ies = NULL; struct wpabuf *resp_frame = NULL; struct wpabuf *beacon_ies = NULL; struct wpabuf *key_replay_ctr = NULL; struct wpabuf *ptk_kck = NULL; struct wpabuf *ptk_kek = NULL; struct wpabuf *fils_pmk = NULL; char *str, *pos; u8 addr[ETH_ALEN]; u8 fils_pmkid[PMKID_LEN]; os_memset(&event, 0, sizeof(event)); ai = &event.assoc_info; while ((str = str_token(param, " ", &ctx))) { pos = os_strchr(str, '='); if (!pos) goto fail; *pos++ = '\0'; if (os_strcmp(str, "reassoc") == 0) { ai->reassoc = atoi(pos); } else if (os_strcmp(str, "req_ies") == 0) { wpabuf_free(req_ies); req_ies = wpabuf_parse_bin(pos); if (!req_ies) goto fail; ai->req_ies = wpabuf_head(req_ies); ai->req_ies_len = wpabuf_len(req_ies); } else if (os_strcmp(str, "resp_ies") == 0) { wpabuf_free(resp_ies); resp_ies = wpabuf_parse_bin(pos); if (!resp_ies) goto fail; ai->resp_ies = wpabuf_head(resp_ies); ai->resp_ies_len = wpabuf_len(resp_ies); } else if (os_strcmp(str, "resp_frame") == 0) { wpabuf_free(resp_frame); resp_frame = wpabuf_parse_bin(pos); if (!resp_frame) goto fail; ai->resp_frame = wpabuf_head(resp_frame); ai->resp_frame_len = wpabuf_len(resp_frame); } else if (os_strcmp(str, "beacon_ies") == 0) { wpabuf_free(beacon_ies); beacon_ies = wpabuf_parse_bin(pos); if (!beacon_ies) goto fail; ai->beacon_ies = wpabuf_head(beacon_ies); ai->beacon_ies_len = wpabuf_len(beacon_ies); } else if (os_strcmp(str, "freq") == 0) { ai->freq = atoi(pos); } else if (os_strcmp(str, "wmm::info_bitmap") == 0) { ai->wmm_params.info_bitmap = atoi(pos); } else if (os_strcmp(str, "wmm::uapsd_queues") == 0) { ai->wmm_params.uapsd_queues = atoi(pos); } else if (os_strcmp(str, "addr") == 0) { if (hwaddr_aton(pos, addr)) goto fail; ai->addr = addr; } else if (os_strcmp(str, "authorized") == 0) { ai->authorized = atoi(pos); } else if (os_strcmp(str, "key_replay_ctr") == 0) { wpabuf_free(key_replay_ctr); key_replay_ctr = wpabuf_parse_bin(pos); if (!key_replay_ctr) goto fail; ai->key_replay_ctr = wpabuf_head(key_replay_ctr); ai->key_replay_ctr_len = wpabuf_len(key_replay_ctr); } else if (os_strcmp(str, "ptk_kck") == 0) { wpabuf_free(ptk_kck); ptk_kck = wpabuf_parse_bin(pos); if (!ptk_kck) goto fail; ai->ptk_kck = wpabuf_head(ptk_kck); ai->ptk_kck_len = wpabuf_len(ptk_kck); } else if (os_strcmp(str, "ptk_kek") == 0) { wpabuf_free(ptk_kek); ptk_kek = wpabuf_parse_bin(pos); if (!ptk_kek) goto fail; ai->ptk_kek = wpabuf_head(ptk_kek); ai->ptk_kek_len = wpabuf_len(ptk_kek); } else if (os_strcmp(str, "subnet_status") == 0) { ai->subnet_status = atoi(pos); } else if (os_strcmp(str, "fils_erp_next_seq_num") == 0) { ai->fils_erp_next_seq_num = atoi(pos); } else if (os_strcmp(str, "fils_pmk") == 0) { wpabuf_free(fils_pmk); fils_pmk = wpabuf_parse_bin(pos); if (!fils_pmk) goto fail; ai->fils_pmk = wpabuf_head(fils_pmk); ai->fils_pmk_len = wpabuf_len(fils_pmk); } else if (os_strcmp(str, "fils_pmkid") == 0) { if (hexstr2bin(pos, fils_pmkid, PMKID_LEN) < 0) goto fail; ai->fils_pmkid = fils_pmkid; } else { goto fail; } } wpa_supplicant_event(wpa_s, EVENT_ASSOC, &event); ret = 0; fail: wpabuf_free(req_ies); wpabuf_free(resp_ies); wpabuf_free(resp_frame); wpabuf_free(beacon_ies); wpabuf_free(key_replay_ctr); wpabuf_free(ptk_kck); wpabuf_free(ptk_kek); wpabuf_free(fils_pmk); return ret; } static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; union wpa_event_data event; enum wpa_event_type ev; /* [parameters..] */ wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd); pos = cmd; param = os_strchr(pos, ' '); if (param) *param++ = '\0'; os_memset(&event, 0, sizeof(event)); if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) { ev = EVENT_INTERFACE_ENABLED; } else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) { ev = EVENT_INTERFACE_DISABLED; } else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) { ev = EVENT_AVOID_FREQUENCIES; if (param == NULL) param = ""; if (freq_range_list_parse(&event.freq_range, param) < 0) return -1; wpa_supplicant_event(wpa_s, ev, &event); os_free(event.freq_range.range); return 0; } else if (os_strcmp(cmd, "SCAN_RES") == 0) { return wpas_ctrl_iface_driver_scan_res(wpa_s, param); } else if (os_strcmp(cmd, "ASSOC") == 0) { return wpas_ctrl_iface_driver_event_assoc(wpa_s, param); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s", cmd); return -1; } wpa_supplicant_event(wpa_s, ev, &event); return 0; } static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 src[ETH_ALEN], *buf; int used; size_t len; wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); pos = cmd; used = hwaddr_aton2(pos, src); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; len = os_strlen(pos); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, buf, len) < 0) { os_free(buf); return -1; } wpa_supplicant_rx_eapol(wpa_s, src, buf, len); os_free(buf); return 0; } static int wpas_ctrl_iface_eapol_tx(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; u8 dst[ETH_ALEN], *buf; int used, ret; size_t len; unsigned int prev; wpa_printf(MSG_DEBUG, "External EAPOL TX: %s", cmd); pos = cmd; used = hwaddr_aton2(pos, dst); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; len = os_strlen(pos); if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (!buf || hexstr2bin(pos, buf, len) < 0) { os_free(buf); return -1; } prev = wpa_s->ext_eapol_frame_io; wpa_s->ext_eapol_frame_io = 0; ret = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, buf, len); wpa_s->ext_eapol_frame_io = prev; os_free(buf); return ret; } static u16 ipv4_hdr_checksum(const void *buf, size_t len) { size_t i; u32 sum = 0; const u16 *pos = buf; for (i = 0; i < len / 2; i++) sum += *pos++; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return sum ^ 0xffff; } #define HWSIM_PACKETLEN 1500 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) static void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_supplicant *wpa_s = ctx; const struct ether_header *eth; struct ip ip; const u8 *pos; unsigned int i; char extra[30]; if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) { wpa_printf(MSG_DEBUG, "test data: RX - ignore unexpected length %d", (int) len); return; } eth = (const struct ether_header *) buf; os_memcpy(&ip, eth + 1, sizeof(ip)); pos = &buf[sizeof(*eth) + sizeof(ip)]; if (ip.ip_hl != 5 || ip.ip_v != 4 || ntohs(ip.ip_len) > HWSIM_IP_LEN) { wpa_printf(MSG_DEBUG, "test data: RX - ignore unexpected IP header"); return; } for (i = 0; i < ntohs(ip.ip_len) - sizeof(ip); i++) { if (*pos != (u8) i) { wpa_printf(MSG_DEBUG, "test data: RX - ignore mismatching payload"); return; } pos++; } extra[0] = '\0'; if (ntohs(ip.ip_len) != HWSIM_IP_LEN) os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.ip_len)); wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s", MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra); } static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, char *cmd) { int enabled = atoi(cmd); char *pos; const char *ifname; if (!enabled) { if (wpa_s->l2_test) { l2_packet_deinit(wpa_s->l2_test); wpa_s->l2_test = NULL; wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled"); } return 0; } if (wpa_s->l2_test) return 0; pos = os_strstr(cmd, " ifname="); if (pos) ifname = pos + 8; else ifname = wpa_s->ifname; wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr, ETHERTYPE_IP, wpas_data_test_rx, wpa_s, 1); if (wpa_s->l2_test == NULL) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled"); return 0; } static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst[ETH_ALEN], src[ETH_ALEN]; char *pos, *pos2; int used; long int val; u8 tos; u8 buf[2 + HWSIM_PACKETLEN]; struct ether_header *eth; struct ip *ip; u8 *dpos; unsigned int i; size_t send_len = HWSIM_IP_LEN; if (wpa_s->l2_test == NULL) return -1; /* format: [len=] */ pos = cmd; used = hwaddr_aton2(pos, dst); if (used < 0) return -1; pos += used; while (*pos == ' ') pos++; used = hwaddr_aton2(pos, src); if (used < 0) return -1; pos += used; val = strtol(pos, &pos2, 0); if (val < 0 || val > 0xff) return -1; tos = val; pos = os_strstr(pos2, " len="); if (pos) { i = atoi(pos + 5); if (i < sizeof(*ip) || i > HWSIM_IP_LEN) return -1; send_len = i; } eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_IP); ip = (struct ip *) (eth + 1); os_memset(ip, 0, sizeof(*ip)); ip->ip_hl = 5; ip->ip_v = 4; ip->ip_ttl = 64; ip->ip_tos = tos; ip->ip_len = htons(send_len); ip->ip_p = 1; ip->ip_src.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); ip->ip_dst.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->ip_sum = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); for (i = 0; i < send_len - sizeof(*ip); i++) *dpos++ = i; if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2], sizeof(struct ether_header) + send_len) < 0) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos); return 0; } static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s, char *cmd) { u8 *buf; struct ether_header *eth; struct l2_packet_data *l2 = NULL; size_t len; u16 ethertype; int res = -1; len = os_strlen(cmd); if (len & 1 || len < ETH_HLEN * 2) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(cmd, buf, len) < 0) goto done; eth = (struct ether_header *) buf; ethertype = ntohs(eth->ether_type); l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype, wpas_data_test_rx, wpa_s, 1); if (l2 == NULL) goto done; res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len); wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res); done: if (l2) l2_packet_deinit(l2); os_free(buf); return res < 0 ? -1 : 0; } static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD char *pos; wpa_trace_fail_after = atoi(cmd); pos = os_strchr(cmd, ':'); if (pos) { pos++; os_strlcpy(wpa_trace_fail_func, pos, sizeof(wpa_trace_fail_func)); } else { wpa_trace_fail_after = 0; } return 0; #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD char *pos; wpa_trace_test_fail_after = atoi(cmd); pos = os_strchr(cmd, ':'); if (pos) { pos++; os_strlcpy(wpa_trace_test_fail_func, pos, sizeof(wpa_trace_test_fail_func)); } else { wpa_trace_test_fail_after = 0; } return 0; #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ return -1; #endif /* WPA_TRACE_BFD */ } static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; int i, count = (intptr_t) timeout_ctx; wpa_printf(MSG_DEBUG, "TEST: Send %d control interface event messages", count); for (i = 0; i < count; i++) { wpa_msg_ctrl(wpa_s, MSG_INFO, "TEST-EVENT-MESSAGE %d/%d", i + 1, count); } } static int wpas_ctrl_event_test(struct wpa_supplicant *wpa_s, const char *cmd) { int count; count = atoi(cmd); if (count <= 0) return -1; return eloop_register_timeout(0, 0, wpas_ctrl_event_test_cb, wpa_s, (void *) (intptr_t) count); } static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s, const char *cmd) { struct wpabuf *buf; size_t len; len = os_strlen(cmd); if (len & 1) return -1; len /= 2; if (len == 0) { buf = NULL; } else { buf = wpabuf_alloc(len); if (buf == NULL) return -1; if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { wpabuf_free(buf); return -1; } } wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf); return 0; } static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s) { u8 zero[WPA_TK_MAX_LEN]; if (wpa_s->last_tk_alg == WPA_ALG_NONE) return -1; wpa_printf(MSG_INFO, "TESTING: Reset PN"); os_memset(zero, 0, sizeof(zero)); /* First, use a zero key to avoid any possible duplicate key avoidance * in the driver. */ if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr, wpa_s->last_tk_key_idx, 1, zero, 6, zero, wpa_s->last_tk_len, KEY_FLAG_PAIRWISE_RX_TX) < 0) return -1; /* Set the previously configured key to reset its TSC/RSC */ return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr, wpa_s->last_tk_key_idx, 1, zero, 6, wpa_s->last_tk, wpa_s->last_tk_len, KEY_FLAG_PAIRWISE_RX_TX); } static int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd) { const char *pos = cmd; int error, pairwise; error = atoi(pos); pos = os_strchr(pos, ' '); if (!pos) return -1; pairwise = atoi(pos); wpa_sm_key_request(wpa_s->wpa, error, pairwise); return 0; } static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_SME struct wpa_driver_associate_params params; int ret; os_memset(¶ms, 0, sizeof(params)); params.bssid = wpa_s->bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; params.freq.freq = wpa_s->sme.freq; if (wpa_s->last_assoc_req_wpa_ie) { params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie); params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie); } params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = wpa_s->group_cipher; params.mgmt_group_suite = wpa_s->mgmt_group_cipher; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; params.mgmt_frame_protection = wpa_s->sme.mfp; params.rrm_used = wpa_s->rrm.rrm_used; if (wpa_s->sme.prev_bssid_set) params.prev_bssid = wpa_s->sme.prev_bssid; wpa_printf(MSG_INFO, "TESTING: Resend association request"); ret = wpa_drv_associate(wpa_s, ¶ms); wpa_s->testing_resend_assoc = 1; return ret; #else /* CONFIG_SME */ return -1; #endif /* CONFIG_SME */ } static int wpas_ctrl_iface_send_twt_setup(struct wpa_supplicant *wpa_s, const char *cmd) { u8 dtok = 1; int exponent = 10; int mantissa = 8192; u8 min_twt = 255; unsigned long long twt = 0; bool requestor = true; int setup_cmd = 0; bool trigger = true; bool implicit = true; bool flow_type = true; int flow_id = 0; bool protection = false; u8 twt_channel = 0; u8 control = BIT(4); /* Control field (IEEE P802.11ax/D8.0 Figure * 9-687): B4 = TWT Information Frame Disabled */ const char *tok_s; tok_s = os_strstr(cmd, " dialog="); if (tok_s) dtok = atoi(tok_s + os_strlen(" dialog=")); tok_s = os_strstr(cmd, " exponent="); if (tok_s) exponent = atoi(tok_s + os_strlen(" exponent=")); tok_s = os_strstr(cmd, " mantissa="); if (tok_s) mantissa = atoi(tok_s + os_strlen(" mantissa=")); tok_s = os_strstr(cmd, " min_twt="); if (tok_s) min_twt = atoi(tok_s + os_strlen(" min_twt=")); tok_s = os_strstr(cmd, " setup_cmd="); if (tok_s) setup_cmd = atoi(tok_s + os_strlen(" setup_cmd=")); tok_s = os_strstr(cmd, " twt="); if (tok_s) sscanf(tok_s + os_strlen(" twt="), "%llu", &twt); tok_s = os_strstr(cmd, " requestor="); if (tok_s) requestor = atoi(tok_s + os_strlen(" requestor=")); tok_s = os_strstr(cmd, " trigger="); if (tok_s) trigger = atoi(tok_s + os_strlen(" trigger=")); tok_s = os_strstr(cmd, " implicit="); if (tok_s) implicit = atoi(tok_s + os_strlen(" implicit=")); tok_s = os_strstr(cmd, " flow_type="); if (tok_s) flow_type = atoi(tok_s + os_strlen(" flow_type=")); tok_s = os_strstr(cmd, " flow_id="); if (tok_s) flow_id = atoi(tok_s + os_strlen(" flow_id=")); tok_s = os_strstr(cmd, " protection="); if (tok_s) protection = atoi(tok_s + os_strlen(" protection=")); tok_s = os_strstr(cmd, " twt_channel="); if (tok_s) twt_channel = atoi(tok_s + os_strlen(" twt_channel=")); tok_s = os_strstr(cmd, " control="); if (tok_s) control = atoi(tok_s + os_strlen(" control=")); return wpas_twt_send_setup(wpa_s, dtok, exponent, mantissa, min_twt, setup_cmd, twt, requestor, trigger, implicit, flow_type, flow_id, protection, twt_channel, control); } static int wpas_ctrl_iface_send_twt_teardown(struct wpa_supplicant *wpa_s, const char *cmd) { u8 flags = 0x1; const char *tok_s; tok_s = os_strstr(cmd, " flags="); if (tok_s) flags = atoi(tok_s + os_strlen(" flags=")); return wpas_twt_send_teardown(wpa_s, flags); } #endif /* CONFIG_TESTING_OPTIONS */ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) { char *pos = cmd; int frame; size_t len; struct wpabuf *buf; struct ieee802_11_elems elems; frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; wpa_s = wpas_vendor_elem(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; pos++; len = os_strlen(pos); if (len == 0) return 0; if (len & 1) return -1; len /= 2; buf = wpabuf_alloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { wpabuf_free(buf); return -1; } if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) == ParseFailed) { wpabuf_free(buf); return -1; } if (wpa_s->vendor_elem[frame] == NULL) { wpa_s->vendor_elem[frame] = buf; goto update_ies; } if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) { wpabuf_free(buf); return -1; } wpabuf_put_buf(wpa_s->vendor_elem[frame], buf); wpabuf_free(buf); update_ies: wpas_vendor_elem_update(wpa_s); if (frame == VENDOR_ELEM_PROBE_REQ || frame == VENDOR_ELEM_PROBE_REQ_P2P) wpa_supplicant_set_default_scan_ies(wpa_s); return 0; } static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { int frame = atoi(cmd); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; wpa_s = wpas_vendor_elem(wpa_s, frame); if (wpa_s->vendor_elem[frame] == NULL) return 0; return wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(wpa_s->vendor_elem[frame]), wpabuf_len(wpa_s->vendor_elem[frame])); } static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) { char *pos = cmd; int frame; size_t len; u8 *buf; struct ieee802_11_elems elems; int res; frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; wpa_s = wpas_vendor_elem(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) return -1; pos++; if (*pos == '*') { wpabuf_free(wpa_s->vendor_elem[frame]); wpa_s->vendor_elem[frame] = NULL; wpas_vendor_elem_update(wpa_s); return 0; } if (wpa_s->vendor_elem[frame] == NULL) return -1; len = os_strlen(pos); if (len == 0) return 0; if (len & 1) return -1; len /= 2; buf = os_malloc(len); if (buf == NULL) return -1; if (hexstr2bin(pos, buf, len) < 0) { os_free(buf); return -1; } if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) { os_free(buf); return -1; } res = wpas_vendor_elem_remove(wpa_s, frame, buf, len); os_free(buf); return res; } static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) { struct wpa_supplicant *wpa_s = ctx; size_t len; const u8 *data; /* * Neighbor Report element (IEEE P802.11-REVmc/D5.0) * BSSID[6] * BSSID Information[4] * Operating Class[1] * Channel Number[1] * PHY Type[1] * Optional Subelements[variable] */ #define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) if (!neighbor_rep || wpabuf_len(neighbor_rep) == 0) { wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED); goto out; } data = wpabuf_head_u8(neighbor_rep); len = wpabuf_len(neighbor_rep); while (len >= 2 + NR_IE_MIN_LEN) { const u8 *nr; char lci[256 * 2 + 1]; char civic[256 * 2 + 1]; u8 nr_len = data[1]; const u8 *pos = data, *end; if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || nr_len < NR_IE_MIN_LEN) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL: Invalid Neighbor Report element: id=%u len=%u", data[0], nr_len); goto out; } if (2U + nr_len > len) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", data[0], len, nr_len); goto out; } pos += 2; end = pos + nr_len; nr = pos; pos += NR_IE_MIN_LEN; lci[0] = '\0'; civic[0] = '\0'; while (end - pos > 2) { u8 s_id, s_len; s_id = *pos++; s_len = *pos++; if (s_len > end - pos) goto out; if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) { /* Measurement Token[1] */ /* Measurement Report Mode[1] */ /* Measurement Type[1] */ /* Measurement Report[variable] */ switch (pos[2]) { case MEASURE_TYPE_LCI: if (lci[0]) break; wpa_snprintf_hex(lci, sizeof(lci), pos, s_len); break; case MEASURE_TYPE_LOCATION_CIVIC: if (civic[0]) break; wpa_snprintf_hex(civic, sizeof(civic), pos, s_len); break; } } pos += s_len; } wpa_msg(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED "bssid=" MACSTR " info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s", MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], nr[ETH_ALEN + 6], lci[0] ? " lci=" : "", lci, civic[0] ? " civic=" : "", civic); data = end; len -= 2 + nr_len; } out: wpabuf_free(neighbor_rep); } static int wpas_ctrl_iface_send_neighbor_rep(struct wpa_supplicant *wpa_s, char *cmd) { struct wpa_ssid_value ssid, *ssid_p = NULL; int ret, lci = 0, civic = 0; char *ssid_s; ssid_s = os_strstr(cmd, "ssid="); if (ssid_s) { if (ssid_parse(ssid_s + 5, &ssid)) { wpa_msg(wpa_s, MSG_INFO, "CTRL: Send Neighbor Report: bad SSID"); return -1; } ssid_p = &ssid; /* * Move cmd after the SSID text that may include "lci" or * "civic". */ cmd = os_strchr(ssid_s + 6, ssid_s[5] == '"' ? '"' : ' '); if (cmd) cmd++; } if (cmd && os_strstr(cmd, "lci")) lci = 1; if (cmd && os_strstr(cmd, "civic")) civic = 1; ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, lci, civic, wpas_ctrl_neighbor_rep_cb, wpa_s); return ret; } static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s) { eapol_sm_erp_flush(wpa_s->eapol); return 0; } static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, char *cmd) { char *token, *context = NULL; unsigned int enable = ~0, type = 0; u8 _addr[ETH_ALEN], _mask[ETH_ALEN]; u8 *addr = NULL, *mask = NULL; while ((token = str_token(cmd, " ", &context))) { if (os_strcasecmp(token, "scan") == 0) { type |= MAC_ADDR_RAND_SCAN; } else if (os_strcasecmp(token, "sched") == 0) { type |= MAC_ADDR_RAND_SCHED_SCAN; } else if (os_strcasecmp(token, "pno") == 0) { type |= MAC_ADDR_RAND_PNO; } else if (os_strcasecmp(token, "all") == 0) { type = wpa_s->mac_addr_rand_supported; } else if (os_strncasecmp(token, "enable=", 7) == 0) { enable = atoi(token + 7); } else if (os_strncasecmp(token, "addr=", 5) == 0) { addr = _addr; if (hwaddr_aton(token + 5, addr)) { wpa_printf(MSG_INFO, "CTRL: Invalid MAC address: %s", token); return -1; } } else if (os_strncasecmp(token, "mask=", 5) == 0) { mask = _mask; if (hwaddr_aton(token + 5, mask)) { wpa_printf(MSG_INFO, "CTRL: Invalid MAC address mask: %s", token); return -1; } } else { wpa_printf(MSG_INFO, "CTRL: Invalid MAC_RAND_SCAN parameter: %s", token); return -1; } } if (!type) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified"); return -1; } if (enable > 1) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN enable=<0/1> not specified"); return -1; } if (!enable) return wpas_disable_mac_addr_randomization(wpa_s, type); return wpas_enable_mac_addr_randomization(wpa_s, type, addr, mask); } static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { size_t reply_len; reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen); #ifdef CONFIG_AP reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len], buflen - reply_len); #endif /* CONFIG_AP */ return reply_len; } static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s) { ptksa_cache_flush(wpa_s->ptksa, NULL, WPA_CIPHER_NONE); wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); #ifdef CONFIG_AP wpas_ap_pmksa_cache_flush(wpa_s); #endif /* CONFIG_AP */ } #ifdef CONFIG_PMKSA_CACHE_EXTERNAL static int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) { struct rsn_pmksa_cache_entry *entry; struct wpa_ssid *ssid; char *pos, *pos2, *end; int ret; struct os_reltime now; ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd)); if (!ssid) return -1; pos = buf; end = buf + buflen; os_get_reltime(&now); /* * Entry format: * * * [FILS Cache Identifier] */ for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry; entry = entry->next) { if (entry->network_ctx != ssid) continue; pos2 = pos; ret = os_snprintf(pos2, end - pos2, MACSTR " ", MAC2STR(entry->aa)); if (os_snprintf_error(end - pos2, ret)) break; pos2 += ret; pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid, PMKID_LEN); ret = os_snprintf(pos2, end - pos2, " "); if (os_snprintf_error(end - pos2, ret)) break; pos2 += ret; pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk, entry->pmk_len); ret = os_snprintf(pos2, end - pos2, " %d %d %d %d", (int) (entry->reauth_time - now.sec), (int) (entry->expiration - now.sec), entry->akmp, entry->opportunistic); if (os_snprintf_error(end - pos2, ret)) break; pos2 += ret; if (entry->fils_cache_id_set) { ret = os_snprintf(pos2, end - pos2, " %02x%02x", entry->fils_cache_id[0], entry->fils_cache_id[1]); if (os_snprintf_error(end - pos2, ret)) break; pos2 += ret; } ret = os_snprintf(pos2, end - pos2, "\n"); if (os_snprintf_error(end - pos2, ret)) break; pos2 += ret; pos = pos2; } return pos - buf; } static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s, char *cmd) { struct rsn_pmksa_cache_entry *entry; struct wpa_ssid *ssid; char *pos, *pos2; int ret = -1; struct os_reltime now; int reauth_time = 0, expiration = 0, i; /* * Entry format: * * * [FILS Cache Identifier] */ ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd)); if (!ssid) return -1; pos = os_strchr(cmd, ' '); if (!pos) return -1; pos++; entry = os_zalloc(sizeof(*entry)); if (!entry) return -1; if (hwaddr_aton(pos, entry->aa)) goto fail; pos = os_strchr(pos, ' '); if (!pos) goto fail; pos++; if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0) goto fail; pos = os_strchr(pos, ' '); if (!pos) goto fail; pos++; pos2 = os_strchr(pos, ' '); if (!pos2) goto fail; entry->pmk_len = (pos2 - pos) / 2; if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX || hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0) goto fail; pos = os_strchr(pos, ' '); if (!pos) goto fail; pos++; if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration, &entry->akmp, &entry->opportunistic) != 4) goto fail; for (i = 0; i < 4; i++) { pos = os_strchr(pos, ' '); if (!pos) { if (i < 3) goto fail; break; } pos++; } if (pos) { if (hexstr2bin(pos, entry->fils_cache_id, FILS_CACHE_ID_LEN) < 0) goto fail; entry->fils_cache_id_set = 1; } os_get_reltime(&now); entry->expiration = now.sec + expiration; entry->reauth_time = now.sec + reauth_time; entry->network_ctx = ssid; + entry->external = true; + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); entry = NULL; ret = 0; fail: os_free(entry); return ret; } #ifdef CONFIG_MESH static int wpas_ctrl_iface_mesh_pmksa_get(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) { u8 spa[ETH_ALEN]; if (!wpa_s->ifmsh) return -1; if (os_strcasecmp(cmd, "any") == 0) return wpas_ap_pmksa_cache_list_mesh(wpa_s, NULL, buf, buflen); if (hwaddr_aton(cmd, spa)) return -1; return wpas_ap_pmksa_cache_list_mesh(wpa_s, spa, buf, buflen); } static int wpas_ctrl_iface_mesh_pmksa_add(struct wpa_supplicant *wpa_s, char *cmd) { /* * We do not check mesh interface existence because PMKSA should be * stored before wpa_s->ifmsh creation to suppress commit message * creation. */ return wpas_ap_pmksa_cache_add_external(wpa_s, cmd); } #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ #ifdef CONFIG_FILS static int wpas_ctrl_iface_fils_hlp_req_add(struct wpa_supplicant *wpa_s, const char *cmd) { struct fils_hlp_req *req; const char *pos; /* format: */ req = os_zalloc(sizeof(*req)); if (!req) return -1; if (hwaddr_aton(cmd, req->dst)) goto fail; pos = os_strchr(cmd, ' '); if (!pos) goto fail; pos++; req->pkt = wpabuf_parse_bin(pos); if (!req->pkt) goto fail; dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list); return 0; fail: wpabuf_free(req->pkt); os_free(req); return -1; } #endif /* CONFIG_FILS */ static int wpas_ctrl_cmd_debug_level(const char *cmd) { if (os_strcmp(cmd, "PING") == 0 || os_strncmp(cmd, "BSS ", 4) == 0 || os_strncmp(cmd, "GET_NETWORK ", 12) == 0 || os_strncmp(cmd, "STATUS", 6) == 0 || os_strncmp(cmd, "STA ", 4) == 0 || os_strncmp(cmd, "STA-", 4) == 0) return MSG_EXCESSIVE; return MSG_DEBUG; } static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s, const char *cmd) { size_t frame_classifier_len; const char *pos, *end; struct robust_av_data *robust_av = &wpa_s->robust_av; int val; /* * format: * [up_bitmap=] [up_limit=] * [stream_timeout=] [frame_classifier=] */ os_memset(robust_av, 0, sizeof(struct robust_av_data)); if (os_strncmp(cmd, "add ", 4) == 0) { robust_av->request_type = SCS_REQ_ADD; } else if (os_strcmp(cmd, "remove") == 0) { robust_av->request_type = SCS_REQ_REMOVE; robust_av->valid_config = false; return wpas_send_mscs_req(wpa_s); } else if (os_strncmp(cmd, "change ", 7) == 0) { robust_av->request_type = SCS_REQ_CHANGE; } else { return -1; } pos = os_strstr(cmd, "up_bitmap="); if (!pos) return -1; val = hex2byte(pos + 10); if (val < 0) return -1; robust_av->up_bitmap = val; pos = os_strstr(cmd, "up_limit="); if (!pos) return -1; robust_av->up_limit = atoi(pos + 9); pos = os_strstr(cmd, "stream_timeout="); if (!pos) return -1; robust_av->stream_timeout = atoi(pos + 15); if (robust_av->stream_timeout == 0) return -1; pos = os_strstr(cmd, "frame_classifier="); if (!pos) return -1; pos += 17; end = os_strchr(pos, ' '); if (!end) end = pos + os_strlen(pos); frame_classifier_len = (end - pos) / 2; if (frame_classifier_len > sizeof(robust_av->frame_classifier) || hexstr2bin(pos, robust_av->frame_classifier, frame_classifier_len)) return -1; robust_av->frame_classifier_len = frame_classifier_len; robust_av->valid_config = true; return wpas_send_mscs_req(wpa_s); } #ifdef CONFIG_PASN static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd) { char *token, *context = NULL; u8 bssid[ETH_ALEN]; int akmp = -1, cipher = -1, got_bssid = 0; u16 group = 0xFFFF; u8 *comeback = NULL; size_t comeback_len = 0; int id = 0, ret = -1; /* * Entry format: bssid= akmp= cipher= group= * [comeback=] */ while ((token = str_token(cmd, " ", &context))) { if (os_strncmp(token, "bssid=", 6) == 0) { if (hwaddr_aton(token + 6, bssid)) goto out; got_bssid = 1; } else if (os_strcmp(token, "akmp=PASN") == 0) { akmp = WPA_KEY_MGMT_PASN; #ifdef CONFIG_IEEE80211R } else if (os_strcmp(token, "akmp=FT-PSK") == 0) { akmp = WPA_KEY_MGMT_FT_PSK; } else if (os_strcmp(token, "akmp=FT-EAP-SHA384") == 0) { akmp = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; } else if (os_strcmp(token, "akmp=FT-EAP") == 0) { akmp = WPA_KEY_MGMT_FT_IEEE8021X; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE } else if (os_strcmp(token, "akmp=SAE") == 0) { akmp = WPA_KEY_MGMT_SAE; #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS } else if (os_strcmp(token, "akmp=FILS-SHA256") == 0) { akmp = WPA_KEY_MGMT_FILS_SHA256; } else if (os_strcmp(token, "akmp=FILS-SHA384") == 0) { akmp = WPA_KEY_MGMT_FILS_SHA384; #endif /* CONFIG_FILS */ } else if (os_strcmp(token, "cipher=CCMP-256") == 0) { cipher = WPA_CIPHER_CCMP_256; } else if (os_strcmp(token, "cipher=GCMP-256") == 0) { cipher = WPA_CIPHER_GCMP_256; } else if (os_strcmp(token, "cipher=CCMP") == 0) { cipher = WPA_CIPHER_CCMP; } else if (os_strcmp(token, "cipher=GCMP") == 0) { cipher = WPA_CIPHER_GCMP; } else if (os_strncmp(token, "group=", 6) == 0) { group = atoi(token + 6); } else if (os_strncmp(token, "nid=", 4) == 0) { id = atoi(token + 4); } else if (os_strncmp(token, "comeback=", 9) == 0) { comeback_len = os_strlen(token + 9); if (comeback || !comeback_len || comeback_len % 2) goto out; comeback_len /= 2; comeback = os_malloc(comeback_len); if (!comeback || hexstr2bin(token + 9, comeback, comeback_len)) goto out; } else { wpa_printf(MSG_DEBUG, "CTRL: PASN Invalid parameter: '%s'", token); goto out; } } if (!got_bssid || akmp == -1 || cipher == -1 || group == 0xFFFF) { wpa_printf(MSG_DEBUG,"CTRL: PASN missing parameter"); goto out; } ret = wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id, comeback, comeback_len); out: os_free(comeback); return ret; } static int wpas_ctrl_iface_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const char *cmd) { u8 bssid[ETH_ALEN]; if (os_strncmp(cmd, "bssid=", 6) != 0 || hwaddr_aton(cmd + 6, bssid)) { wpa_printf(MSG_DEBUG, "CTRL: PASN_DEAUTH without valid BSSID"); return -1; } return wpas_pasn_deauthenticate(wpa_s, bssid); } #endif /* CONFIG_PASN */ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { char *reply; const int reply_size = 4096; int reply_len; if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || os_strncmp(buf, "SET_NETWORK ", 12) == 0 || os_strncmp(buf, "PMKSA_ADD ", 10) == 0 || os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) { if (wpa_debug_show_keys) wpa_dbg(wpa_s, MSG_DEBUG, "Control interface command '%s'", buf); else wpa_dbg(wpa_s, MSG_DEBUG, "Control interface command '%s [REMOVED]'", os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ? WPA_CTRL_RSP : (os_strncmp(buf, "SET_NETWORK ", 12) == 0 ? "SET_NETWORK" : "key-add")); } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 || os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) { wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); } else { int level = wpas_ctrl_cmd_debug_level(buf); wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } reply = os_malloc(reply_size); if (reply == NULL) { *resp_len = 1; return NULL; } os_memcpy(reply, "OK\n", 3); reply_len = 3; if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; } else if (os_strcmp(buf, "IFNAME") == 0) { reply_len = os_strlen(wpa_s->ifname); os_memcpy(reply, wpa_s->ifname, reply_len); } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; } else if (os_strncmp(buf, "NOTE ", 5) == 0) { wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); } else if (os_strcmp(buf, "MIB") == 0) { reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { reply_len += eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, reply_size - reply_len); #ifdef CONFIG_MACSEC reply_len += ieee802_1x_kay_get_mib( wpa_s->kay, reply + reply_len, reply_size - reply_len); #endif /* CONFIG_MACSEC */ } } else if (os_strncmp(buf, "STATUS", 6) == 0) { reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); } else if (os_strcmp(buf, "PMKSA") == 0) { reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { wpas_ctrl_iface_pmksa_flush(wpa_s); #ifdef CONFIG_PMKSA_CACHE_EXTERNAL } else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) { reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10, reply, reply_size); } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0) reply_len = -1; #ifdef CONFIG_MESH } else if (os_strncmp(buf, "MESH_PMKSA_GET ", 15) == 0) { reply_len = wpas_ctrl_iface_mesh_pmksa_get(wpa_s, buf + 15, reply, reply_size); } else if (os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) { if (wpas_ctrl_iface_mesh_pmksa_add(wpa_s, buf + 15) < 0) reply_len = -1; #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; } else if (os_strncmp(buf, "DUMP", 4) == 0) { reply_len = wpa_config_dump_values(wpa_s->conf, reply, reply_size); } else if (os_strncmp(buf, "GET ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4, reply, reply_size); } else if (os_strcmp(buf, "LOGON") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, false); } else if (os_strcmp(buf, "LOGOFF") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, true); } else if (os_strcmp(buf, "REASSOCIATE") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; else wpas_request_connection(wpa_s); } else if (os_strcmp(buf, "REATTACH") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED || !wpa_s->current_ssid) reply_len = -1; else { wpa_s->reattach = 1; wpas_request_connection(wpa_s); } } else if (os_strcmp(buf, "RECONNECT") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; else if (wpa_s->disconnected) wpas_request_connection(wpa_s); #ifdef IEEE8021X_EAPOL } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) reply_len = -1; #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_IEEE80211R } else if (os_strncmp(buf, "FT_DS ", 6) == 0) { if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) reply_len = -1; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS } else if (os_strcmp(buf, "WPS_PBC") == 0) { int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL); if (res == -2) { os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); reply_len = 17; } else if (res) reply_len = -1; } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8); if (res == -2) { os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); reply_len = 17; } else if (res) reply_len = -1; } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, reply, reply_size); } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_check_pin( wpa_s, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { if (wpas_wps_cancel(wpa_s)) reply_len = -1; #ifdef CONFIG_WPS_NFC } else if (os_strcmp(buf, "WPS_NFC") == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL)) reply_len = -1; } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8)) reply_len = -1; } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token( wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token( wpa_s, buf + 14, reply, reply_size); } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) { reply_len = wpas_ctrl_nfc_get_handover_req( wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { reply_len = wpas_ctrl_nfc_get_handover_sel( wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20)) reply_len = -1; #endif /* CONFIG_WPS_NFC */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) reply_len = -1; #ifdef CONFIG_AP } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin( wpa_s, buf + 11, reply, reply_size); #endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER } else if (os_strcmp(buf, "WPS_ER_START") == 0) { if (wpas_wps_er_start(wpa_s, NULL)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) { if (wpas_wps_er_start(wpa_s, buf + 13)) reply_len = -1; } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { wpas_wps_er_stop(wpa_s); } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) { int ret = wpas_wps_er_pbc(wpa_s, buf + 11); if (ret == -2) { os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); reply_len = 17; } else if (ret == -3) { os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18); reply_len = 18; } else if (ret == -4) { os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20); reply_len = 20; } else if (ret) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s, buf + 18)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14)) reply_len = -1; #ifdef CONFIG_WPS_NFC } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( wpa_s, buf + 24, reply, reply_size); #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) { if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_MESH } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) { reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( wpa_s, buf + 19, reply, reply_size); } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) { reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( wpa_s, "", reply, reply_size); } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) { if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) { if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s, buf + 18)) reply_len = -1; } else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) { if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) { if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "MESH_LINK_PROBE ", 16) == 0) { if (wpa_supplicant_ctrl_iface_mesh_link_probe(wpa_s, buf + 16)) reply_len = -1; #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { if (p2p_ctrl_find(wpa_s, buf + 8)) reply_len = -1; } else if (os_strcmp(buf, "P2P_FIND") == 0) { if (p2p_ctrl_find(wpa_s, "")) reply_len = -1; } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) { wpas_p2p_stop_find(wpa_s); } else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) { if (p2p_ctrl_asp_provision(wpa_s, buf + 18)) reply_len = -1; } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) { if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23)) reply_len = -1; } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) { reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) { if (p2p_ctrl_listen(wpa_s, buf + 11)) reply_len = -1; } else if (os_strcmp(buf, "P2P_LISTEN") == 0) { if (p2p_ctrl_listen(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) { if (wpas_p2p_group_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { if (p2p_ctrl_group_add(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_MEMBER ", 17) == 0) { reply_len = p2p_ctrl_group_member(wpa_s, buf + 17, reply, reply_size); } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) { if (p2p_ctrl_prov_disc(wpa_s, buf + 14)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) { reply_len = p2p_get_passphrase(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) { reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply, reply_size); } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) { if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) { if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) { wpas_p2p_sd_service_update(wpa_s); } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) { if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) { wpas_p2p_service_flush(wpa_s); } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) { if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) { if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) { if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) { if (p2p_ctrl_reject(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) { if (p2p_ctrl_invite(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) { reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) { if (p2p_ctrl_set(wpa_s, buf + 8) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_FLUSH") == 0) { p2p_ctrl_flush(wpa_s); } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) { if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_CANCEL") == 0) { if (wpas_p2p_cancel(wpa_s)) reply_len = -1; } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) { if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) { if (p2p_ctrl_presence_req(wpa_s, "") < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) { if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { if (p2p_ctrl_ext_listen(wpa_s, "") < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) reply_len = -1; } else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) { if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13)) reply_len = -1; } else if (os_strcmp(buf, "P2P_LO_STOP") == 0) { if (wpas_p2p_lo_stop(wpa_s)) reply_len = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) { reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16, reply, reply_size); #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "FETCH_ANQP") == 0) { if (interworking_fetch_anqp(wpa_s) < 0) reply_len = -1; } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) { interworking_stop_fetch_anqp(wpa_s); } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) { if (ctrl_interworking_select(wpa_s, NULL) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) { if (ctrl_interworking_select(wpa_s, buf + 20) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) { if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) { int id; id = ctrl_interworking_connect(wpa_s, buf + 25, 1); if (id < 0) reply_len = -1; else { reply_len = os_snprintf(reply, reply_size, "%d\n", id); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { if (get_anqp(wpa_s, buf + 9) < 0) reply_len = -1; } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) { if (gas_request(wpa_s, buf + 12) < 0) reply_len = -1; } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) { reply_len = gas_response_get(wpa_s, buf + 17, reply, reply_size); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) { if (get_hs20_anqp(wpa_s, buf + 14) < 0) reply_len = -1; } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) { if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) reply_len = -1; } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) { if (hs20_icon_request(wpa_s, buf + 18, 0) < 0) reply_len = -1; } else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) { if (hs20_icon_request(wpa_s, buf + 14, 1) < 0) reply_len = -1; } else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) { reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size); } else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) { if (del_hs20_icon(wpa_s, buf + 14) < 0) reply_len = -1; } else if (os_strcmp(buf, "FETCH_OSU") == 0) { if (hs20_fetch_osu(wpa_s, 0) < 0) reply_len = -1; } else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) { if (hs20_fetch_osu(wpa_s, 1) < 0) reply_len = -1; } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) { hs20_cancel_fetch_osu(wpa_s); #endif /* CONFIG_HS20 */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( wpa_s, buf + os_strlen(WPA_CTRL_RSP))) reply_len = -1; else { /* * Notify response from timeout to allow the control * interface response to be sent first. */ eloop_register_timeout(0, 0, wpas_ctrl_eapol_response, wpa_s, NULL); } } else if (os_strcmp(buf, "RECONFIGURE") == 0) { if (wpa_supplicant_reload_configuration(wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "TERMINATE") == 0) { wpa_supplicant_terminate_proc(wpa_s->global); } else if (os_strncmp(buf, "BSSID ", 6) == 0) { if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) reply_len = -1; } else if (os_strncmp(buf, "BSSID_IGNORE", 12) == 0) { reply_len = wpa_supplicant_ctrl_iface_bssid_ignore( wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) { /* deprecated backwards compatibility alias for BSSID_IGNORE */ reply_len = wpa_supplicant_ctrl_iface_bssid_ignore( wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { reply_len = wpa_supplicant_ctrl_iface_log_level( wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, NULL, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { wpas_request_disconnection(wpa_s); } else if (os_strcmp(buf, "SCAN") == 0) { wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); } else if (os_strncmp(buf, "SCAN ", 5) == 0) { wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len); } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); } else if (os_strcmp(buf, "ABORT_SCAN") == 0) { if (wpas_abort_ongoing_scan(wpa_s) < 0) reply_len = -1; } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) { if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16)) reply_len = -1; } else if (os_strcmp(buf, "ADD_NETWORK") == 0) { reply_len = wpa_supplicant_ctrl_iface_add_network( wpa_s, reply, reply_size); } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) { if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12)) reply_len = -1; } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12, wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "LIST_CREDS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_creds( wpa_s, reply, reply_size); } else if (os_strcmp(buf, "ADD_CRED") == 0) { reply_len = wpa_supplicant_ctrl_iface_add_cred( wpa_s, reply, reply_size); } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) { if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12)) reply_len = -1; } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) { if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9)) reply_len = -1; } else if (os_strncmp(buf, "GET_CRED ", 9) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9, reply, reply_size); #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) reply_len = -1; #endif /* CONFIG_NO_CONFIG_WRITE */ } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_capability( wpa_s, buf + 15, reply, reply_size); } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) reply_len = -1; } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) { if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14)) reply_len = -1; } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( wpa_s->global, reply, reply_size); } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { reply_len = wpa_supplicant_global_iface_interfaces( wpa_s->global, buf + 10, reply, reply_size); } else if (os_strncmp(buf, "BSS ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_bss( wpa_s, buf + 4, reply, reply_size); #ifdef CONFIG_AP } else if (os_strcmp(buf, "STA-FIRST") == 0) { reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "STA ", 4) == 0) { reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply, reply_size); } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply, reply_size); } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) { if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12)) reply_len = -1; } else if (os_strcmp(buf, "STOP_AP") == 0) { if (wpas_ap_stop_ap(wpa_s)) reply_len = -1; #endif /* CONFIG_AP */ } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(wpa_s->global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(wpa_s->global); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcmp(buf, "DROP_SA") == 0) { wpa_supplicant_ctrl_iface_drop_sa(wpa_s); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "ROAM ", 5) == 0) { if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5)) reply_len = -1; } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) { wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0; } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) { if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15)) reply_len = -1; } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) { if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) { wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10); #ifdef CONFIG_TDLS } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) { if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) { if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) { if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s, buf + 24)) reply_len = -1; } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) { reply_len = wpa_supplicant_ctrl_iface_tdls_link_status( wpa_s, buf + 17, reply, reply_size); #endif /* CONFIG_TDLS */ } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) { reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) { if (wmm_ac_ctrl_addts(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) { if (wmm_ac_ctrl_delts(wpa_s, buf + 13)) reply_len = -1; } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { reply_len = wpa_supplicant_signal_poll(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "SIGNAL_MONITOR", 14) == 0) { if (wpas_ctrl_iface_signal_monitor(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) { reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply, reply_size); #ifdef CONFIG_AUTOSCAN } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) { if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_AUTOSCAN */ } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { reply_len = wpas_ctrl_iface_driver_flags(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "DRIVER_FLAGS2") == 0) { reply_len = wpas_ctrl_iface_driver_flags2(wpa_s, reply, reply_size); #ifdef ANDROID } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, reply_size); #endif /* ANDROID */ } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply, reply_size); } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) { pmksa_cache_clear_current(wpa_s->wpa); eapol_sm_request_reauth(wpa_s->eapol); #ifdef CONFIG_WNM } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) { if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10)) reply_len = -1; } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) { if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) { if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18)) reply_len = -1; #endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "FLUSH") == 0) { wpa_supplicant_ctrl_iface_flush(wpa_s); } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) { reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply, reply_size); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0) reply_len = -1; } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) { wpas_ctrl_iface_mgmt_tx_done(wpa_s); } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { if (wpas_ctrl_iface_mgmt_rx_process(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) { if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0) reply_len = -1; } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0) reply_len = -1; } else if (os_strncmp(buf, "EAPOL_TX ", 9) == 0) { if (wpas_ctrl_iface_eapol_tx(wpa_s, buf + 9) < 0) reply_len = -1; } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) { if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0) reply_len = -1; } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) { if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0) reply_len = -1; } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) { if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) { if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0) reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) { if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) { if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0) reply_len = -1; } else if (os_strcmp(buf, "RESET_PN") == 0) { if (wpas_ctrl_reset_pn(wpa_s) < 0) reply_len = -1; } else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) { if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0) reply_len = -1; } else if (os_strcmp(buf, "RESEND_ASSOC") == 0) { if (wpas_ctrl_resend_assoc(wpa_s) < 0) reply_len = -1; } else if (os_strcmp(buf, "UNPROT_DEAUTH") == 0) { sme_event_unprot_disconnect( wpa_s, wpa_s->bssid, NULL, WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); } else if (os_strncmp(buf, "TWT_SETUP ", 10) == 0) { if (wpas_ctrl_iface_send_twt_setup(wpa_s, buf + 9)) reply_len = -1; } else if (os_strcmp(buf, "TWT_SETUP") == 0) { if (wpas_ctrl_iface_send_twt_setup(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "TWT_TEARDOWN ", 13) == 0) { if (wpas_ctrl_iface_send_twt_teardown(wpa_s, buf + 12)) reply_len = -1; } else if (os_strcmp(buf, "TWT_TEARDOWN") == 0) { if (wpas_ctrl_iface_send_twt_teardown(wpa_s, "")) reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) reply_len = -1; } else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) { reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply, reply_size); } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) { if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0) reply_len = -1; } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) { if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20)) reply_len = -1; } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { wpas_ctrl_iface_erp_flush(wpa_s); } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) { reply_len = wpas_ctrl_iface_get_pref_freq_list( wpa_s, buf + 19, reply, reply_size); #ifdef CONFIG_FILS } else if (os_strncmp(buf, "FILS_HLP_REQ_ADD ", 17) == 0) { if (wpas_ctrl_iface_fils_hlp_req_add(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) { wpas_flush_fils_hlp_req(wpa_s); #endif /* CONFIG_FILS */ #ifdef CONFIG_DPP } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { int res; res = wpas_dpp_qr_code(wpa_s, buf + 12); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_NFC_URI ", 12) == 0) { int res; res = wpas_dpp_nfc_uri(wpa_s, buf + 12); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_NFC_HANDOVER_REQ ", 21) == 0) { int res; res = wpas_dpp_nfc_handover_req(wpa_s, buf + 20); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_NFC_HANDOVER_SEL ", 21) == 0) { int res; res = wpas_dpp_nfc_handover_sel(wpa_s, buf + 20); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { int res; res = dpp_bootstrap_gen(wpa_s->dpp, buf + 18); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { if (dpp_bootstrap_remove(wpa_s->dpp, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { const char *uri; uri = dpp_bootstrap_get_uri(wpa_s->dpp, atoi(buf + 22)); if (!uri) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%s", uri); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { reply_len = dpp_bootstrap_info(wpa_s->dpp, atoi(buf + 19), reply, reply_size); } else if (os_strncmp(buf, "DPP_BOOTSTRAP_SET ", 18) == 0) { if (dpp_bootstrap_set(wpa_s->dpp, atoi(buf + 18), os_strchr(buf + 18, ' ')) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { if (wpas_dpp_listen(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { wpas_dpp_stop(wpa_s); wpas_dpp_listen_stop(wpa_s); } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { int res; res = dpp_configurator_add(wpa_s->dpp, buf + 20); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { if (dpp_configurator_remove(wpa_s->dpp, buf + 24) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { if (wpas_dpp_configurator_sign(wpa_s, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { reply_len = dpp_configurator_get_key_id(wpa_s->dpp, atoi(buf + 25), reply, reply_size); } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { int res; res = wpas_dpp_pkex_add(wpa_s, buf + 12); if (res < 0) { reply_len = -1; } else { reply_len = os_snprintf(reply, reply_size, "%d", res); if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0) reply_len = -1; #ifdef CONFIG_DPP2 } else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) { if (wpas_dpp_controller_start(wpa_s, buf + 20) < 0) reply_len = -1; } else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) { if (wpas_dpp_controller_start(wpa_s, NULL) < 0) reply_len = -1; } else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) { dpp_controller_stop(wpa_s->dpp); } else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) { if (wpas_dpp_chirp(wpa_s, buf + 9) < 0) reply_len = -1; } else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) { wpas_dpp_chirp_stop(wpa_s); } else if (os_strncmp(buf, "DPP_RECONFIG ", 13) == 0) { if (wpas_dpp_reconfig(wpa_s, buf + 13) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CA_SET ", 11) == 0) { if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0) reply_len = -1; #endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ } else if (os_strncmp(buf, "MSCS ", 5) == 0) { if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5)) reply_len = -1; #ifdef CONFIG_PASN } else if (os_strncmp(buf, "PASN_START ", 11) == 0) { if (wpas_ctrl_iface_pasn_start(wpa_s, buf + 11) < 0) reply_len = -1; } else if (os_strcmp(buf, "PASN_STOP") == 0) { wpas_pasn_auth_stop(wpa_s); } else if (os_strcmp(buf, "PTKSA_CACHE_LIST") == 0) { reply_len = ptksa_cache_list(wpa_s->ptksa, reply, reply_size); } else if (os_strncmp(buf, "PASN_DEAUTH ", 12) == 0) { if (wpas_ctrl_iface_pasn_deauthenticate(wpa_s, buf + 12) < 0) reply_len = -1; #endif /* CONFIG_PASN */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; } if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } *resp_len = reply_len; return reply; } static int wpa_supplicant_global_iface_add(struct wpa_global *global, char *cmd) { struct wpa_interface iface; char *pos, *extra; struct wpa_supplicant *wpa_s; unsigned int create_iface = 0; u8 mac_addr[ETH_ALEN]; enum wpa_driver_if_type type = WPA_IF_STATION; /* * TABTABTABTAB * TAB[TAB[TAB]] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); os_memset(&iface, 0, sizeof(iface)); do { iface.ifname = pos = cmd; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.ifname[0] == '\0') return -1; if (pos == NULL) break; iface.confname = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.confname[0] == '\0') iface.confname = NULL; if (pos == NULL) break; iface.driver = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.driver[0] == '\0') iface.driver = NULL; if (pos == NULL) break; iface.ctrl_interface = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.ctrl_interface[0] == '\0') iface.ctrl_interface = NULL; if (pos == NULL) break; iface.driver_param = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.driver_param[0] == '\0') iface.driver_param = NULL; if (pos == NULL) break; iface.bridge_ifname = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (iface.bridge_ifname[0] == '\0') iface.bridge_ifname = NULL; if (pos == NULL) break; extra = pos; pos = os_strchr(pos, '\t'); if (pos) *pos++ = '\0'; if (!extra[0]) break; if (os_strcmp(extra, "create") == 0) { create_iface = 1; if (!pos) break; if (os_strcmp(pos, "sta") == 0) { type = WPA_IF_STATION; } else if (os_strcmp(pos, "ap") == 0) { type = WPA_IF_AP_BSS; } else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported interface type: '%s'", pos); return -1; } } else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported extra parameter: '%s'", extra); return -1; } } while (0); if (create_iface) { wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'", iface.ifname); if (!global->ifaces) return -1; if (wpa_drv_if_add(global->ifaces, type, iface.ifname, NULL, NULL, NULL, mac_addr, NULL) < 0) { wpa_printf(MSG_ERROR, "CTRL_IFACE interface creation failed"); return -1; } wpa_printf(MSG_DEBUG, "CTRL_IFACE interface '%s' created with MAC addr: " MACSTR, iface.ifname, MAC2STR(mac_addr)); } if (wpa_supplicant_get_iface(global, iface.ifname)) goto fail; wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); if (!wpa_s) goto fail; wpa_s->added_vif = create_iface; return 0; fail: if (create_iface) wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname); return -1; } static int wpa_supplicant_global_iface_remove(struct wpa_global *global, char *cmd) { struct wpa_supplicant *wpa_s; int ret; unsigned int delete_iface; wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); wpa_s = wpa_supplicant_get_iface(global, cmd); if (wpa_s == NULL) return -1; delete_iface = wpa_s->added_vif; ret = wpa_supplicant_remove_iface(global, wpa_s, 0); if (!ret && delete_iface) { wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'", cmd); ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd); } return ret; } static void wpa_free_iface_info(struct wpa_interface_info *iface) { struct wpa_interface_info *prev; while (iface) { prev = iface; iface = iface->next; os_free(prev->ifname); os_free(prev->desc); os_free(prev); } } static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len) { int i, res; struct wpa_interface_info *iface = NULL, *last = NULL, *tmp; char *pos, *end; for (i = 0; wpa_drivers[i]; i++) { const struct wpa_driver_ops *drv = wpa_drivers[i]; if (drv->get_interfaces == NULL) continue; tmp = drv->get_interfaces(global->drv_priv[i]); if (tmp == NULL) continue; if (last == NULL) iface = last = tmp; else last->next = tmp; while (last->next) last = last->next; } pos = buf; end = buf + len; for (tmp = iface; tmp; tmp = tmp->next) { res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", tmp->drv_name, tmp->ifname, tmp->desc ? tmp->desc : ""); if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } pos += res; } wpa_free_iface_info(iface); return pos - buf; } static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, const char *input, char *buf, int len) { int res; char *pos, *end; struct wpa_supplicant *wpa_s; int show_ctrl = 0; if (input) show_ctrl = !!os_strstr(input, "ctrl"); wpa_s = global->ifaces; pos = buf; end = buf + len; while (wpa_s) { if (show_ctrl) res = os_snprintf(pos, end - pos, "%s ctrl_iface=%s\n", wpa_s->ifname, wpa_s->conf->ctrl_interface ? wpa_s->conf->ctrl_interface : "N/A"); else res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } pos += res; wpa_s = wpa_s->next; } return pos - buf; } static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global, const char *ifname, char *cmd, size_t *resp_len) { struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(ifname, wpa_s->ifname) == 0) break; } if (wpa_s == NULL) { char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n"); if (resp) *resp_len = os_strlen(resp); else *resp_len = 1; return resp; } return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len); } static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, char *buf, size_t *resp_len) { #ifdef CONFIG_P2P static const char * cmd[] = { "LIST_NETWORKS", "P2P_FIND", "P2P_STOP_FIND", "P2P_LISTEN", "P2P_GROUP_ADD", "P2P_GET_PASSPHRASE", "P2P_SERVICE_UPDATE", "P2P_SERVICE_FLUSH", "P2P_FLUSH", "P2P_CANCEL", "P2P_PRESENCE_REQ", "P2P_EXT_LISTEN", #ifdef CONFIG_AP "STA-FIRST", #endif /* CONFIG_AP */ NULL }; static const char * prefix[] = { #ifdef ANDROID "DRIVER ", #endif /* ANDROID */ "GET_CAPABILITY ", "GET_NETWORK ", "REMOVE_NETWORK ", "P2P_FIND ", "P2P_CONNECT ", "P2P_LISTEN ", "P2P_GROUP_REMOVE ", "P2P_GROUP_ADD ", "P2P_GROUP_MEMBER ", "P2P_PROV_DISC ", "P2P_SERV_DISC_REQ ", "P2P_SERV_DISC_CANCEL_REQ ", "P2P_SERV_DISC_RESP ", "P2P_SERV_DISC_EXTERNAL ", "P2P_SERVICE_ADD ", "P2P_SERVICE_DEL ", "P2P_SERVICE_REP ", "P2P_REJECT ", "P2P_INVITE ", "P2P_PEER ", "P2P_SET ", "P2P_UNAUTHORIZE ", "P2P_PRESENCE_REQ ", "P2P_EXT_LISTEN ", "P2P_REMOVE_CLIENT ", "WPS_NFC_TOKEN ", "WPS_NFC_TAG_READ ", "NFC_GET_HANDOVER_SEL ", "NFC_GET_HANDOVER_REQ ", "NFC_REPORT_HANDOVER ", "P2P_ASP_PROVISION ", "P2P_ASP_PROVISION_RESP ", #ifdef CONFIG_AP "STA ", "STA-NEXT ", #endif /* CONFIG_AP */ NULL }; int found = 0; int i; if (global->p2p_init_wpa_s == NULL) return NULL; for (i = 0; !found && cmd[i]; i++) { if (os_strcmp(buf, cmd[i]) == 0) found = 1; } for (i = 0; !found && prefix[i]; i++) { if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0) found = 1; } if (found) return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, buf, resp_len); #endif /* CONFIG_P2P */ return NULL; } static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global, char *buf, size_t *resp_len) { #ifdef CONFIG_WIFI_DISPLAY if (global->p2p_init_wpa_s == NULL) return NULL; if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 || os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, buf, resp_len); #endif /* CONFIG_WIFI_DISPLAY */ return NULL; } static char * wpas_global_ctrl_iface_redir(struct wpa_global *global, char *buf, size_t *resp_len) { char *ret; ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len); if (ret) return ret; ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len); if (ret) return ret; return NULL; } static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd) { char *value; value = os_strchr(cmd, ' '); if (value == NULL) return -1; *value++ = '\0'; wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value); #ifdef CONFIG_WIFI_DISPLAY if (os_strcasecmp(cmd, "wifi_display") == 0) { wifi_display_enable(global, !!atoi(value)); return 0; } #endif /* CONFIG_WIFI_DISPLAY */ /* Restore cmd to its original value to allow redirection */ value[-1] = ' '; return -1; } static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global, char *cmd) { struct wpa_supplicant *wpa_s[2]; /* src, dst */ char *p; unsigned int i; /* cmd: " * */ for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) { p = os_strchr(cmd, ' '); if (p == NULL) return -1; *p = '\0'; wpa_s[i] = global->ifaces; for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) { if (os_strcmp(cmd, wpa_s[i]->ifname) == 0) break; } if (!wpa_s[i]) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find iface=%s", cmd); return -1; } cmd = p + 1; } return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]); } #ifndef CONFIG_NO_CONFIG_WRITE static int wpas_global_ctrl_iface_save_config(struct wpa_global *global) { int ret = 0, saved = 0; struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (!wpa_s->conf->update_config) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)"); continue; } if (wpa_config_write(wpa_s->confname, wpa_s->conf)) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration"); ret = 1; } else { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated"); saved++; } } if (!saved && !ret) { wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated"); ret = 1; } return ret; } #endif /* CONFIG_NO_CONFIG_WRITE */ static int wpas_global_ctrl_iface_status(struct wpa_global *global, char *buf, size_t buflen) { char *pos, *end; int ret; struct wpa_supplicant *wpa_s; pos = buf; end = buf + buflen; #ifdef CONFIG_P2P if (global->p2p && !global->p2p_disabled) { ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR "\n" "p2p_state=%s\n", MAC2STR(global->p2p_dev_addr), p2p_get_state_txt(global->p2p)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } else if (global->p2p) { ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n"); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY ret = os_snprintf(pos, end - pos, "wifi_display=%d\n", !!global->wifi_display); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #endif /* CONFIG_WIFI_DISPLAY */ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { ret = os_snprintf(pos, end - pos, "ifname=%s\n" "address=" MACSTR "\n", wpa_s->ifname, MAC2STR(wpa_s->own_addr)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } return pos - buf; } #ifdef CONFIG_FST static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global, char *cmd, char *buf, size_t reply_size) { char ifname[IFNAMSIZ + 1]; struct fst_iface_cfg cfg; struct wpa_supplicant *wpa_s; struct fst_wpa_obj iface_obj; if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { wpa_s = wpa_supplicant_get_iface(global, ifname); if (wpa_s) { if (wpa_s->fst) { wpa_printf(MSG_INFO, "FST: Already attached"); return -1; } fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); wpa_s->fst = fst_attach(ifname, wpa_s->own_addr, &iface_obj, &cfg); if (wpa_s->fst) return os_snprintf(buf, reply_size, "OK\n"); } } return -1; } static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global, char *cmd, char *buf, size_t reply_size) { char ifname[IFNAMSIZ + 1]; struct wpa_supplicant *wpa_s; if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { wpa_s = wpa_supplicant_get_iface(global, ifname); if (wpa_s) { if (!fst_iface_detach(ifname)) { wpa_s->fst = NULL; return os_snprintf(buf, reply_size, "OK\n"); } } } return -1; } #endif /* CONFIG_FST */ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *buf, size_t *resp_len) { char *reply; const int reply_size = 2048; int reply_len; int level = MSG_DEBUG; if (os_strncmp(buf, "IFNAME=", 7) == 0) { char *pos = os_strchr(buf + 7, ' '); if (pos) { *pos++ = '\0'; return wpas_global_ctrl_iface_ifname(global, buf + 7, pos, resp_len); } } reply = wpas_global_ctrl_iface_redir(global, buf, resp_len); if (reply) return reply; if (os_strcmp(buf, "PING") == 0) level = MSG_EXCESSIVE; wpa_hexdump_ascii(level, "RX global ctrl_iface", (const u8 *) buf, os_strlen(buf)); reply = os_malloc(reply_size); if (reply == NULL) { *resp_len = 1; return NULL; } os_memcpy(reply, "OK\n", 3); reply_len = 3; if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) { if (wpa_supplicant_global_iface_add(global, buf + 14)) reply_len = -1; } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) { if (wpa_supplicant_global_iface_remove(global, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( global, reply, reply_size); } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { reply_len = wpa_supplicant_global_iface_interfaces( global, buf + 10, reply, reply_size); #ifdef CONFIG_FST } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11, reply, reply_size); } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11, reply, reply_size); } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); #endif /* CONFIG_FST */ } else if (os_strcmp(buf, "TERMINATE") == 0) { wpa_supplicant_terminate_proc(global); } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(global); } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpas_global_ctrl_iface_set(global, buf + 4)) { #ifdef CONFIG_P2P if (global->p2p_init_wpa_s) { os_free(reply); /* Check if P2P redirection would work for this * command. */ return wpa_supplicant_ctrl_iface_process( global->p2p_init_wpa_s, buf, resp_len); } #endif /* CONFIG_P2P */ reply_len = -1; } } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { if (wpas_global_ctrl_iface_dup_network(global, buf + 12)) reply_len = -1; #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpas_global_ctrl_iface_save_config(global)) reply_len = -1; #endif /* CONFIG_NO_CONFIG_WRITE */ } else if (os_strcmp(buf, "STATUS") == 0) { reply_len = wpas_global_ctrl_iface_status(global, reply, reply_size); #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { if (wpas_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; } if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } *resp_len = reply_len; return reply; } diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 7a6567330407..565ced0fd7e2 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -1,3103 +1,3104 @@ /* * WPA Supplicant / dbus-based control interface (P2P) * Copyright (c) 2011-2012, Intel Corporation * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "utils/includes.h" #include "common.h" #include "../config.h" #include "../wpa_supplicant_i.h" #include "../wps_supplicant.h" #include "../notify.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" #include "dbus_new_handlers_p2p.h" #include "dbus_dict_helpers.h" #include "p2p/p2p.h" #include "common/ieee802_11_defs.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/wps_hostapd.h" #include "../p2p_supplicant.h" #include "../wifi_display.h" static int wpas_dbus_validate_dbus_ipaddr(struct wpa_dbus_dict_entry entry) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE || entry.array_len != 4) return 0; return 1; } static dbus_bool_t no_p2p_mgmt_interface(DBusError *error) { dbus_set_error_const(error, WPAS_DBUS_ERROR_IFACE_UNKNOWN, "Could not find P2P mgmt interface"); return FALSE; } /** * Parses out the mac address from the peer object path. * @peer_path - object path of the form * /fi/w1/wpa_supplicant1/Interfaces/n/Peers/00112233445566 (no colons) * @addr - out param must be of ETH_ALEN size * Returns 0 if valid (including MAC), -1 otherwise */ static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN]) { const char *p; if (!peer_path) return -1; p = os_strrchr(peer_path, '/'); if (!p) return -1; p++; return hwaddr_compact_aton(p, addr); } /** * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown * error message * @message: Pointer to incoming dbus message this error refers to * Returns: a dbus error message * * Convenience function to create and return an invalid persistent group error. */ static DBusMessage * wpas_dbus_error_persistent_group_unknown(DBusMessage *message) { return dbus_message_new_error( message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, "There is no such persistent group in this P2P device."); } /** * wpas_dbus_error_no_p2p_mgmt_iface - Return a new InterfaceUnknown error * message * @message: Pointer to incoming dbus message this error refers to * Returns: a dbus error message * * Convenience function to create and return an unknown interface error. */ static DBusMessage * wpas_dbus_error_no_p2p_mgmt_iface(DBusMessage *message) { wpa_printf(MSG_DEBUG, "dbus: Could not find P2P mgmt interface"); return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN, "Could not find P2P mgmt interface"); } DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, struct wpa_supplicant *wpa_s) { struct wpa_dbus_dict_entry entry; DBusMessage *reply = NULL; DBusMessageIter iter; DBusMessageIter iter_dict; unsigned int timeout = 0; enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; int num_req_dev_types = 0; unsigned int i; u8 *req_dev_types = NULL; unsigned int freq = 0; dbus_message_iter_init(message, &iter); entry.key = NULL; if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "Timeout") == 0 && entry.type == DBUS_TYPE_INT32) { timeout = entry.uint32_value; } else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != WPAS_DBUS_TYPE_BINARRAY) goto error_clear; os_free(req_dev_types); req_dev_types = os_malloc(WPS_DEV_TYPE_LEN * entry.array_len); if (!req_dev_types) goto error_clear; for (i = 0; i < entry.array_len; i++) { if (wpabuf_len(entry.binarray_value[i]) != WPS_DEV_TYPE_LEN) goto error_clear; os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN, wpabuf_head(entry.binarray_value[i]), WPS_DEV_TYPE_LEN); } num_req_dev_types = entry.array_len; } else if (os_strcmp(entry.key, "DiscoveryType") == 0 && entry.type == DBUS_TYPE_STRING) { if (os_strcmp(entry.str_value, "start_with_full") == 0) type = P2P_FIND_START_WITH_FULL; else if (os_strcmp(entry.str_value, "social") == 0) type = P2P_FIND_ONLY_SOCIAL; else if (os_strcmp(entry.str_value, "progressive") == 0) type = P2P_FIND_PROGRESSIVE; else goto error_clear; } else if (os_strcmp(entry.key, "freq") == 0 && (entry.type == DBUS_TYPE_INT32 || entry.type == DBUS_TYPE_UINT32)) { freq = entry.uint32_value; } else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) { reply = wpas_dbus_error_no_p2p_mgmt_iface(message); goto error_nop2p; } if (wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, - req_dev_types, NULL, 0, 0, NULL, freq)) + req_dev_types, NULL, 0, 0, NULL, freq, false)) reply = wpas_dbus_error_unknown_error( message, "Could not start P2P find"); os_free(req_dev_types); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: reply = wpas_dbus_error_invalid_args(message, entry.key); error_nop2p: os_free(req_dev_types); return reply; } DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, struct wpa_supplicant *wpa_s) { wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpa_s) wpas_p2p_stop_find(wpa_s); return NULL; } DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter; char *peer_object_path = NULL; u8 peer_addr[ETH_ALEN]; dbus_message_iter_init(message, &iter); dbus_message_iter_get_basic(&iter, &peer_object_path); if (parse_peer_object_path(peer_object_path, peer_addr) < 0) return wpas_dbus_error_invalid_args(message, NULL); wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return wpas_dbus_error_no_p2p_mgmt_iface(message); if (wpas_p2p_reject(wpa_s, peer_addr) < 0) return wpas_dbus_error_unknown_error(message, "Failed to call wpas_p2p_reject method."); return NULL; } DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message, struct wpa_supplicant *wpa_s) { dbus_int32_t timeout = 0; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout, DBUS_TYPE_INVALID)) return wpas_dbus_error_no_memory(message); wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return wpas_dbus_error_no_p2p_mgmt_iface(message); if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) { return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR, "Could not start P2P listen"); } return NULL; } DBusMessage * wpas_dbus_handler_p2p_extendedlisten( DBusMessage *message, struct wpa_supplicant *wpa_s) { unsigned int period = 0, interval = 0; struct wpa_dbus_dict_entry entry; DBusMessageIter iter; DBusMessageIter iter_dict; dbus_message_iter_init(message, &iter); entry.key = NULL; if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "period") == 0 && entry.type == DBUS_TYPE_INT32) period = entry.uint32_value; else if (os_strcmp(entry.key, "interval") == 0 && entry.type == DBUS_TYPE_INT32) interval = entry.uint32_value; else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return wpas_dbus_error_no_p2p_mgmt_iface(message); if (wpas_p2p_ext_listen(wpa_s, period, interval)) return wpas_dbus_error_unknown_error( message, "failed to initiate a p2p_ext_listen."); return NULL; error_clear: wpa_dbus_dict_entry_clear(&entry); error: return wpas_dbus_error_invalid_args(message, entry.key); } DBusMessage * wpas_dbus_handler_p2p_presence_request( DBusMessage *message, struct wpa_supplicant *wpa_s) { unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0; struct wpa_dbus_dict_entry entry; DBusMessageIter iter; DBusMessageIter iter_dict; dbus_message_iter_init(message, &iter); entry.key = NULL; if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "duration1") == 0 && entry.type == DBUS_TYPE_INT32) dur1 = entry.uint32_value; else if (os_strcmp(entry.key, "interval1") == 0 && entry.type == DBUS_TYPE_INT32) int1 = entry.uint32_value; else if (os_strcmp(entry.key, "duration2") == 0 && entry.type == DBUS_TYPE_INT32) dur2 = entry.uint32_value; else if (os_strcmp(entry.key, "interval2") == 0 && entry.type == DBUS_TYPE_INT32) int2 = entry.uint32_value; else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0) return wpas_dbus_error_unknown_error(message, "Failed to invoke presence request."); return NULL; error_clear: wpa_dbus_dict_entry_clear(&entry); error: return wpas_dbus_error_invalid_args(message, entry.key); } DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; char *pg_object_path = NULL; int persistent_group = 0; int freq = 0; char *iface = NULL; unsigned int group_id = 0; struct wpa_ssid *ssid; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto inv_args; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto inv_args; if (os_strcmp(entry.key, "persistent") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { persistent_group = entry.bool_value; } else if (os_strcmp(entry.key, "frequency") == 0 && entry.type == DBUS_TYPE_INT32) { freq = entry.int32_value; if (freq <= 0) goto inv_args_clear; } else if (os_strcmp(entry.key, "persistent_group_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) pg_object_path = os_strdup(entry.str_value); else goto inv_args_clear; wpa_dbus_dict_entry_clear(&entry); } wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) { reply = wpas_dbus_error_no_p2p_mgmt_iface(message); goto out; } if (pg_object_path != NULL) { char *net_id_str; /* * A persistent group Object Path is defined meaning we want * to re-invoke a persistent group. */ iface = wpas_dbus_new_decompose_object_path( pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &net_id_str); if (iface == NULL || net_id_str == NULL || !wpa_s->parent->dbus_new_path || os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); goto out; } group_id = strtoul(net_id_str, NULL, 10); if (errno == EINVAL) { reply = wpas_dbus_error_invalid_args( message, pg_object_path); goto out; } /* Get the SSID structure from the persistent group id */ ssid = wpa_config_get_network(wpa_s->conf, group_id); if (ssid == NULL || ssid->disabled != 2) goto inv_args; if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0, - 0, 0, 0, 0, NULL, 0, 0)) { + 0, 0, 0, 0, NULL, 0, 0, + false)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0, - 0, 0, 0)) + 0, 0, 0, false)) goto inv_args; out: os_free(pg_object_path); os_free(iface); return reply; inv_args_clear: wpa_dbus_dict_entry_clear(&entry); inv_args: reply = wpas_dbus_error_invalid_args(message, NULL); goto out; } DBusMessage * wpas_dbus_handler_p2p_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s) { if (wpas_p2p_disconnect(wpa_s)) return wpas_dbus_error_unknown_error(message, "failed to disconnect"); return NULL; } static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s, DBusMessage *message, DBusMessage **out_reply, DBusError *error) { /* Return an error message or an error if P2P isn't available */ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) { if (out_reply) { *out_reply = dbus_message_new_error( message, DBUS_ERROR_FAILED, "P2P is not available for this interface"); } dbus_set_error_const(error, DBUS_ERROR_FAILED, "P2P is not available for this interface"); return FALSE; } if (!wpa_s->global->p2p_init_wpa_s) { if (out_reply) *out_reply = wpas_dbus_error_no_p2p_mgmt_iface( message); return no_p2p_mgmt_interface(error); } return TRUE; } DBusMessage * wpas_dbus_handler_p2p_remove_client(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; char *peer_object_path = NULL; char *interface_addr = NULL; u8 peer_addr[ETH_ALEN]; if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto err; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto err; if (os_strcmp(entry.key, "peer") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { os_free(peer_object_path); peer_object_path = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); } else if (os_strcmp(entry.key, "iface") == 0 && entry.type == DBUS_TYPE_STRING) { os_free(interface_addr); interface_addr = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); } else { wpa_dbus_dict_entry_clear(&entry); goto err; } } if ((!peer_object_path && !interface_addr) || (peer_object_path && (parse_peer_object_path(peer_object_path, peer_addr) < 0 || !p2p_peer_known(wpa_s->global->p2p, peer_addr))) || (interface_addr && hwaddr_aton(interface_addr, peer_addr) < 0)) goto err; wpas_p2p_remove_client(wpa_s, peer_addr, interface_addr != NULL); reply = NULL; out: os_free(peer_object_path); os_free(interface_addr); return reply; err: reply = wpas_dbus_error_invalid_args(message, "Invalid address format"); goto out; } DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; wpa_s = wpa_s->global->p2p_init_wpa_s; wpas_p2p_stop_find(wpa_s); os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; p2p_flush(wpa_s->global->p2p); return NULL; } DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; char *peer_object_path = NULL; int persistent_group = 0; int join = 0; int authorize_only = 0; int go_intent = -1; int freq = 0; u8 addr[ETH_ALEN]; char *pin = NULL; enum p2p_wps_method wps_method = WPS_NOT_READY; int new_pin; char *err_msg = NULL; char *iface = NULL; int ret; if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto inv_args; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto inv_args; if (os_strcmp(entry.key, "peer") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); } else if (os_strcmp(entry.key, "persistent") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { persistent_group = entry.bool_value; } else if (os_strcmp(entry.key, "join") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { join = entry.bool_value; } else if (os_strcmp(entry.key, "authorize_only") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { authorize_only = entry.bool_value; } else if (os_strcmp(entry.key, "frequency") == 0 && entry.type == DBUS_TYPE_INT32) { freq = entry.int32_value; if (freq <= 0) goto inv_args_clear; } else if (os_strcmp(entry.key, "go_intent") == 0 && entry.type == DBUS_TYPE_INT32) { go_intent = entry.int32_value; if ((go_intent < 0) || (go_intent > 15)) goto inv_args_clear; } else if (os_strcmp(entry.key, "wps_method") == 0 && entry.type == DBUS_TYPE_STRING) { if (os_strcmp(entry.str_value, "pbc") == 0) wps_method = WPS_PBC; else if (os_strcmp(entry.str_value, "pin") == 0) wps_method = WPS_PIN_DISPLAY; else if (os_strcmp(entry.str_value, "display") == 0) wps_method = WPS_PIN_DISPLAY; else if (os_strcmp(entry.str_value, "keypad") == 0) wps_method = WPS_PIN_KEYPAD; else goto inv_args_clear; } else if (os_strcmp(entry.key, "pin") == 0 && entry.type == DBUS_TYPE_STRING) { pin = os_strdup(entry.str_value); } else goto inv_args_clear; wpa_dbus_dict_entry_clear(&entry); } if (wps_method == WPS_NOT_READY || parse_peer_object_path(peer_object_path, addr) < 0 || !p2p_peer_known(wpa_s->global->p2p, addr)) goto inv_args; /* * Validate the wps_method specified and the pin value. */ if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD) goto inv_args; wpa_s = wpa_s->global->p2p_init_wpa_s; new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, go_intent, freq, 0, -1, 0, 0, 0, 0, 0, 0, - NULL, 0); + NULL, 0, false); if (new_pin >= 0) { char npin[9]; char *generated_pin; ret = os_snprintf(npin, sizeof(npin), "%08d", new_pin); if (os_snprintf_error(sizeof(npin), ret)) { reply = wpas_dbus_error_unknown_error(message, "invalid PIN"); goto out; } generated_pin = npin; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, &generated_pin, DBUS_TYPE_INVALID); } else { switch (new_pin) { case -2: err_msg = "connect failed due to channel unavailability."; iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE; break; case -3: err_msg = "connect failed due to unsupported channel."; iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED; break; default: err_msg = "connect failed due to unspecified error."; iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR; break; } /* * TODO: * Do we need specialized errors corresponding to above * error conditions as against just returning a different * error message? */ reply = dbus_message_new_error(message, iface, err_msg); } out: os_free(peer_object_path); os_free(pin); return reply; inv_args_clear: wpa_dbus_dict_entry_clear(&entry); inv_args: reply = wpas_dbus_error_invalid_args(message, NULL); goto out; } /** * wpas_dbus_handler_p2p_cancel - Cancel P2P group formation * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure * Returns: NULL on success or DBus error on failure * * Handler for "Cancel" method call. Returns NULL if P2P cancel succeeds or DBus * error on P2P cancel failure */ DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message, struct wpa_supplicant *wpa_s) { if (wpas_p2p_cancel(wpa_s)) return wpas_dbus_error_unknown_error(message, "P2P cancel failed"); return NULL; } DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; char *peer_object_path = NULL; char *pg_object_path = NULL; char *iface = NULL; u8 peer_addr[ETH_ALEN]; unsigned int group_id = 0; int persistent = 0; struct wpa_ssid *ssid; if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto err; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto err; if (os_strcmp(entry.key, "peer") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); } else if (os_strcmp(entry.key, "persistent_group_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { pg_object_path = os_strdup(entry.str_value); persistent = 1; wpa_dbus_dict_entry_clear(&entry); } else { wpa_dbus_dict_entry_clear(&entry); goto err; } } if (parse_peer_object_path(peer_object_path, peer_addr) < 0 || !p2p_peer_known(wpa_s->global->p2p, peer_addr)) goto err; wpa_s = wpa_s->global->p2p_init_wpa_s; if (persistent) { char *net_id_str; /* * A group ID is defined meaning we want to re-invoke a * persistent group */ iface = wpas_dbus_new_decompose_object_path( pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &net_id_str); if (iface == NULL || net_id_str == NULL || !wpa_s->parent->dbus_new_path || os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); goto out; } group_id = strtoul(net_id_str, NULL, 10); if (errno == EINVAL) { reply = wpas_dbus_error_invalid_args( message, pg_object_path); goto out; } /* Get the SSID structure from the persistent group id */ ssid = wpa_config_get_network(wpa_s->conf, group_id); if (ssid == NULL || ssid->disabled != 2) goto err; if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0, - 0, 0, 0) < 0) { + 0, 0, 0, false) < 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } } else { /* * No group ID means propose to a peer to join my active group */ if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname, - peer_addr, NULL)) { + peer_addr, NULL, false)) { reply = wpas_dbus_error_unknown_error( message, "Failed to join to an active group"); goto out; } } out: os_free(iface); os_free(pg_object_path); os_free(peer_object_path); return reply; err: reply = wpas_dbus_error_invalid_args(message, NULL); goto out; } DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter; char *peer_object_path = NULL; char *config_method = NULL; u8 peer_addr[ETH_ALEN]; dbus_message_iter_init(message, &iter); dbus_message_iter_get_basic(&iter, &peer_object_path); if (parse_peer_object_path(peer_object_path, peer_addr) < 0) return wpas_dbus_error_invalid_args(message, NULL); dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, &config_method); /* * Validation checks on config_method are being duplicated here * to be able to return invalid args reply since the error code * from p2p module are not granular enough (yet). */ if (os_strcmp(config_method, "display") && os_strcmp(config_method, "keypad") && os_strcmp(config_method, "pbc") && os_strcmp(config_method, "pushbutton")) return wpas_dbus_error_invalid_args(message, NULL); wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return wpas_dbus_error_no_p2p_mgmt_iface(message); if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0) return wpas_dbus_error_unknown_error(message, "Failed to send provision discovery request"); return NULL; } /* * P2P Device property accessor methods. */ dbus_bool_t wpas_dbus_getter_p2p_device_config( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, dict_iter; DBusMessageIter iter_secdev_dict_entry, iter_secdev_dict_val, iter_secdev_dict_array; const char *dev_name; int num_vendor_extensions = 0; int i; const struct wpabuf *vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; wpa_s = wpa_s->global->p2p_init_wpa_s; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", &variant_iter) || !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) goto err_no_mem; /* DeviceName */ dev_name = wpa_s->conf->device_name; if (dev_name && !wpa_dbus_dict_append_string(&dict_iter, "DeviceName", dev_name)) goto err_no_mem; /* Primary device type */ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType", (char *) wpa_s->conf->device_type, WPS_DEV_TYPE_LEN)) goto err_no_mem; /* Secondary device types */ if (wpa_s->conf->num_sec_device_types) { if (!wpa_dbus_dict_begin_array(&dict_iter, "SecondaryDeviceTypes", DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, &iter_secdev_dict_entry, &iter_secdev_dict_val, &iter_secdev_dict_array)) goto err_no_mem; for (i = 0; i < wpa_s->conf->num_sec_device_types; i++) wpa_dbus_dict_bin_array_add_element( &iter_secdev_dict_array, wpa_s->conf->sec_device_type[i], WPS_DEV_TYPE_LEN); if (!wpa_dbus_dict_end_array(&dict_iter, &iter_secdev_dict_entry, &iter_secdev_dict_val, &iter_secdev_dict_array)) goto err_no_mem; } /* GO IP address */ if (WPA_GET_BE32(wpa_s->conf->ip_addr_go) && !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo", (char *) wpa_s->conf->ip_addr_go, 4)) goto err_no_mem; /* IP address mask */ if (WPA_GET_BE32(wpa_s->conf->ip_addr_mask) && !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask", (char *) wpa_s->conf->ip_addr_mask, 4)) goto err_no_mem; /* IP address start */ if (WPA_GET_BE32(wpa_s->conf->ip_addr_start) && !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrStart", (char *) wpa_s->conf->ip_addr_start, 4)) goto err_no_mem; /* IP address end */ if (WPA_GET_BE32(wpa_s->conf->ip_addr_end) && !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrEnd", (char *) wpa_s->conf->ip_addr_end, 4)) goto err_no_mem; /* Vendor Extensions */ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (wpa_s->conf->wps_vendor_ext[i] == NULL) continue; vendor_ext[num_vendor_extensions++] = wpa_s->conf->wps_vendor_ext[i]; } if ((num_vendor_extensions && !wpa_dbus_dict_append_wpabuf_array(&dict_iter, "VendorExtension", vendor_ext, num_vendor_extensions)) || !wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent", wpa_s->conf->p2p_go_intent) || !wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect", wpa_s->conf->persistent_reconnect) || !wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass", wpa_s->conf->p2p_listen_reg_class) || !wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel", wpa_s->conf->p2p_listen_channel) || !wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass", wpa_s->conf->p2p_oper_reg_class) || !wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel", wpa_s->conf->p2p_oper_channel) || (wpa_s->conf->p2p_ssid_postfix && !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix", wpa_s->conf->p2p_ssid_postfix)) || !wpa_dbus_dict_append_bool(&dict_iter, "IntraBss", wpa_s->conf->p2p_intra_bss) || !wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle", wpa_s->conf->p2p_group_idle) || !wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack", wpa_s->conf->disassoc_low_ack) || !wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface", wpa_s->conf->p2p_no_group_iface) || !wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay", wpa_s->conf->p2p_search_delay) || !wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || !dbus_message_iter_close_container(iter, &variant_iter)) goto err_no_mem; return TRUE; err_no_mem: dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } dbus_bool_t wpas_dbus_setter_p2p_device_config( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, iter_dict; struct wpa_dbus_dict_entry entry = {.type = DBUS_TYPE_STRING }; unsigned int i; if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; wpa_s = wpa_s->global->p2p_init_wpa_s; dbus_message_iter_recurse(iter, &variant_iter); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) return FALSE; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, "invalid message format"); return FALSE; } if (os_strcmp(entry.key, "DeviceName") == 0) { char *devname; if (entry.type != DBUS_TYPE_STRING || os_strlen(entry.str_value) > WPS_DEV_NAME_MAX_LEN) goto error; devname = os_strdup(entry.str_value); if (devname == NULL) goto err_no_mem_clear; os_free(wpa_s->conf->device_name); wpa_s->conf->device_name = devname; wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_NAME; } else if (os_strcmp(entry.key, "PrimaryDeviceType") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE || entry.array_len != WPS_DEV_TYPE_LEN) goto error; os_memcpy(wpa_s->conf->device_type, entry.bytearray_value, WPS_DEV_TYPE_LEN); wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_TYPE; } else if (os_strcmp(entry.key, "SecondaryDeviceTypes") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != WPAS_DBUS_TYPE_BINARRAY || entry.array_len > MAX_SEC_DEVICE_TYPES) goto error; for (i = 0; i < entry.array_len; i++) if (wpabuf_len(entry.binarray_value[i]) != WPS_DEV_TYPE_LEN) goto err_no_mem_clear; for (i = 0; i < entry.array_len; i++) os_memcpy(wpa_s->conf->sec_device_type[i], wpabuf_head(entry.binarray_value[i]), WPS_DEV_TYPE_LEN); wpa_s->conf->num_sec_device_types = entry.array_len; wpa_s->conf->changed_parameters |= CFG_CHANGED_SEC_DEVICE_TYPE; } else if (os_strcmp(entry.key, "VendorExtension") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != WPAS_DBUS_TYPE_BINARRAY || (entry.array_len > P2P_MAX_WPS_VENDOR_EXT)) goto error; wpa_s->conf->changed_parameters |= CFG_CHANGED_VENDOR_EXTENSION; for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(wpa_s->conf->wps_vendor_ext[i]); if (i < entry.array_len) { wpa_s->conf->wps_vendor_ext[i] = entry.binarray_value[i]; entry.binarray_value[i] = NULL; } else wpa_s->conf->wps_vendor_ext[i] = NULL; } } else if (os_strcmp(entry.key, "GOIntent") == 0 && entry.type == DBUS_TYPE_UINT32 && (entry.uint32_value <= 15)) wpa_s->conf->p2p_go_intent = entry.uint32_value; else if (os_strcmp(entry.key, "PersistentReconnect") == 0 && entry.type == DBUS_TYPE_BOOLEAN) wpa_s->conf->persistent_reconnect = entry.bool_value; else if (os_strcmp(entry.key, "ListenRegClass") == 0 && entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_listen_reg_class = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_LISTEN_CHANNEL; } else if (os_strcmp(entry.key, "ListenChannel") == 0 && entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_listen_channel = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_LISTEN_CHANNEL; } else if (os_strcmp(entry.key, "OperRegClass") == 0 && entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_oper_reg_class = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_OPER_CHANNEL; } else if (os_strcmp(entry.key, "OperChannel") == 0 && entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_oper_channel = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_OPER_CHANNEL; } else if (os_strcmp(entry.key, "SsidPostfix") == 0) { char *postfix; if (entry.type != DBUS_TYPE_STRING) goto error; postfix = os_strdup(entry.str_value); if (!postfix) goto err_no_mem_clear; os_free(wpa_s->conf->p2p_ssid_postfix); wpa_s->conf->p2p_ssid_postfix = postfix; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_SSID_POSTFIX; } else if (os_strcmp(entry.key, "IntraBss") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { wpa_s->conf->p2p_intra_bss = entry.bool_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_INTRA_BSS; } else if (os_strcmp(entry.key, "IpAddrGo") == 0) { if (!wpas_dbus_validate_dbus_ipaddr(entry)) goto error; os_memcpy(wpa_s->conf->ip_addr_go, entry.bytearray_value, 4); } else if (os_strcmp(entry.key, "IpAddrMask") == 0) { if (!wpas_dbus_validate_dbus_ipaddr(entry)) goto error; os_memcpy(wpa_s->conf->ip_addr_mask, entry.bytearray_value, 4); } else if (os_strcmp(entry.key, "IpAddrStart") == 0) { if (!wpas_dbus_validate_dbus_ipaddr(entry)) goto error; os_memcpy(wpa_s->conf->ip_addr_start, entry.bytearray_value, 4); } else if (os_strcmp(entry.key, "IpAddrEnd") == 0) { if (!wpas_dbus_validate_dbus_ipaddr(entry)) goto error; os_memcpy(wpa_s->conf->ip_addr_end, entry.bytearray_value, 4); } else if (os_strcmp(entry.key, "GroupIdle") == 0 && entry.type == DBUS_TYPE_UINT32) wpa_s->conf->p2p_group_idle = entry.uint32_value; else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 && entry.type == DBUS_TYPE_UINT32) wpa_s->conf->disassoc_low_ack = entry.uint32_value; else if (os_strcmp(entry.key, "NoGroupIface") == 0 && entry.type == DBUS_TYPE_BOOLEAN) wpa_s->conf->p2p_no_group_iface = entry.bool_value; else if (os_strcmp(entry.key, "p2p_search_delay") == 0 && entry.type == DBUS_TYPE_UINT32) wpa_s->conf->p2p_search_delay = entry.uint32_value; else goto error; wpa_dbus_dict_entry_clear(&entry); } if (wpa_s->conf->changed_parameters) { /* Some changed parameters requires to update config*/ wpa_supplicant_update_config(wpa_s); } return TRUE; error: dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, "invalid message format"); wpa_dbus_dict_entry_clear(&entry); return FALSE; err_no_mem_clear: dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); wpa_dbus_dict_entry_clear(&entry); return FALSE; } dbus_bool_t wpas_dbus_getter_p2p_peers( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct p2p_data *p2p = wpa_s->global->p2p; int next = 0, i = 0; int num = 0, out_of_mem = 0; const u8 *addr; const struct p2p_peer_info *peer_info = NULL; dbus_bool_t success = FALSE; struct dl_list peer_objpath_list; struct peer_objpath_node { struct dl_list list; char path[WPAS_DBUS_OBJECT_PATH_MAX]; } *node, *tmp; char **peer_obj_paths = NULL; if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error) || !wpa_s->parent->parent->dbus_new_path) return FALSE; dl_list_init(&peer_objpath_list); /* Get the first peer info */ peer_info = p2p_get_peer_found(p2p, NULL, next); /* Get next and accumulate them */ next = 1; while (peer_info != NULL) { node = os_zalloc(sizeof(struct peer_objpath_node)); if (!node) { out_of_mem = 1; goto error; } addr = peer_info->p2p_device_addr; os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->parent->parent->dbus_new_path, MAC2STR(addr)); dl_list_add_tail(&peer_objpath_list, &node->list); num++; peer_info = p2p_get_peer_found(p2p, addr, next); } /* * Now construct the peer object paths in a form suitable for * array_property_getter helper below. */ peer_obj_paths = os_calloc(num, sizeof(char *)); if (!peer_obj_paths) { out_of_mem = 1; goto error; } dl_list_for_each_safe(node, tmp, &peer_objpath_list, struct peer_objpath_node, list) peer_obj_paths[i++] = node->path; success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_OBJECT_PATH, peer_obj_paths, num, error); error: if (peer_obj_paths) os_free(peer_obj_paths); dl_list_for_each_safe(node, tmp, &peer_objpath_list, struct peer_objpath_node, list) { dl_list_del(&node->list); os_free(node); } if (out_of_mem) dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return success; } enum wpas_p2p_role { WPAS_P2P_ROLE_DEVICE, WPAS_P2P_ROLE_GO, WPAS_P2P_ROLE_CLIENT, }; static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (!ssid) return WPAS_P2P_ROLE_DEVICE; if (wpa_s->wpa_state != WPA_COMPLETED) return WPAS_P2P_ROLE_DEVICE; switch (ssid->mode) { case WPAS_MODE_P2P_GO: case WPAS_MODE_P2P_GROUP_FORMATION: return WPAS_P2P_ROLE_GO; case WPAS_MODE_INFRA: if (ssid->p2p_group) return WPAS_P2P_ROLE_CLIENT; return WPAS_P2P_ROLE_DEVICE; default: return WPAS_P2P_ROLE_DEVICE; } } dbus_bool_t wpas_dbus_getter_p2p_role( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *str; switch (wpas_get_p2p_role(wpa_s)) { case WPAS_P2P_ROLE_GO: str = "GO"; break; case WPAS_P2P_ROLE_CLIENT: str = "client"; break; default: str = "device"; break; } return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str, error); } dbus_bool_t wpas_dbus_getter_p2p_group( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; char *dbus_groupobj_path = path_buf; if (wpa_s->dbus_groupobj_path == NULL) os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); else os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s", wpa_s->dbus_groupobj_path); return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, &dbus_groupobj_path, error); } dbus_bool_t wpas_dbus_getter_p2p_peergo( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; if (!wpa_s->parent->parent->dbus_new_path) return FALSE; if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT) os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); else os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->parent->parent->dbus_new_path, MAC2STR(wpa_s->go_dev_addr)); path = go_peer_obj_path; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, &path, error); } /* * Peer object properties accessor methods */ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; char *tmp; if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) return FALSE; /* get the peer info */ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } tmp = os_strdup(info->device_name); if (!tmp) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); os_free(tmp); return FALSE; } os_free(tmp); return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; char *tmp; if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) return FALSE; /* get the peer info */ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } tmp = os_strdup(info->manufacturer); if (!tmp) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); os_free(tmp); return FALSE; } os_free(tmp); return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_modelname( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; char *tmp; if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) return FALSE; /* get the peer info */ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } tmp = os_strdup(info->model_name); if (!tmp) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); os_free(tmp); return FALSE; } os_free(tmp); return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; char *tmp; if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) return FALSE; /* get the peer info */ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } tmp = os_strdup(info->model_number); if (!tmp) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); os_free(tmp); return FALSE; } os_free(tmp); return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; char *tmp; if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) return FALSE; /* get the peer info */ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } tmp = os_strdup(info->serial_number); if (!tmp) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); os_free(tmp); return FALSE; } os_free(tmp); return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, (char *) info->pri_dev_type, WPS_DEV_TYPE_LEN, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_config_method( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, &info->config_methods, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_level( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, &info->level, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE, &info->dev_capab, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE, &info->group_capab, error)) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; DBusMessageIter variant_iter, array_iter; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, &variant_iter) || !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, &array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, "%s: failed to construct message 1", __func__); return FALSE; } if (info->wps_sec_dev_type_list_len) { const u8 *sec_dev_type_list = info->wps_sec_dev_type_list; int num_sec_device_types = info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; int i; DBusMessageIter inner_array_iter; for (i = 0; i < num_sec_device_types; i++) { if (!dbus_message_iter_open_container( &array_iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &inner_array_iter) || !dbus_message_iter_append_fixed_array( &inner_array_iter, DBUS_TYPE_BYTE, &sec_dev_type_list, WPS_DEV_TYPE_LEN) || !dbus_message_iter_close_container( &array_iter, &inner_array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, "%s: failed to construct message 2 (%d)", __func__, i); return FALSE; } sec_dev_type_list += WPS_DEV_TYPE_LEN; } } if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || !dbus_message_iter_close_container(iter, &variant_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, "%s: failed to construct message 3", __func__); return FALSE; } return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT]; unsigned int i, num = 0; struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } /* Add WPS vendor extensions attribute */ os_memset(vendor_extension, 0, sizeof(vendor_extension)); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (info->wps_vendor_ext[i] == NULL) continue; vendor_extension[num] = info->wps_vendor_ext[i]; num++; } if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE, vendor_extension, num, error)) return FALSE; return TRUE; } dbus_bool_t wpas_dbus_getter_p2p_peer_ies( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (info->wfd_subelems == NULL) return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, NULL, 0, error); return wpas_dbus_simple_array_property_getter( iter, DBUS_TYPE_BYTE, (char *) info->wfd_subelems->buf, info->wfd_subelems->used, error); } dbus_bool_t wpas_dbus_getter_p2p_peer_device_address( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } return wpas_dbus_simple_array_property_getter( iter, DBUS_TYPE_BYTE, (char *) info->p2p_device_addr, ETH_ALEN, error); } struct peer_group_data { struct wpa_supplicant *wpa_s; const struct p2p_peer_info *info; char **paths; unsigned int nb_paths; int error; }; static int match_group_where_peer_is_client(struct p2p_group *group, void *user_data) { struct peer_group_data *data = user_data; const struct p2p_group_config *cfg; struct wpa_supplicant *wpa_s_go; char **paths; if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr)) return 1; cfg = p2p_group_get_config(group); wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid, cfg->ssid_len); if (wpa_s_go == NULL) return 1; paths = os_realloc_array(data->paths, data->nb_paths + 1, sizeof(char *)); if (paths == NULL) goto out_of_memory; data->paths = paths; data->paths[data->nb_paths] = wpa_s_go->dbus_groupobj_path; data->nb_paths++; return 1; out_of_memory: data->error = ENOMEM; return 0; } dbus_bool_t wpas_dbus_getter_p2p_peer_groups( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; struct peer_group_data data; struct wpa_supplicant *wpa_s, *wpa_s_go; dbus_bool_t success = FALSE; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } os_memset(&data, 0, sizeof(data)); wpa_s = peer_args->wpa_s; wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return no_p2p_mgmt_interface(error); wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr); if (wpa_s_go) { data.paths = os_calloc(1, sizeof(char *)); if (data.paths == NULL) goto out_of_memory; data.paths[0] = wpa_s_go->dbus_groupobj_path; data.nb_paths = 1; } data.wpa_s = peer_args->wpa_s; data.info = info; p2p_loop_on_all_groups(peer_args->wpa_s->global->p2p, match_group_where_peer_is_client, &data); if (data.error) goto out_of_memory; if (data.paths == NULL) { return wpas_dbus_simple_array_property_getter( iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error); } success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_OBJECT_PATH, data.paths, data.nb_paths, error); goto out; out_of_memory: dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); out: os_free(data.paths); return success; } dbus_bool_t wpas_dbus_getter_p2p_peer_vsie( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (!info) { dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } if (!info->vendor_elems) return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, NULL, 0, error); return wpas_dbus_simple_array_property_getter( iter, DBUS_TYPE_BYTE, (char *) info->vendor_elems->buf, info->vendor_elems->used, error); } /** * wpas_dbus_getter_persistent_groups - Get array of persistent group objects * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure * @user_data: Function specific data * Returns: TRUE on success, FALSE on failure * * Getter for "PersistentGroups" property. */ dbus_bool_t wpas_dbus_getter_persistent_groups( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; char **paths; unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return no_p2p_mgmt_interface(error); if (!wpa_s->parent->dbus_new_path) return FALSE; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (network_is_persistent_group(ssid)) num++; paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; } /* Loop through configured networks and append object path of each */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!network_is_persistent_group(ssid)) continue; paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (paths[i] == NULL) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); goto out; } /* Construct the object path for this network. */ os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", wpa_s->parent->dbus_new_path, ssid->id); } success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_OBJECT_PATH, paths, num, error); out: while (i) os_free(paths[--i]); os_free(paths); return success; } /** * wpas_dbus_getter_persistent_group_properties - Get options for a persistent * group * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure * @user_data: Function specific data * Returns: TRUE on success, FALSE on failure * * Getter for "Properties" property of a persistent group. */ dbus_bool_t wpas_dbus_getter_persistent_group_properties( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; /* Leveraging the fact that persistent group object is still * represented in same manner as network within. */ return wpas_dbus_getter_network_properties(property_desc, iter, error, net); } /** * wpas_dbus_setter_persistent_group_properties - Set options for a persistent * group * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure * @user_data: Function specific data * Returns: TRUE on success, FALSE on failure * * Setter for "Properties" property of a persistent group. */ dbus_bool_t wpas_dbus_setter_persistent_group_properties( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_ssid *ssid = net->ssid; DBusMessageIter variant_iter; /* * Leveraging the fact that persistent group object is still * represented in same manner as network within. */ dbus_message_iter_recurse(iter, &variant_iter); return set_network_properties(net->wpa_s, ssid, &variant_iter, error); } /** * wpas_dbus_new_iface_add_persistent_group - Add a new configured * persistent_group * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: A dbus message containing the object path of the new * persistent group * * Handler function for "AddPersistentGroup" method call of a P2P Device * interface. */ DBusMessage * wpas_dbus_handler_add_persistent_group( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_ssid *ssid = NULL; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; DBusError error; dbus_message_iter_init(message, &iter); wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) { reply = wpas_dbus_error_no_p2p_mgmt_iface(message); goto err; } if (wpa_s->parent->dbus_new_path) ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { wpa_printf(MSG_ERROR, "dbus: %s: Cannot add new persistent group", __func__); reply = wpas_dbus_error_unknown_error( message, "wpa_supplicant could not add a persistent group on this interface."); goto err; } /* Mark the ssid as being a persistent group before the notification */ ssid->disabled = 2; ssid->p2p_persistent_group = 1; wpas_notify_persistent_group_added(wpa_s, ssid); wpa_config_set_network_defaults(ssid); dbus_error_init(&error); if (!set_network_properties(wpa_s, ssid, &iter, &error)) { wpa_printf(MSG_DEBUG, "dbus: %s: Control interface could not set persistent group properties", __func__); reply = wpas_dbus_reply_new_from_error( message, &error, DBUS_ERROR_INVALID_ARGS, "Failed to set network properties"); dbus_error_free(&error); goto err; } /* Construct the object path for this network. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", wpa_s->parent->dbus_new_path, ssid->id); reply = dbus_message_new_method_return(message); if (reply == NULL) { reply = wpas_dbus_error_no_memory(message); goto err; } if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { dbus_message_unref(reply); reply = wpas_dbus_error_no_memory(message); goto err; } return reply; err: if (ssid) { wpas_notify_persistent_group_removed(wpa_s, ssid); wpa_config_remove_network(wpa_s->conf, ssid->id); } return reply; } /** * wpas_dbus_handler_remove_persistent_group - Remove a configured persistent * group * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: NULL on success or dbus error on failure * * Handler function for "RemovePersistentGroup" method call of a P2P Device * interface. */ DBusMessage * wpas_dbus_handler_remove_persistent_group( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; const char *op; char *iface = NULL, *persistent_group_id; int id; struct wpa_ssid *ssid; dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID); wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) { reply = wpas_dbus_error_no_p2p_mgmt_iface(message); goto out; } /* * Extract the network ID and ensure the network is actually a child of * this interface. */ iface = wpas_dbus_new_decompose_object_path( op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &persistent_group_id); if (iface == NULL || persistent_group_id == NULL || !wpa_s->parent->dbus_new_path || os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } id = strtoul(persistent_group_id, NULL, 10); if (errno == EINVAL) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { reply = wpas_dbus_error_persistent_group_unknown(message); goto out; } wpas_notify_persistent_group_removed(wpa_s, ssid); if (wpa_config_remove_network(wpa_s->conf, id) < 0) { wpa_printf(MSG_ERROR, "dbus: %s: error occurred when removing persistent group %d", __func__, id); reply = wpas_dbus_error_unknown_error( message, "error removing the specified persistent group on this interface."); goto out; } out: os_free(iface); return reply; } static void remove_persistent_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { wpas_notify_persistent_group_removed(wpa_s, ssid); if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) { wpa_printf(MSG_ERROR, "dbus: %s: error occurred when removing persistent group %d", __func__, ssid->id); return; } } /** * wpas_dbus_handler_remove_all_persistent_groups - Remove all configured * persistent groups * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * Returns: NULL on success or dbus error on failure * * Handler function for "RemoveAllPersistentGroups" method call of a * P2P Device interface. */ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( DBusMessage *message, struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *next; struct wpa_config *config; wpa_s = wpa_s->global->p2p_init_wpa_s; if (!wpa_s) return wpas_dbus_error_no_p2p_mgmt_iface(message); config = wpa_s->conf; ssid = config->ssid; while (ssid) { next = ssid->next; if (network_is_persistent_group(ssid)) remove_persistent_group(wpa_s, ssid); ssid = next; } return NULL; } /* * Group object properties accessor methods */ dbus_bool_t wpas_dbus_getter_p2p_group_members( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; unsigned int num_members; char **paths; unsigned int i; void *next = NULL; const u8 *addr; dbus_bool_t success = FALSE; if (!wpa_s->parent->parent->dbus_new_path) return FALSE; /* Verify correct role for this property */ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) { return wpas_dbus_simple_array_property_getter( iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error); } ssid = wpa_s->conf->ssid; /* At present WPAS P2P_GO mode only applicable for p2p_go */ if (ssid->mode != WPAS_MODE_P2P_GO && ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) return FALSE; num_members = p2p_get_group_num_members(wpa_s->p2p_group); paths = os_calloc(num_members, sizeof(char *)); if (!paths) goto out_of_memory; i = 0; while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) { paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (!paths[i]) goto out_of_memory; os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->parent->parent->dbus_new_path, MAC2STR(addr)); i++; } success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_OBJECT_PATH, paths, num_members, error); for (i = 0; i < num_members; i++) os_free(paths[i]); os_free(paths); return success; out_of_memory: dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); if (paths) { for (i = 0; i < num_members; i++) os_free(paths[i]); os_free(paths); } return FALSE; } dbus_bool_t wpas_dbus_getter_p2p_group_ssid( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; if (wpa_s->current_ssid == NULL) return FALSE; return wpas_dbus_simple_array_property_getter( iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len, error); } dbus_bool_t wpas_dbus_getter_p2p_group_bssid( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u8 role = wpas_get_p2p_role(wpa_s); u8 *p_bssid; if (role == WPAS_P2P_ROLE_CLIENT) { if (wpa_s->current_ssid == NULL) return FALSE; p_bssid = wpa_s->current_ssid->bssid; } else { if (wpa_s->ap_iface == NULL) return FALSE; p_bssid = wpa_s->ap_iface->bss[0]->own_addr; } return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, p_bssid, ETH_ALEN, error); } dbus_bool_t wpas_dbus_getter_p2p_group_frequency( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u16 op_freq; u8 role = wpas_get_p2p_role(wpa_s); if (role == WPAS_P2P_ROLE_CLIENT) { if (wpa_s->go_params == NULL) return FALSE; op_freq = wpa_s->go_params->freq; } else { if (wpa_s->ap_iface == NULL) return FALSE; op_freq = wpa_s->ap_iface->freq; } return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, &op_freq, error); } dbus_bool_t wpas_dbus_getter_p2p_group_passphrase( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) return FALSE; return wpas_dbus_string_property_getter(iter, ssid->passphrase, error); } dbus_bool_t wpas_dbus_getter_p2p_group_psk( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u8 *p_psk = NULL; u8 psk_len = 0; struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) return FALSE; if (ssid->psk_set) { p_psk = ssid->psk; psk_len = sizeof(ssid->psk); } return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, p_psk, psk_len, error); } dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct hostapd_data *hapd; struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; unsigned int i, num_vendor_ext = 0; os_memset(vendor_ext, 0, sizeof(vendor_ext)); /* Verify correct role for this property */ if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) { if (wpa_s->ap_iface == NULL) return FALSE; hapd = wpa_s->ap_iface->bss[0]; /* Parse WPS Vendor Extensions sent in Beacon/Probe Response */ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { if (hapd->conf->wps_vendor_ext[i] == NULL) continue; vendor_ext[num_vendor_ext++] = hapd->conf->wps_vendor_ext[i]; } } /* Return vendor extensions or no data */ return wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE, vendor_ext, num_vendor_ext, error); } dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, iter_dict, array_iter, sub; struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; unsigned int i; struct hostapd_data *hapd = NULL; if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO && wpa_s->ap_iface != NULL) hapd = wpa_s->ap_iface->bss[0]; else return FALSE; dbus_message_iter_recurse(iter, &variant_iter); if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) return FALSE; /* * This is supposed to be array of bytearrays (aay), but the earlier * implementation used a dict with "WPSVendorExtensions" as the key in * this setter function which does not match the format used by the * getter function. For backwards compatibility, allow both formats to * be used in the setter. */ if (dbus_message_iter_get_element_type(&variant_iter) == DBUS_TYPE_ARRAY) { /* This is the proper format matching the getter */ struct wpabuf *vals[MAX_WPS_VENDOR_EXTENSIONS]; dbus_message_iter_recurse(&variant_iter, &array_iter); if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) { wpa_printf(MSG_DEBUG, "dbus: Not an array of array of bytes"); return FALSE; } i = 0; os_memset(vals, 0, sizeof(vals)); while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) { char *val; int len; if (i == MAX_WPS_VENDOR_EXTENSIONS) { wpa_printf(MSG_DEBUG, "dbus: Too many WPSVendorExtensions values"); i = MAX_WPS_VENDOR_EXTENSIONS + 1; break; } dbus_message_iter_recurse(&array_iter, &sub); dbus_message_iter_get_fixed_array(&sub, &val, &len); wpa_hexdump(MSG_DEBUG, "dbus: WPSVendorExtentions[]", val, len); vals[i] = wpabuf_alloc_copy(val, len); if (vals[i] == NULL) { i = MAX_WPS_VENDOR_EXTENSIONS + 1; break; } i++; dbus_message_iter_next(&array_iter); } if (i > MAX_WPS_VENDOR_EXTENSIONS) { for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) wpabuf_free(vals[i]); return FALSE; } for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { wpabuf_free(hapd->conf->wps_vendor_ext[i]); hapd->conf->wps_vendor_ext[i] = vals[i]; } hostapd_update_wps(hapd); return TRUE; } if (dbus_message_iter_get_element_type(&variant_iter) != DBUS_TYPE_DICT_ENTRY) return FALSE; wpa_printf(MSG_DEBUG, "dbus: Try to use backwards compatibility version of WPSVendorExtensions setter"); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) return FALSE; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, "invalid message format"); return FALSE; } if (os_strcmp(entry.key, "WPSVendorExtensions") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != WPAS_DBUS_TYPE_BINARRAY || entry.array_len > MAX_WPS_VENDOR_EXTENSIONS) goto error; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { wpabuf_free(hapd->conf->wps_vendor_ext[i]); if (i < entry.array_len) { hapd->conf->wps_vendor_ext[i] = entry.binarray_value[i]; entry.binarray_value[i] = NULL; } else hapd->conf->wps_vendor_ext[i] = NULL; } hostapd_update_wps(hapd); } else goto error; wpa_dbus_dict_entry_clear(&entry); } return TRUE; error: wpa_dbus_dict_entry_clear(&entry); dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, "invalid message format"); return FALSE; } DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; int upnp = 0; int bonjour = 0; char *service = NULL; struct wpabuf *query = NULL; struct wpabuf *resp = NULL; u8 version = 0; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "service_type") == 0 && entry.type == DBUS_TYPE_STRING) { if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; else if (os_strcmp(entry.str_value, "bonjour") == 0) bonjour = 1; else goto error_clear; } else if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; } else if (os_strcmp(entry.key, "service") == 0 && entry.type == DBUS_TYPE_STRING) { os_free(service); service = os_strdup(entry.str_value); } else if (os_strcmp(entry.key, "query") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; query = wpabuf_alloc_copy( entry.bytearray_value, entry.array_len); } else if (os_strcmp(entry.key, "response") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; resp = wpabuf_alloc_copy(entry.bytearray_value, entry.array_len); } wpa_dbus_dict_entry_clear(&entry); } if (upnp == 1) { if (version <= 0 || service == NULL) goto error; if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0) goto error; } else if (bonjour == 1) { if (query == NULL || resp == NULL) goto error; if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) goto error; query = NULL; resp = NULL; } else goto error; os_free(service); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: os_free(service); wpabuf_free(query); wpabuf_free(resp); return wpas_dbus_error_invalid_args(message, NULL); } DBusMessage * wpas_dbus_handler_p2p_delete_service( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; int upnp = 0; int bonjour = 0; int ret = 0; char *service = NULL; struct wpabuf *query = NULL; u8 version = 0; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "service_type") == 0 && entry.type == DBUS_TYPE_STRING) { if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; else if (os_strcmp(entry.str_value, "bonjour") == 0) bonjour = 1; else goto error_clear; } else if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; } else if (os_strcmp(entry.key, "service") == 0 && entry.type == DBUS_TYPE_STRING) { os_free(service); service = os_strdup(entry.str_value); } else if (os_strcmp(entry.key, "query") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; wpabuf_free(query); query = wpabuf_alloc_copy(entry.bytearray_value, entry.array_len); } else { goto error_clear; } wpa_dbus_dict_entry_clear(&entry); } if (upnp == 1) { if (version <= 0 || service == NULL) goto error; ret = wpas_p2p_service_del_upnp(wpa_s, version, service); if (ret != 0) goto error; } else if (bonjour == 1) { if (query == NULL) goto error; ret = wpas_p2p_service_del_bonjour(wpa_s, query); if (ret != 0) goto error; } else goto error; wpabuf_free(query); os_free(service); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: wpabuf_free(query); os_free(service); return wpas_dbus_error_invalid_args(message, NULL); } DBusMessage * wpas_dbus_handler_p2p_flush_service(DBusMessage *message, struct wpa_supplicant *wpa_s) { wpas_p2p_service_flush(wpa_s); return NULL; } DBusMessage * wpas_dbus_handler_p2p_service_sd_req( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; int upnp = 0; char *service = NULL; char *peer_object_path = NULL; struct wpabuf *tlv = NULL; u8 version = 0; u64 ref = 0; u8 addr_buf[ETH_ALEN], *addr; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "peer_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); } else if (os_strcmp(entry.key, "service_type") == 0 && entry.type == DBUS_TYPE_STRING) { if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; else goto error_clear; } else if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; } else if (os_strcmp(entry.key, "service") == 0 && entry.type == DBUS_TYPE_STRING) { service = os_strdup(entry.str_value); } else if (os_strcmp(entry.key, "tlv") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; tlv = wpabuf_alloc_copy(entry.bytearray_value, entry.array_len); } else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } if (!peer_object_path) { addr = NULL; } else { if (parse_peer_object_path(peer_object_path, addr_buf) < 0 || !p2p_peer_known(wpa_s->global->p2p, addr_buf)) goto error; addr = addr_buf; } if (upnp == 1) { if (version <= 0 || service == NULL) goto error; ref = wpas_p2p_sd_request_upnp(wpa_s, addr, version, service); } else { if (tlv == NULL) goto error; ref = wpas_p2p_sd_request(wpa_s, addr, tlv); wpabuf_free(tlv); } if (ref != 0) { reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_UINT64, &ref, DBUS_TYPE_INVALID); } else { reply = wpas_dbus_error_unknown_error( message, "Unable to send SD request"); } out: os_free(service); os_free(peer_object_path); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: if (tlv) wpabuf_free(tlv); reply = wpas_dbus_error_invalid_args(message, NULL); goto out; } DBusMessage * wpas_dbus_handler_p2p_service_sd_res( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter_dict; DBusMessage *reply = NULL; DBusMessageIter iter; struct wpa_dbus_dict_entry entry; char *peer_object_path = NULL; struct wpabuf *tlv = NULL; int freq = 0; int dlg_tok = 0; u8 addr[ETH_ALEN]; dbus_message_iter_init(message, &iter); if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (os_strcmp(entry.key, "peer_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); } else if (os_strcmp(entry.key, "frequency") == 0 && entry.type == DBUS_TYPE_INT32) { freq = entry.uint32_value; } else if (os_strcmp(entry.key, "dialog_token") == 0 && (entry.type == DBUS_TYPE_UINT32 || entry.type == DBUS_TYPE_INT32)) { dlg_tok = entry.uint32_value; } else if (os_strcmp(entry.key, "tlvs") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; tlv = wpabuf_alloc_copy(entry.bytearray_value, entry.array_len); } else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } if (parse_peer_object_path(peer_object_path, addr) < 0 || !p2p_peer_known(wpa_s->global->p2p, addr) || tlv == NULL) goto error; wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv); wpabuf_free(tlv); out: os_free(peer_object_path); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: reply = wpas_dbus_error_invalid_args(message, NULL); goto out; } DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter; u64 req = 0; dbus_message_iter_init(message, &iter); dbus_message_iter_get_basic(&iter, &req); if (req == 0) goto error; if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0) goto error; return NULL; error: return wpas_dbus_error_invalid_args(message, NULL); } DBusMessage * wpas_dbus_handler_p2p_service_update( DBusMessage *message, struct wpa_supplicant *wpa_s) { wpas_p2p_sd_service_update(wpa_s); return NULL; } DBusMessage * wpas_dbus_handler_p2p_serv_disc_external( DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessageIter iter; int ext = 0; dbus_message_iter_init(message, &iter); dbus_message_iter_get_basic(&iter, &ext); wpa_s->p2p_sd_over_ctrl_iface = ext; return NULL; } #ifdef CONFIG_WIFI_DISPLAY dbus_bool_t wpas_dbus_getter_global_wfd_ies( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; struct wpabuf *ie; dbus_bool_t ret; ie = wifi_display_get_wfd_ie(global); if (ie == NULL) return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, NULL, 0, error); ret = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, wpabuf_head(ie), wpabuf_len(ie), error); wpabuf_free(ie); return ret; } dbus_bool_t wpas_dbus_setter_global_wfd_ies( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; DBusMessageIter variant, array; struct wpabuf *ie = NULL; const u8 *data; int len; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) goto err; dbus_message_iter_recurse(iter, &variant); if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY) goto err; dbus_message_iter_recurse(&variant, &array); dbus_message_iter_get_fixed_array(&array, &data, &len); if (len == 0) { wifi_display_enable(global, 0); wifi_display_deinit(global); return TRUE; } ie = wpabuf_alloc(len); if (ie == NULL) goto err; wpabuf_put_data(ie, data, len); if (wifi_display_subelem_set_from_ies(global, ie) != 0) goto err; if (global->wifi_display == 0) wifi_display_enable(global, 1); wpabuf_free(ie); return TRUE; err: wpabuf_free(ie); dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, "invalid message format"); return FALSE; } #endif /* CONFIG_WIFI_DISPLAY */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index b511d1cc1457..a565e658f33d 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,5549 +1,5550 @@ /* * WPA Supplicant - Driver event processing * Copyright (c) 2003-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "eloop.h" #include "config.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "pcsc_funcs.h" #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "ap/hostapd.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/gas_server.h" #include "common/dpp.h" #include "common/ptksa_cache.h" #include "crypto/random.h" #include "bssid_ignore.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" #include "gas_query.h" #include "p2p_supplicant.h" #include "bgscan.h" #include "autoscan.h" #include "ap.h" #include "bss.h" #include "scan.h" #include "offchannel.h" #include "interworking.h" #include "mesh.h" #include "mesh_mpm.h" #include "wmm_ac.h" #include "dpp_supplicant.h" #define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5 #ifndef CONFIG_NO_SCAN_PROCESSING static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, int new_scan, int own_request); #endif /* CONFIG_NO_SCAN_PROCESSING */ int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct os_reltime now; if (ssid == NULL || ssid->disabled_until.sec == 0) return 0; os_get_reltime(&now); if (ssid->disabled_until.sec > now.sec) return ssid->disabled_until.sec - now.sec; wpas_clear_temp_disabled(wpa_s, ssid, 0); return 0; } #ifndef CONFIG_NO_SCAN_PROCESSING /** * wpas_reenabled_network_time - Time until first network is re-enabled * @wpa_s: Pointer to wpa_supplicant data * Returns: If all enabled networks are temporarily disabled, returns the time * (in sec) until the first network is re-enabled. Otherwise returns 0. * * This function is used in case all enabled networks are temporarily disabled, * in which case it returns the time (in sec) that the first network will be * re-enabled. The function assumes that at least one network is enabled. */ static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; int disabled_for, res = 0; #ifdef CONFIG_INTERWORKING if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking && wpa_s->conf->cred) return 0; #endif /* CONFIG_INTERWORKING */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid->disabled) continue; disabled_for = wpas_temp_disabled(wpa_s, ssid); if (!disabled_for) return 0; if (!res || disabled_for < res) res = disabled_for; } return res; } #endif /* CONFIG_NO_SCAN_PROCESSING */ void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING) return; wpa_dbg(wpa_s, MSG_DEBUG, "Try to associate due to network getting re-enabled"); if (wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } } static struct wpa_bss * wpa_supplicant_get_new_bss( struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss *bss = NULL; struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid->ssid_len > 0) bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) bss = wpa_bss_get_bssid(wpa_s, bssid); return bss; } static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); if (!bss) { wpa_supplicant_update_scan_results(wpa_s); /* Get the BSS from the new scan results */ bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); } if (bss) wpa_s->current_bss = bss; } static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; u8 drv_ssid[SSID_MAX_LEN]; size_t drv_ssid_len; int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) { wpa_supplicant_update_current_bss(wpa_s); if (wpa_s->current_ssid->ssid_len == 0) return 0; /* current profile still in use */ res = wpa_drv_get_ssid(wpa_s, drv_ssid); if (res < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to read SSID from driver"); return 0; /* try to use current profile */ } drv_ssid_len = res; if (drv_ssid_len == wpa_s->current_ssid->ssid_len && os_memcmp(drv_ssid, wpa_s->current_ssid->ssid, drv_ssid_len) == 0) return 0; /* current profile still in use */ #ifdef CONFIG_OWE if ((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_OWE) && wpa_s->current_bss && (wpa_s->current_bss->flags & WPA_BSS_OWE_TRANSITION) && drv_ssid_len == wpa_s->current_bss->ssid_len && os_memcmp(drv_ssid, wpa_s->current_bss->ssid, drv_ssid_len) == 0) return 0; /* current profile still in use */ #endif /* CONFIG_OWE */ wpa_msg(wpa_s, MSG_DEBUG, "Driver-initiated BSS selection changed the SSID to %s", wpa_ssid_txt(drv_ssid, drv_ssid_len)); /* continue selecting a new network profile */ } wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " "information"); ssid = wpa_supplicant_get_ssid(wpa_s); if (ssid == NULL) { wpa_msg(wpa_s, MSG_INFO, "No network configuration found for the current AP"); return -1; } if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled"); return -1; } if (disallowed_bssid(wpa_s, wpa_s->bssid) || disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed"); return -1; } res = wpas_temp_disabled(wpa_s, ssid); if (res > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily " "disabled for %d second(s)", res); return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " "current AP"); if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { u8 wpa_ie[80]; size_t wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len) < 0) wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites"); } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); } if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) eapol_sm_invalidate_cached_session(wpa_s->eapol); old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; wpa_supplicant_update_current_bss(wpa_s); wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); return 0; } void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->countermeasures) { wpa_s->countermeasures = 0; wpa_drv_set_countermeasures(wpa_s, 0); wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped"); /* * It is possible that the device is sched scanning, which means * that a connection attempt will be done only when we receive * scan results. However, in this case, it would be preferable * to scan and connect immediately, so cancel the sched_scan and * issue a regular scan flow. */ wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } } void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) { int bssid_changed; wnm_bss_keep_alive_deinit(wpa_s); #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_AP wpa_supplicant_ap_deinit(wpa_s); #endif /* CONFIG_AP */ #ifdef CONFIG_HS20 /* Clear possibly configured frame filters */ wpa_drv_configure_frame_filters(wpa_s, 0); #endif /* CONFIG_HS20 */ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return; if (os_reltime_initialized(&wpa_s->session_start)) { os_reltime_age(&wpa_s->session_start, &wpa_s->session_length); wpa_s->session_start.sec = 0; wpa_s->session_start.usec = 0; wpas_notify_session_length(wpa_s); } wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); sme_clear_on_disassoc(wpa_s); wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; if (bssid_changed) wpas_notify_bssid_changed(wpa_s); eapol_sm_notify_portEnabled(wpa_s->eapol, false); eapol_sm_notify_portValid(wpa_s->eapol, false); if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || wpa_s->key_mgmt == WPA_KEY_MGMT_DPP || wpa_s->drv_authorized_port) eapol_sm_notify_eap_success(wpa_s->eapol, false); wpa_s->drv_authorized_port = 0; wpa_s->ap_ies_from_associnfo = 0; wpa_s->current_ssid = NULL; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->key_mgmt = 0; wpas_rrm_reset(wpa_s); wpa_s->wnmsleep_used = 0; wnm_clear_coloc_intf_reporting(wpa_s); wpa_s->disable_mbo_oce = 0; #ifdef CONFIG_TESTING_OPTIONS wpa_s->last_tk_alg = WPA_ALG_NONE; os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk)); #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->ieee80211ac = 0; if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) wpa_s->enabled_4addr_mode = 0; } static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) { struct wpa_ie_data ie; int pmksa_set = -1; size_t i; /* Start with assumption of no PMKSA cache entry match */ pmksa_cache_clear_current(wpa_s->wpa); if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 || ie.pmkid == NULL) return; for (i = 0; i < ie.num_pmkid; i++) { pmksa_set = pmksa_cache_set_current(wpa_s->wpa, ie.pmkid + i * PMKID_LEN, NULL, NULL, 0, NULL, 0); if (pmksa_set == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); break; } } wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from " "PMKSA cache", pmksa_set == 0 ? "" : "not "); } static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate " "event"); return; } wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR " index=%d preauth=%d", MAC2STR(data->pmkid_candidate.bssid), data->pmkid_candidate.index, data->pmkid_candidate.preauth); pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid, data->pmkid_candidate.index, data->pmkid_candidate.preauth); } static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) return 0; #ifdef IEEE8021X_EAPOL if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->current_ssid && !(wpa_s->current_ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either * plaintext or static WEP keys). */ return 0; } #endif /* IEEE8021X_EAPOL */ return 1; } /** * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC * @wpa_s: pointer to wpa_supplicant data * @ssid: Configuration data for the network * Returns: 0 on success, -1 on failure * * This function is called when starting authentication with a network that is * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA). */ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { #ifdef IEEE8021X_EAPOL #ifdef PCSC_FUNCS int aka = 0, sim = 0; if ((ssid != NULL && ssid->eap.pcsc == NULL) || wpa_s->scard != NULL || wpa_s->conf->external_sim) return 0; if (ssid == NULL || ssid->eap.eap_methods == NULL) { sim = 1; aka = 1; } else { struct eap_method_type *eap = ssid->eap.eap_methods; while (eap->vendor != EAP_VENDOR_IETF || eap->method != EAP_TYPE_NONE) { if (eap->vendor == EAP_VENDOR_IETF) { if (eap->method == EAP_TYPE_SIM) sim = 1; else if (eap->method == EAP_TYPE_AKA || eap->method == EAP_TYPE_AKA_PRIME) aka = 1; } eap++; } } if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL) sim = 0; if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL && eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) == NULL) aka = 0; if (!sim && !aka) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to " "use SIM, but neither EAP-SIM nor EAP-AKA are " "enabled"); return 0; } wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM " "(sim=%d aka=%d) - initialize PCSC", sim, aka); wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader); if (wpa_s->scard == NULL) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM " "(pcsc-lite)"); return -1; } wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); #endif /* PCSC_FUNCS */ #endif /* IEEE8021X_EAPOL */ return 0; } #ifndef CONFIG_NO_SCAN_PROCESSING #ifdef CONFIG_WEP static int has_wep_key(struct wpa_ssid *ssid) { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) return 1; } return 0; } #endif /* CONFIG_WEP */ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { int privacy = 0; if (ssid->mixed_cell) return 1; #ifdef CONFIG_WPS if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) return 1; #endif /* CONFIG_WPS */ #ifdef CONFIG_OWE if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only) return 1; #endif /* CONFIG_OWE */ #ifdef CONFIG_WEP if (has_wep_key(ssid)) privacy = 1; #endif /* CONFIG_WEP */ #ifdef IEEE8021X_EAPOL if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) privacy = 1; #endif /* IEEE8021X_EAPOL */ if (wpa_key_mgmt_wpa(ssid->key_mgmt)) privacy = 1; if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) privacy = 1; if (bss->caps & IEEE80211_CAP_PRIVACY) return privacy; return !privacy; } static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss, int debug_print) { struct wpa_ie_data ie; int proto_match = 0; const u8 *rsn_ie, *wpa_ie; int ret; #ifdef CONFIG_WEP int wep_ok; #endif /* CONFIG_WEP */ ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); if (ret >= 0) return ret; #ifdef CONFIG_WEP /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */ wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) && (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) && ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) || (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); #endif /* CONFIG_WEP */ rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse failed"); break; } if (!ie.has_pairwise) ie.pairwise_cipher = wpa_default_rsn_cipher(bss->freq); if (!ie.has_group) ie.group_cipher = wpa_default_rsn_cipher(bss->freq); #ifdef CONFIG_WEP if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN in RSN IE"); return 1; } #endif /* CONFIG_WEP */ if (!(ie.proto & ssid->proto) && !(ssid->proto & WPA_PROTO_OSEN)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK cipher mismatch"); break; } if (ssid->group_mgmt_cipher && !(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - group mgmt cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt mismatch"); break; } if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt frame protection"); break; } if ((ie.capabilities & WPA_CAPABILITY_MFPR) && wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt frame protection enabled but AP requires it"); break; } if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); return 1; } if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED && (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - MFP Required but network not MFP Capable"); return 0; } wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse failed"); break; } #ifdef CONFIG_WEP if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN in WPA IE"); return 1; } #endif /* CONFIG_WEP */ if (!(ie.proto & ssid->proto)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt mismatch"); break; } if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE"); return 1; } if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie && !rsn_ie) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X"); return 1; } #ifdef CONFIG_OWE if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only && !wpa_ie && !rsn_ie) { if (wpa_s->owe_transition_select && wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE) && ssid->owe_transition_bss_select_count + 1 <= MAX_OWE_TRANSITION_BSS_SELECT_COUNT) { ssid->owe_transition_bss_select_count++; if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip OWE transition BSS (selection count %d does not exceed %d)", ssid->owe_transition_bss_select_count, MAX_OWE_TRANSITION_BSS_SELECT_COUNT); wpa_s->owe_transition_search = 1; return 0; } if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " allow in OWE transition mode"); return 1; } #endif /* CONFIG_OWE */ if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); return 0; } if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN"); return 1; } if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); return 1; } if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with WPA/WPA2"); return 0; } static int freq_allowed(int *freqs, int freq) { int i; if (freqs == NULL) return 1; for (i = 0; freqs[i]; i++) if (freqs[i] == freq) return 1; return 0; } static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss, int debug_print) { const struct hostapd_hw_modes *mode = NULL, *modes; const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES }; const u8 *rate_ie; int i, j, k; if (bss->freq == 0) return 1; /* Cannot do matching without knowing band */ modes = wpa_s->hw.modes; if (modes == NULL) { /* * The driver does not provide any additional information * about the utilized hardware, so allow the connection attempt * to continue. */ return 1; } for (i = 0; i < wpa_s->hw.num_modes; i++) { for (j = 0; j < modes[i].num_channels; j++) { int freq = modes[i].channels[j].freq; if (freq == bss->freq) { if (mode && mode->mode == HOSTAPD_MODE_IEEE80211G) break; /* do not allow 802.11b replace * 802.11g */ mode = &modes[i]; break; } } } if (mode == NULL) return 0; for (i = 0; i < (int) sizeof(scan_ie); i++) { rate_ie = wpa_bss_get_ie(bss, scan_ie[i]); if (rate_ie == NULL) continue; for (j = 2; j < rate_ie[1] + 2; j++) { int flagged = !!(rate_ie[j] & 0x80); int r = (rate_ie[j] & 0x7f) * 5; /* * IEEE Std 802.11n-2009 7.3.2.2: * The new BSS Membership selector value is encoded * like a legacy basic rate, but it is not a rate and * only indicates if the BSS members are required to * support the mandatory features of Clause 20 [HT PHY] * in order to join the BSS. */ if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)) { if (!ht_supported(mode)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " hardware does not support HT PHY"); return 0; } continue; } /* There's also a VHT selector for 802.11ac */ if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) { if (!vht_supported(mode)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " hardware does not support VHT PHY"); return 0; } continue; } #ifdef CONFIG_SAE if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) { if (wpa_s->conf->sae_pwe == 0 && !ssid->sae_password_id && wpa_key_mgmt_sae(ssid->key_mgmt)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " SAE H2E disabled"); #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->ignore_sae_h2e_only) { wpa_dbg(wpa_s, MSG_DEBUG, "TESTING: Ignore SAE H2E requirement mismatch"); continue; } #endif /* CONFIG_TESTING_OPTIONS */ return 0; } continue; } #endif /* CONFIG_SAE */ if (!flagged) continue; /* check for legacy basic rates */ for (k = 0; k < mode->num_rates; k++) { if (mode->rates[k] == r) break; } if (k == mode->num_rates) { /* * IEEE Std 802.11-2007 7.3.2.2 demands that in * order to join a BSS all required rates * have to be supported by the hardware. */ if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)", r / 10, r % 10, bss->freq, mode->mode, mode->num_rates); return 0; } } } return 1; } /* * Test whether BSS is in an ESS. * This is done differently in DMG (60 GHz) and non-DMG bands */ static int bss_is_ess(struct wpa_bss *bss) { if (bss_is_dmg(bss)) { return (bss->caps & IEEE80211_CAP_DMG_MASK) == IEEE80211_CAP_DMG_AP; } return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) == IEEE80211_CAP_ESS); } static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask) { size_t i; for (i = 0; i < ETH_ALEN; i++) { if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i])) return 0; } return 1; } static int addr_in_list(const u8 *addr, const u8 *list, size_t num) { size_t i; for (i = 0; i < num; i++) { const u8 *a = list + i * ETH_ALEN * 2; const u8 *m = a + ETH_ALEN; if (match_mac_mask(a, addr, m)) return 1; } return 0; } static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 **ret_ssid, size_t *ret_ssid_len) { #ifdef CONFIG_OWE const u8 *owe, *pos, *end, *bssid; u8 ssid_len; struct wpa_bss *open_bss; owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN)) return; pos = owe + 6; end = owe + 2 + owe[1]; if (end - pos < ETH_ALEN + 1) return; bssid = pos; pos += ETH_ALEN; ssid_len = *pos++; if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) return; /* Match the profile SSID against the OWE transition mode SSID on the * open network. */ wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR " SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len)); *ret_ssid = pos; *ret_ssid_len = ssid_len; if (!(bss->flags & WPA_BSS_OWE_TRANSITION)) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (wpas_network_disabled(wpa_s, ssid)) continue; if (ssid->ssid_len == ssid_len && os_memcmp(ssid->ssid, pos, ssid_len) == 0) { /* OWE BSS in transition mode for a currently * enabled OWE network. */ wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode OWE SSID for active OWE profile"); bss->flags |= WPA_BSS_OWE_TRANSITION; break; } } } if (bss->ssid_len > 0) return; open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid); if (!open_bss) return; if (ssid_len != open_bss->ssid_len || os_memcmp(pos, open_bss->ssid, ssid_len) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode SSID mismatch: %s", wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len)); return; } owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE); if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) { wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode open BSS unexpected info"); return; } pos = owe + 6; end = owe + 2 + owe[1]; if (end - pos < ETH_ALEN + 1) return; if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID mismatch: " MACSTR, MAC2STR(pos)); return; } pos += ETH_ALEN; ssid_len = *pos++; if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) return; wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s", wpa_ssid_txt(pos, ssid_len)); os_memcpy(bss->ssid, pos, ssid_len); bss->ssid_len = ssid_len; bss->flags |= WPA_BSS_OWE_TRANSITION; #endif /* CONFIG_OWE */ } static int disabled_freq(struct wpa_supplicant *wpa_s, int freq) { int i, j; if (!wpa_s->hw.modes || !wpa_s->hw.num_modes) return 0; for (j = 0; j < wpa_s->hw.num_modes; j++) { struct hostapd_hw_modes *mode = &wpa_s->hw.modes[j]; for (i = 0; i < mode->num_channels; i++) { struct hostapd_channel_data *chan = &mode->channels[i]; if (chan->freq == freq) return !!(chan->flag & HOSTAPD_CHAN_DISABLED); } } return 1; } static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *match_ssid, size_t match_ssid_len, struct wpa_bss *bss, int bssid_ignore_count, bool debug_print); #ifdef CONFIG_SAE_PK static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s, struct wpa_bss *orig_bss, struct wpa_ssid *ssid, const u8 *match_ssid, size_t match_ssid_len) { struct wpa_bss *bss; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { int count; const u8 *ie; if (bss == orig_bss) continue; ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (!(ieee802_11_rsnx_capab(ie, WLAN_RSNX_CAPAB_SAE_PK))) continue; /* TODO: Could be more thorough in checking what kind of * signal strength or throughput estimate would be acceptable * compared to the originally selected BSS. */ if (bss->est_throughput < 2000) return false; count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid); if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len, bss, count, 0)) return true; } return false; } #endif /* CONFIG_SAE_PK */ static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *match_ssid, size_t match_ssid_len, struct wpa_bss *bss, int bssid_ignore_count, bool debug_print) { int res; bool wpa, check_ssid, osen, rsn_osen = false; struct wpa_ie_data data; #ifdef CONFIG_MBO const u8 *assoc_disallow; #endif /* CONFIG_MBO */ #ifdef CONFIG_SAE u8 rsnxe_capa = 0; #endif /* CONFIG_SAE */ const u8 *ie; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa = ie && ie[1]; ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); wpa |= ie && ie[1]; if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 && (data.key_mgmt & WPA_KEY_MGMT_OSEN)) rsn_osen = true; ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); osen = ie != NULL; #ifdef CONFIG_SAE ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (ie && ie[1] >= 1) rsnxe_capa = ie[2]; #endif /* CONFIG_SAE */ check_ssid = wpa || ssid->ssid_len > 0; if (wpas_network_disabled(wpa_s, ssid)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); return false; } res = wpas_temp_disabled(wpa_s, ssid); if (res > 0) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled temporarily for %d second(s)", res); return false; } #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && bssid_ignore_count) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID ignored (WPS)"); return false; } if (wpa && ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = false; if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Only allow wildcard SSID match if an AP advertises active * WPS operation that matches our mode. */ check_ssid = ssid->ssid_len > 0 || !wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss); } #endif /* CONFIG_WPS */ if (ssid->bssid_set && ssid->ssid_len == 0 && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) check_ssid = false; if (check_ssid && (match_ssid_len != ssid->ssid_len || os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); return false; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch"); return false; } /* check the list of BSSIDs to ignore */ if (ssid->num_bssid_ignore && addr_in_list(bss->bssid, ssid->bssid_ignore, ssid->num_bssid_ignore)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID configured to be ignored"); return false; } /* if there is a list of accepted BSSIDs, only accept those APs */ if (ssid->num_bssid_accept && !addr_in_list(bss->bssid, ssid->bssid_accept, ssid->num_bssid_accept)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID not in list of accepted values"); return false; } if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, debug_print)) return false; if (!osen && !wpa && !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network not allowed"); return false; } #ifdef CONFIG_WEP if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && has_wep_key(ssid)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 AP for WEP network block"); return false; } #endif /* CONFIG_WEP */ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && !rsn_osen) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network not allowed"); return false; } if (!wpa_supplicant_match_privacy(bss, ssid)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy mismatch"); return false; } if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) && !bss_is_pbss(bss)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS, PBSS, or MBSS"); return false; } if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - PBSS mismatch (ssid %d bss %d)", ssid->pbss, bss_is_pbss(bss)); return false; } if (!freq_allowed(ssid->freq_list, bss->freq)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not allowed"); return false; } #ifdef CONFIG_MESH if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 && ssid->frequency != bss->freq) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not allowed (mesh)"); return false; } #endif /* CONFIG_MESH */ if (!rate_match(wpa_s, ssid, bss, debug_print)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do not match"); return false; } #ifdef CONFIG_SAE if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) && wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) && !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - SAE H2E required, but not supported by the AP"); return false; } #endif /* CONFIG_SAE */ #ifdef CONFIG_SAE_PK if (ssid->sae_pk == SAE_PK_MODE_ONLY && !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK))) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - SAE-PK required, but not supported by the AP"); return false; } #endif /* CONFIG_SAE_PK */ #ifndef CONFIG_IBSS_RSN if (ssid->mode == WPAS_MODE_IBSS && !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS RSN not supported in the build"); return false; } #endif /* !CONFIG_IBSS_RSN */ #ifdef CONFIG_P2P if (ssid->p2p_group && !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen"); return false; } if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) { struct wpabuf *p2p_ie; u8 dev_addr[ETH_ALEN]; ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); if (!ie) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element"); return false; } p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); if (!p2p_ie) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element"); return false; } if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 || os_memcmp(dev_addr, ssid->go_p2p_dev_addr, ETH_ALEN) != 0) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element"); wpabuf_free(p2p_ie); return false; } wpabuf_free(p2p_ie); } /* * TODO: skip the AP if its P2P IE has Group Formation bit set in the * P2P Group Capability Bitmap and we are not in Group Formation with * that device. */ #endif /* CONFIG_P2P */ if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) { struct os_reltime diff; os_reltime_sub(&wpa_s->scan_min_time, &bss->last_update, &diff); if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - scan result not recent enough (%u.%06u seconds too old)", (unsigned int) diff.sec, (unsigned int) diff.usec); return false; } #ifdef CONFIG_MBO #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->ignore_assoc_disallow) goto skip_assoc_disallow; #endif /* CONFIG_TESTING_OPTIONS */ assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW); if (assoc_disallow && assoc_disallow[1] >= 1) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - MBO association disallowed (reason %u)", assoc_disallow[2]); return false; } if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - AP temporarily disallowed"); return false; } #ifdef CONFIG_TESTING_OPTIONS skip_assoc_disallow: #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_MBO */ #ifdef CONFIG_DPP if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) && !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) && (!ssid->dpp_connector || !ssid->dpp_netaccesskey || !ssid->dpp_csign)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no PMKSA entry for DPP"); return false; } #endif /* CONFIG_DPP */ #ifdef CONFIG_SAE_PK if (ssid->sae_pk == SAE_PK_MODE_AUTOMATIC && wpa_key_mgmt_sae(ssid->key_mgmt) && ((ssid->sae_password && sae_pk_valid_password(ssid->sae_password)) || (!ssid->sae_password && ssid->passphrase && sae_pk_valid_password(ssid->passphrase))) && !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) && sae_pk_acceptable_bss_with_pk(wpa_s, bss, ssid, match_ssid, match_ssid_len)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - another acceptable BSS with SAE-PK in the same ESS"); return false; } #endif /* CONFIG_SAE_PK */ if (bss->ssid_len == 0) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no SSID known for the BSS"); return false; } /* Matching configuration found */ return true; } struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, int only_first_ssid, int debug_print) { u8 wpa_ie_len, rsn_ie_len; const u8 *ie; struct wpa_ssid *ssid; int osen; const u8 *match_ssid; size_t match_ssid_len; int bssid_ignore_count; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); osen = ie != NULL; if (debug_print) { wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq, wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "", (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ? " p2p" : "", osen ? " osen=1" : ""); } bssid_ignore_count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid); if (bssid_ignore_count) { int limit = 1; if (wpa_supplicant_enabled_networks(wpa_s) == 1) { /* * When only a single network is enabled, we can * trigger BSSID ignoring on the first failure. This * should not be done with multiple enabled networks to * avoid getting forced to move into a worse ESS on * single error if there are no other BSSes of the * current ESS. */ limit = 0; } if (bssid_ignore_count > limit) { if (debug_print) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID ignored (count=%d limit=%d)", bssid_ignore_count, limit); } return NULL; } } match_ssid = bss->ssid; match_ssid_len = bss->ssid_len; owe_trans_ssid(wpa_s, bss, &match_ssid, &match_ssid_len); if (match_ssid_len == 0) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); return NULL; } if (disallowed_bssid(wpa_s, bss->bssid)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed"); return NULL; } if (disallowed_ssid(wpa_s, match_ssid, match_ssid_len)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed"); return NULL; } if (disabled_freq(wpa_s, bss->freq)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - channel disabled"); return NULL; } for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) { if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len, bss, bssid_ignore_count, debug_print)) return ssid; } /* No matching configuration found */ return NULL; } static struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid, int only_first_ssid) { unsigned int i; if (wpa_s->current_ssid) { struct wpa_ssid *ssid; wpa_dbg(wpa_s, MSG_DEBUG, "Scan results matching the currently selected network"); for (i = 0; i < wpa_s->last_scan_res_used; i++) { struct wpa_bss *bss = wpa_s->last_scan_res[i]; ssid = wpa_scan_res_match(wpa_s, i, bss, group, only_first_ssid, 0); if (ssid != wpa_s->current_ssid) continue; wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR " freq=%d level=%d snr=%d est_throughput=%u", i, MAC2STR(bss->bssid), bss->freq, bss->level, bss->snr, bss->est_throughput); } } if (only_first_ssid) wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d", group->id); else wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", group->priority); for (i = 0; i < wpa_s->last_scan_res_used; i++) { struct wpa_bss *bss = wpa_s->last_scan_res[i]; wpa_s->owe_transition_select = 1; *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group, only_first_ssid, 1); wpa_s->owe_transition_select = 0; if (!*selected_ssid) continue; wpa_dbg(wpa_s, MSG_DEBUG, " selected %sBSS " MACSTR " ssid='%s'", bss == wpa_s->current_bss ? "current ": "", MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len)); return bss; } return NULL; } struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid) { struct wpa_bss *selected = NULL; size_t prio; struct wpa_ssid *next_ssid = NULL; struct wpa_ssid *ssid; if (wpa_s->last_scan_res == NULL || wpa_s->last_scan_res_used == 0) return NULL; /* no scan results from last update */ if (wpa_s->next_ssid) { /* check that next_ssid is still valid */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == wpa_s->next_ssid) break; } next_ssid = ssid; wpa_s->next_ssid = NULL; } while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { if (next_ssid && next_ssid->priority == wpa_s->conf->pssid[prio]->priority) { selected = wpa_supplicant_select_bss( wpa_s, next_ssid, selected_ssid, 1); if (selected) break; } selected = wpa_supplicant_select_bss( wpa_s, wpa_s->conf->pssid[prio], selected_ssid, 0); if (selected) break; } if (selected == NULL && wpa_s->bssid_ignore && !wpa_s->countermeasures) { wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear BSSID ignore list and try again"); wpa_bssid_ignore_clear(wpa_s); wpa_s->bssid_ignore_cleared = true; } else if (selected == NULL) break; } ssid = *selected_ssid; if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set && !ssid->passphrase && !ssid->ext_psk) { const char *field_name, *txt = NULL; wpa_dbg(wpa_s, MSG_DEBUG, "PSK/passphrase not yet available for the selected network"); wpas_notify_network_request(wpa_s, ssid, WPA_CTRL_REQ_PSK_PASSPHRASE, NULL); field_name = wpa_supplicant_ctrl_req_to_string( WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt); if (field_name == NULL) return NULL; wpas_send_ctrl_req(wpa_s, ssid, field_name, txt); selected = NULL; } return selected; } static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, int timeout_sec, int timeout_usec) { if (!wpa_supplicant_enabled_networks(wpa_s)) { /* * No networks are enabled; short-circuit request so * we don't wait timeout seconds before transitioning * to INACTIVE state. */ wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request " "since there are no enabled networks"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; } wpa_s->scan_for_connection = 1; wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec); } int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); wpas_notify_wps_event_pbc_overlap(wpa_s); #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || wpa_s->p2p_in_provisioning) { eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s, NULL); return -1; } #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS wpas_wps_pbc_overlap(wpa_s); wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Considering connect request: reassociate: %d selected: " MACSTR " bssid: " MACSTR " pending: " MACSTR " wpa_state: %s ssid=%p current_ssid=%p", wpa_s->reassociate, MAC2STR(selected->bssid), MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), wpa_supplicant_state_txt(wpa_s->wpa_state), ssid, wpa_s->current_ssid); /* * Do not trigger new association unless the BSSID has changed or if * reassociation is requested. If we are in process of associating with * the selected BSSID, do not trigger new attempt. */ if (wpa_s->reassociate || (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && ((wpa_s->wpa_state != WPA_ASSOCIATING && wpa_s->wpa_state != WPA_AUTHENTICATING) || (!is_zero_ether_addr(wpa_s->pending_bssid) && os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != 0) || (is_zero_ether_addr(wpa_s->pending_bssid) && ssid != wpa_s->current_ssid)))) { if (wpa_supplicant_scard_init(wpa_s, ssid)) { wpa_supplicant_req_new_scan(wpa_s, 10, 0); return 0; } wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR, MAC2STR(selected->bssid)); wpa_supplicant_associate(wpa_s, selected, ssid); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to " "connect with the selected AP"); } return 0; } static struct wpa_ssid * wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) { size_t prio; struct wpa_ssid *ssid; for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext) { if (wpas_network_disabled(wpa_s, ssid)) continue; #ifndef CONFIG_IBSS_RSN if (ssid->mode == WPAS_MODE_IBSS && !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) { wpa_msg(wpa_s, MSG_INFO, "IBSS RSN not supported in the build - cannot use the profile for SSID '%s'", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); continue; } #endif /* !CONFIG_IBSS_RSN */ if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_MESH) return ssid; } } return NULL; } /* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based * on BSS added and BSS changed events */ static void wpa_supplicant_rsn_preauth_scan_results( struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; if (rsn_preauth_scan_results(wpa_s->wpa) < 0) return; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { const u8 *ssid, *rsn; ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID); if (ssid == NULL) continue; rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (rsn == NULL) continue; rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn); } } #ifndef CONFIG_NO_ROAMING static int wpas_get_snr_signal_info(u32 frequency, int avg_signal, int noise) { if (noise == WPA_INVALID_NOISE) noise = IS_5GHZ(frequency) ? DEFAULT_NOISE_FLOOR_5GHZ : DEFAULT_NOISE_FLOOR_2GHZ; return avg_signal - noise; } static unsigned int wpas_get_est_throughput_from_bss_snr(const struct wpa_supplicant *wpa_s, const struct wpa_bss *bss, int snr) { int rate = wpa_bss_get_max_rate(bss); const u8 *ies = wpa_bss_ie_ptr(bss); size_t ie_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, bss->freq); } int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss, struct wpa_bss *selected) { int min_diff, diff; int to_5ghz; int cur_level; unsigned int cur_est, sel_est; struct wpa_signal_info si; int cur_snr = 0; int ret = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation"); wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " freq=%d level=%d snr=%d est_throughput=%u", MAC2STR(current_bss->bssid), current_bss->freq, current_bss->level, current_bss->snr, current_bss->est_throughput); wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " freq=%d level=%d snr=%d est_throughput=%u", MAC2STR(selected->bssid), selected->freq, selected->level, selected->snr, selected->est_throughput); if (wpa_s->current_ssid->bssid_set && os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS " "has preferred BSSID"); return 1; } cur_level = current_bss->level; cur_est = current_bss->est_throughput; sel_est = selected->est_throughput; /* * Try to poll the signal from the driver since this will allow to get * more accurate values. In some cases, there can be big differences * between the RSSI of the Probe Response frames of the AP we are * associated with and the Beacon frames we hear from the same AP after * association. This can happen, e.g., when there are two antennas that * hear the AP very differently. If the driver chooses to hear the * Probe Response frames during the scan on the "bad" antenna because * it wants to save power, but knows to choose the other antenna after * association, we will hear our AP with a low RSSI as part of the * scan even when we can hear it decently on the other antenna. To cope * with this, ask the driver to teach us how it hears the AP. Also, the * scan results may be a bit old, since we can very quickly get fresh * information about our currently associated AP. */ if (wpa_drv_signal_poll(wpa_s, &si) == 0 && (si.avg_beacon_signal || si.avg_signal)) { cur_level = si.avg_beacon_signal ? si.avg_beacon_signal : si.avg_signal; cur_snr = wpas_get_snr_signal_info(si.frequency, cur_level, si.current_noise); cur_est = wpas_get_est_throughput_from_bss_snr(wpa_s, current_bss, cur_snr); wpa_dbg(wpa_s, MSG_DEBUG, "Using signal poll values for the current BSS: level=%d snr=%d est_throughput=%u", cur_level, cur_snr, cur_est); } if (sel_est > cur_est + 5000) { wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS has better estimated throughput"); return 1; } to_5ghz = selected->freq > 4000 && current_bss->freq < 4000; if (cur_level < 0 && cur_level > selected->level + to_5ghz * 2 && sel_est < cur_est * 1.2) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " "signal level"); return 0; } if (cur_est > sel_est + 5000) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better estimated throughput"); return 0; } if (cur_snr > GREAT_SNR) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has good SNR (%u > %u)", cur_snr, GREAT_SNR); return 0; } if (cur_level < -85) /* ..-86 dBm */ min_diff = 1; else if (cur_level < -80) /* -85..-81 dBm */ min_diff = 2; else if (cur_level < -75) /* -80..-76 dBm */ min_diff = 3; else if (cur_level < -70) /* -75..-71 dBm */ min_diff = 4; else if (cur_level < 0) /* -70..-1 dBm */ min_diff = 5; else /* unspecified units (not in dBm) */ min_diff = 2; if (cur_est > sel_est * 1.5) min_diff += 10; else if (cur_est > sel_est * 1.2) min_diff += 5; else if (cur_est > sel_est * 1.1) min_diff += 2; else if (cur_est > sel_est) min_diff++; else if (sel_est > cur_est * 1.5) min_diff -= 10; else if (sel_est > cur_est * 1.2) min_diff -= 5; else if (sel_est > cur_est * 1.1) min_diff -= 2; else if (sel_est > cur_est) min_diff--; if (to_5ghz) min_diff -= 2; diff = selected->level - cur_level; if (diff < min_diff) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference in signal level (%d < %d)", diff, min_diff); ret = 0; } else { wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation due to difference in signal level (%d >= %d)", diff, min_diff); ret = 1; } wpa_msg_ctrl(wpa_s, MSG_INFO, "%scur_bssid=" MACSTR " cur_freq=%d cur_level=%d cur_est=%d sel_bssid=" MACSTR " sel_freq=%d sel_level=%d sel_est=%d", ret ? WPA_EVENT_DO_ROAM : WPA_EVENT_SKIP_ROAM, MAC2STR(current_bss->bssid), current_bss->freq, cur_level, cur_est, MAC2STR(selected->bssid), selected->freq, selected->level, sel_est); return ret; } #endif /* CONFIG_NO_ROAMING */ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { struct wpa_bss *current_bss = NULL; if (wpa_s->reassociate) return 1; /* explicit request to reassociate */ if (wpa_s->wpa_state < WPA_ASSOCIATED) return 1; /* we are not associated; continue */ if (wpa_s->current_ssid == NULL) return 1; /* unknown current SSID */ if (wpa_s->current_ssid != ssid) return 1; /* different network block */ if (wpas_driver_bss_selection(wpa_s)) return 0; /* Driver-based roaming */ if (wpa_s->current_ssid->ssid) current_bss = wpa_bss_get(wpa_s, wpa_s->bssid, wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len); if (!current_bss) current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid); if (!current_bss) return 1; /* current BSS not seen in scan results */ if (current_bss == selected) return 0; if (selected->last_update_idx > current_bss->last_update_idx) return 1; /* current BSS not seen in the last scan */ #ifndef CONFIG_NO_ROAMING return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, selected); #else /* CONFIG_NO_ROAMING */ return 0; #endif /* CONFIG_NO_ROAMING */ } /* * Return a negative value if no scan results could be fetched or if scan * results should not be shared with other virtual interfaces. * Return 0 if scan results were fetched and may be shared with other * interfaces. * Return 1 if scan results may be shared with other virtual interfaces but may * not trigger any operations. * Return 2 if the interface was removed and cannot be used. */ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data, int own_request, int update_only) { struct wpa_scan_results *scan_res = NULL; int ret = 0; int ap = 0; #ifndef CONFIG_NO_RANDOM_POOL size_t i, num; #endif /* CONFIG_NO_RANDOM_POOL */ #ifdef CONFIG_AP if (wpa_s->ap_iface) ap = 1; #endif /* CONFIG_AP */ wpa_supplicant_notify_scanning(wpa_s, 0); scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1); if (scan_res == NULL) { if (wpa_s->conf->ap_scan == 2 || ap || wpa_s->scan_res_handler == scan_only_handler) return -1; if (!own_request) return -1; if (data && data->scan_info.external_scan) return -1; if (wpa_s->scan_res_fail_handler) { void (*handler)(struct wpa_supplicant *wpa_s); handler = wpa_s->scan_res_fail_handler; wpa_s->scan_res_fail_handler = NULL; handler(wpa_s); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); } ret = -1; goto scan_work_done; } #ifndef CONFIG_NO_RANDOM_POOL num = scan_res->num; if (num > 10) num = 10; for (i = 0; i < num; i++) { u8 buf[5]; struct wpa_scan_res *res = scan_res->res[i]; buf[0] = res->bssid[5]; buf[1] = res->qual & 0xff; buf[2] = res->noise & 0xff; buf[3] = res->level & 0xff; buf[4] = res->tsf & 0xff; random_add_randomness(buf, sizeof(buf)); } #endif /* CONFIG_NO_RANDOM_POOL */ if (update_only) { ret = 1; goto scan_work_done; } if (own_request && wpa_s->scan_res_handler && !(data && data->scan_info.external_scan)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; scan_res_handler(wpa_s, scan_res); ret = 1; goto scan_work_done; } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", wpa_s->own_scan_running, data ? data->scan_info.external_scan : 0); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id && wpa_s->own_scan_running && own_request && !(data && data->scan_info.external_scan)) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; } else { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); if (ap) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode"); #ifdef CONFIG_AP if (wpa_s->ap_iface->scan_cb) wpa_s->ap_iface->scan_cb(wpa_s->ap_iface); #endif /* CONFIG_AP */ goto scan_work_done; } if (data && data->scan_info.external_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; } if (wnm_scan_process(wpa_s, 1) > 0) goto scan_work_done; if (sme_proc_obss_scan(wpa_s, scan_res) > 0) goto scan_work_done; if (own_request && data && wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0) goto scan_work_done; if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) goto scan_work_done; if (autoscan_notify_scan(wpa_s, scan_res)) goto scan_work_done; if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); goto scan_work_done; } if (!wpas_driver_bss_selection(wpa_s) && bgscan_notify_scan(wpa_s, scan_res) == 1) goto scan_work_done; wpas_wps_update_ap_info(wpa_s, scan_res); if (wpa_s->wpa_state >= WPA_AUTHENTICATING && wpa_s->wpa_state < WPA_COMPLETED) goto scan_work_done; wpa_scan_results_free(scan_res); if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } os_free(wpa_s->last_scan_freqs); wpa_s->last_scan_freqs = NULL; wpa_s->num_last_scan_freqs = 0; if (own_request && data && data->scan_info.freqs && data->scan_info.num_freqs) { wpa_s->last_scan_freqs = os_malloc(sizeof(int) * data->scan_info.num_freqs); if (wpa_s->last_scan_freqs) { os_memcpy(wpa_s->last_scan_freqs, data->scan_info.freqs, sizeof(int) * data->scan_info.num_freqs); wpa_s->num_last_scan_freqs = data->scan_info.num_freqs; } } return wpas_select_network_from_last_scan(wpa_s, 1, own_request); scan_work_done: wpa_scan_results_free(scan_res); if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } return ret; } static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, int new_scan, int own_request) { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; int time_to_reenable = wpas_reenabled_network_time(wpa_s); if (time_to_reenable > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Postpone network selection by %d seconds since all networks are disabled", time_to_reenable); eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); eloop_register_timeout(time_to_reenable, 0, wpas_network_reenabled, wpa_s, NULL); return 0; } if (wpa_s->p2p_mgmt) return 0; /* no normal connection on p2p_mgmt interface */ wpa_s->owe_transition_search = 0; selected = wpa_supplicant_pick_network(wpa_s, &ssid); #ifdef CONFIG_MESH if (wpa_s->ifmsh) { wpa_msg(wpa_s, MSG_INFO, "Avoiding join because we already joined a mesh group"); return 0; } #endif /* CONFIG_MESH */ if (selected) { int skip; skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); if (skip) { if (new_scan) wpa_supplicant_rsn_preauth_scan_results(wpa_s); return 0; } wpa_s->suitable_network++; if (ssid != wpa_s->current_ssid && wpa_s->wpa_state >= WPA_AUTHENTICATING) { wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); return -1; } if (new_scan) wpa_supplicant_rsn_preauth_scan_results(wpa_s); /* * Do not allow other virtual radios to trigger operations based * on these scan results since we do not want them to start * other associations at the same time. */ return 1; } else { wpa_s->no_suitable_network++; wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network"); wpa_supplicant_associate(wpa_s, NULL, ssid); if (new_scan) wpa_supplicant_rsn_preauth_scan_results(wpa_s); } else if (own_request) { /* * No SSID found. If SCAN results are as a result of * own scan request and not due to a scan request on * another shared interface, try another scan. */ int timeout_sec = wpa_s->scan_interval; int timeout_usec = 0; #ifdef CONFIG_P2P int res; res = wpas_p2p_scan_no_go_seen(wpa_s); if (res == 2) return 2; if (res == 1) return 0; if (wpa_s->p2p_in_provisioning || wpa_s->show_group_started || wpa_s->p2p_in_invitation) { /* * Use shorter wait during P2P Provisioning * state and during P2P join-a-group operation * to speed up group formation. */ timeout_sec = 0; timeout_usec = 250000; wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); return 0; } #endif /* CONFIG_P2P */ #ifdef CONFIG_INTERWORKING if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking && wpa_s->conf->cred) { wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: " "start ANQP fetch since no matching " "networks found"); wpa_s->network_select = 1; wpa_s->auto_network_select = 1; interworking_start_fetch_anqp(wpa_s); return 1; } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_WPS if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing"); timeout_sec = 0; timeout_usec = 500000; wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); return 0; } #endif /* CONFIG_WPS */ #ifdef CONFIG_OWE if (wpa_s->owe_transition_search) { wpa_dbg(wpa_s, MSG_DEBUG, "OWE: Use shorter wait during transition mode search"); timeout_sec = 0; timeout_usec = 500000; wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); return 0; } #endif /* CONFIG_OWE */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_NETWORK_NOT_FOUND); } } return 0; } static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_supplicant *ifs; int res; res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0); if (res == 2) { /* * Interface may have been removed, so must not dereference * wpa_s after this. */ return 1; } if (res < 0) { /* * If no scan results could be fetched, then no need to * notify those interfaces that did not actually request * this scan. Similarly, if scan results started a new operation on this * interface, do not notify other interfaces to avoid concurrent * operations during a connection attempt. */ return 0; } /* * Check other interfaces to see if they share the same radio. If * so, they get updated with this same scan info. */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { if (ifs != wpa_s) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); res = _wpa_supplicant_event_scan_results(ifs, data, 0, res > 0); if (res < 0) return 0; } } return 0; } #endif /* CONFIG_NO_SCAN_PROCESSING */ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_NO_SCAN_PROCESSING return -1; #else /* CONFIG_NO_SCAN_PROCESSING */ struct os_reltime now; wpa_s->ignore_post_flush_scan_res = 0; if (wpa_s->last_scan_res_used == 0) return -1; os_get_reltime(&now); if (os_reltime_expired(&now, &wpa_s->last_scan, wpa_s->conf->scan_res_valid_for_connect)) { wpa_printf(MSG_DEBUG, "Fast associate: Old scan results"); return -1; } return wpas_select_network_from_last_scan(wpa_s, 0, 1); #endif /* CONFIG_NO_SCAN_PROCESSING */ } #ifdef CONFIG_WNM static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->wpa_state < WPA_ASSOCIATED) return; if (!wpa_s->no_keep_alive) { wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR, MAC2STR(wpa_s->bssid)); /* TODO: could skip this if normal data traffic has been sent */ /* TODO: Consider using some more appropriate data frame for * this */ if (wpa_s->l2) l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800, (u8 *) "", 0); } #ifdef CONFIG_SME if (wpa_s->sme.bss_max_idle_period) { unsigned int msec; msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */ if (msec > 100) msec -= 100; eloop_register_timeout(msec / 1000, msec % 1000 * 1000, wnm_bss_keep_alive, wpa_s, NULL); } #endif /* CONFIG_SME */ } static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len) { struct ieee802_11_elems elems; if (ies == NULL) return; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) return; #ifdef CONFIG_SME if (elems.bss_max_idle_period) { unsigned int msec; wpa_s->sme.bss_max_idle_period = WPA_GET_LE16(elems.bss_max_idle_period); wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 " "TU)%s", wpa_s->sme.bss_max_idle_period, (elems.bss_max_idle_period[2] & 0x01) ? " (protected keep-live required)" : ""); if (wpa_s->sme.bss_max_idle_period == 0) wpa_s->sme.bss_max_idle_period = 1; if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); /* msec times 1000 */ msec = wpa_s->sme.bss_max_idle_period * 1024; if (msec > 100) msec -= 100; eloop_register_timeout(msec / 1000, msec % 1000 * 1000, wnm_bss_keep_alive, wpa_s, NULL); } } #endif /* CONFIG_SME */ } #endif /* CONFIG_WNM */ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WNM eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); #endif /* CONFIG_WNM */ } #ifdef CONFIG_INTERWORKING static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map, size_t len) { int res; wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len); res = wpa_drv_set_qos_map(wpa_s, qos_map, len); if (res) { wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver"); } return res; } static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len) { struct ieee802_11_elems elems; if (ies == NULL) return; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) return; if (elems.qos_map_set) { wpas_qos_map_set(wpa_s, elems.qos_map_set, elems.qos_map_set_len); } } #endif /* CONFIG_INTERWORKING */ static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len) { struct ieee802_11_elems elems; const u8 *map_sub_elem, *pos; size_t len; wpa_s->multi_ap_ie = 0; if (!ies || ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed || !elems.multi_ap || elems.multi_ap_len < 7) return; pos = elems.multi_ap + 4; len = elems.multi_ap_len - 4; map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE); if (!map_sub_elem || map_sub_elem[1] < 1) return; wpa_s->multi_ap_backhaul = !!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS); wpa_s->multi_ap_fronthaul = !!(map_sub_elem[2] & MULTI_AP_FRONTHAUL_BSS); wpa_s->multi_ap_ie = 1; } static void multi_ap_set_4addr_mode(struct wpa_supplicant *wpa_s) { if (!wpa_s->current_ssid || !wpa_s->current_ssid->multi_ap_backhaul_sta) return; if (!wpa_s->multi_ap_ie) { wpa_printf(MSG_INFO, "AP does not include valid Multi-AP element"); goto fail; } if (!wpa_s->multi_ap_backhaul) { if (wpa_s->multi_ap_fronthaul && wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) { wpa_printf(MSG_INFO, "WPS active, accepting fronthaul-only BSS"); /* Don't set 4addr mode in this case, so just return */ return; } wpa_printf(MSG_INFO, "AP doesn't support backhaul BSS"); goto fail; } if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) { wpa_printf(MSG_ERROR, "Failed to set 4addr mode"); goto fail; } wpa_s->enabled_4addr_mode = 1; return; fail: wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } #ifdef CONFIG_FST static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len) { struct mb_ies_info mb_ies; if (!ie || !ie_len || !wpa_s->fst) return -ENOENT; os_memset(&mb_ies, 0, sizeof(mb_ies)); while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) { size_t len; len = 2 + ie[1]; if (len > ie_len) { wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found", ie, ie_len); break; } if (ie[0] == WLAN_EID_MULTI_BAND) { wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", (unsigned int) len); mb_ies.ies[mb_ies.nof_ies].ie = ie + 2; mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2; mb_ies.nof_ies++; } ie_len -= len; ie += len; } if (mb_ies.nof_ies > 0) { wpabuf_free(wpa_s->received_mb_ies); wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); return 0; } return -ENOENT; } #endif /* CONFIG_FST */ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int l, len, found = 0, found_x = 0, wpa_found, rsn_found; const u8 *p; u8 bssid[ETH_ALEN]; bool bssid_known; wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); bssid_known = wpa_drv_get_bssid(wpa_s, bssid) == 0; if (data->assoc_info.req_ies) wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, data->assoc_info.req_ies_len); if (data->assoc_info.resp_ies) { wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #ifdef CONFIG_TDLS wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_TDLS */ #ifdef CONFIG_WNM wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_WNM */ #ifdef CONFIG_INTERWORKING interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_INTERWORKING */ if (wpa_s->hw_capab == CAPAB_VHT && get_ie(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) wpa_s->ieee80211ac = 1; multi_ap_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", data->assoc_info.beacon_ies, data->assoc_info.beacon_ies_len); if (data->assoc_info.freq) wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); wpa_s->connection_set = 0; if (data->assoc_info.req_ies && data->assoc_info.resp_ies) { struct ieee802_11_elems req_elems, resp_elems; if (ieee802_11_parse_elems(data->assoc_info.req_ies, data->assoc_info.req_ies_len, &req_elems, 0) != ParseFailed && ieee802_11_parse_elems(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, &resp_elems, 0) != ParseFailed) { wpa_s->connection_set = 1; wpa_s->connection_ht = req_elems.ht_capabilities && resp_elems.ht_capabilities; /* Do not include subset of VHT on 2.4 GHz vendor * extension in consideration for reporting VHT * association. */ wpa_s->connection_vht = req_elems.vht_capabilities && resp_elems.vht_capabilities && (!data->assoc_info.freq || wpas_freq_to_band(data->assoc_info.freq) != BAND_2_4_GHZ); wpa_s->connection_he = req_elems.he_capabilities && resp_elems.he_capabilities; } } p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ while (p && l >= 2) { len = p[1] + 2; if (len > l) { wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", p, l); break; } if (!found && ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 && (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) || (p[0] == WLAN_EID_RSN && p[1] >= 2))) { if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) break; found = 1; wpa_find_assoc_pmkid(wpa_s); } if (!found_x && p[0] == WLAN_EID_RSNX) { if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len)) break; found_x = 1; } l -= len; p += len; } if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); if (!found_x && data->assoc_info.req_ies) wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); #ifdef CONFIG_FILS #ifdef CONFIG_SME if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS || wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) && (!data->assoc_info.resp_frame || fils_process_assoc_resp(wpa_s->wpa, data->assoc_info.resp_frame, data->assoc_info.resp_frame_len) < 0)) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); return -1; } #endif /* CONFIG_SME */ /* Additional processing for FILS when SME is in driver */ if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1); #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && (!bssid_known || owe_process_assoc_resp(wpa_s->wpa, bssid, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len) < 0)) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); return -1; } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 wpa_sm_set_dpp_z(wpa_s->wpa, NULL); if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->dpp_pfs) { struct ieee802_11_elems elems; if (ieee802_11_parse_elems(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, &elems, 0) == ParseFailed || !elems.owe_dh) goto no_pfs; if (dpp_pfs_process(wpa_s->dpp_pfs, elems.owe_dh, elems.owe_dh_len) < 0) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); return -1; } wpa_sm_set_dpp_z(wpa_s->wpa, wpa_s->dpp_pfs->secret); } no_pfs: #endif /* CONFIG_DPP2 */ #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { if (!bssid_known || wpa_ft_validate_reassoc_resp(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, bssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " "Reassociation Response failed"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; } } p = data->assoc_info.resp_ies; l = data->assoc_info.resp_ies_len; #ifdef CONFIG_WPS_STRICT if (p && wpa_s->current_ssid && wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE); if (wps == NULL) { wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not " "include WPS IE in (Re)Association Response"); return -1; } if (wps_validate_assoc_resp(wps) < 0) { wpabuf_free(wps); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; } wpabuf_free(wps); } #endif /* CONFIG_WPS_STRICT */ /* Go through the IEs and make a copy of the MDIE, if present. */ while (p && l >= 2) { len = p[1] + 2; if (len > l) { wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", p, l); break; } if (p[0] == WLAN_EID_MOBILITY_DOMAIN && p[1] >= MOBILITY_DOMAIN_ID_LEN) { wpa_s->sme.ft_used = 1; os_memcpy(wpa_s->sme.mobility_domain, p + 2, MOBILITY_DOMAIN_ID_LEN); break; } l -= len; p += len; } #endif /* CONFIG_SME */ /* Process FT when SME is in the driver */ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && wpa_ft_is_completed(wpa_s->wpa)) { if (!bssid_known || wpa_ft_validate_reassoc_resp(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, bssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " "Reassociation Response failed"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done"); } wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_IEEE80211R */ if (bssid_known) wpas_handle_assoc_resp_mscs(wpa_s, bssid, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); /* WPA/RSN IE from Beacon/ProbeResp */ p = data->assoc_info.beacon_ies; l = data->assoc_info.beacon_ies_len; /* Go through the IEs and make a copy of the WPA/RSN IEs, if present. */ wpa_found = rsn_found = 0; while (p && l >= 2) { len = p[1] + 2; if (len > l) { wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies", p, l); break; } if (!wpa_found && p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) { wpa_found = 1; wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len); } if (!rsn_found && p[0] == WLAN_EID_RSN && p[1] >= 2) { rsn_found = 1; wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len); } if (p[0] == WLAN_EID_RSNX && p[1] >= 1) wpa_sm_set_ap_rsnxe(wpa_s->wpa, p, len); l -= len; p += len; } if (!wpa_found && data->assoc_info.beacon_ies) wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); if (!rsn_found && data->assoc_info.beacon_ies) { wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0); } if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; if (wpa_s->assoc_freq && data->assoc_info.freq && wpa_s->assoc_freq != data->assoc_info.freq) { wpa_printf(MSG_DEBUG, "Operating frequency changed from " "%u to %u MHz", wpa_s->assoc_freq, data->assoc_info.freq); wpa_supplicant_update_scan_results(wpa_s); } wpa_s->assoc_freq = data->assoc_info.freq; return 0; } static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) { const u8 *bss_wpa = NULL, *bss_rsn = NULL, *bss_rsnx = NULL; if (!wpa_s->current_bss || !wpa_s->current_ssid) return -1; if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt)) return 0; bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss, WPA_IE_VENDOR_TYPE); bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN); bss_rsnx = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSNX); if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, bss_wpa ? 2 + bss_wpa[1] : 0) || wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, bss_rsn ? 2 + bss_rsn[1] : 0) || wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx, bss_rsnx ? 2 + bss_rsnx[1] : 0)) return -1; return 0; } static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { #ifdef CONFIG_FST struct assoc_info *ai = data ? &data->assoc_info : NULL; struct wpa_bss *bss = wpa_s->current_bss; const u8 *ieprb, *iebcn; wpabuf_free(wpa_s->received_mb_ies); wpa_s->received_mb_ies = NULL; if (ai && !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) { wpa_printf(MSG_DEBUG, "FST: MB IEs updated from Association Response frame"); return; } if (ai && !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) { wpa_printf(MSG_DEBUG, "FST: MB IEs updated from association event Beacon IEs"); return; } if (!bss) return; ieprb = wpa_bss_ie_ptr(bss); iebcn = ieprb + bss->ie_len; if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len)) wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE"); else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len)) wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE"); #endif /* CONFIG_FST */ } static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; int ft_completed, already_authorized; int new_bss = 0; #if defined(CONFIG_FILS) || defined(CONFIG_MBO) struct wpa_bss *bss; #endif /* CONFIG_FILS || CONFIG_MBO */ #ifdef CONFIG_AP if (wpa_s->ap_iface) { if (!data) return; hostapd_notif_assoc(wpa_s->ap_iface->bss[0], data->assoc_info.addr, data->assoc_info.req_ies, data->assoc_info.req_ies_len, data->assoc_info.reassoc); return; } #endif /* CONFIG_AP */ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); wpa_s->own_reconnect_req = 0; ft_completed = wpa_ft_is_completed(wpa_s->wpa); if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; /* * FILS authentication can share the same mechanism to mark the * connection fully authenticated, so set ft_completed also based on * FILS result. */ if (!ft_completed) ft_completed = wpa_fils_is_completed(wpa_s->wpa); if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { if (os_reltime_initialized(&wpa_s->session_start)) { os_reltime_age(&wpa_s->session_start, &wpa_s->session_length); wpa_s->session_start.sec = 0; wpa_s->session_start.usec = 0; wpas_notify_session_length(wpa_s); } else { wpas_notify_auth_changed(wpa_s); os_get_reltime(&wpa_s->session_start); } wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); new_bss = 1; random_add_randomness(bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); wpas_notify_bssid_changed(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); } if (wpa_supplicant_select_config(wpa_s) < 0) { wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } } multi_ap_set_4addr_mode(wpa_s); if (wpa_s->conf->ap_scan == 1 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss) wpa_msg(wpa_s, MSG_WARNING, "WPA/RSN IEs not updated"); } wpas_fst_update_mb_assoc(wpa_s, data); #ifdef CONFIG_SME os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); wpa_s->sme.prev_bssid_set = 1; wpa_s->sme.last_unprot_disconnect.sec = 0; #endif /* CONFIG_SME */ wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid)); if (wpa_s->current_ssid) { /* When using scanning (ap_scan=1), SIM PC/SC interface can be * initialized before association, but for other modes, * initialize PC/SC here, if the current configuration needs * smartcard or SIM/USIM. */ wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid); } wpa_sm_notify_assoc(wpa_s->wpa, bssid); if (wpa_s->l2) l2_packet_notify_auth_start(wpa_s->l2); already_authorized = data && data->assoc_info.authorized; /* * Set portEnabled first to false in order to get EAP state machine out * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE * state machine may transit to AUTHENTICATING state based on obsolete * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to * AUTHENTICATED without ever giving chance to EAP state machine to * reset the state. */ if (!ft_completed && !already_authorized) { eapol_sm_notify_portEnabled(wpa_s->eapol, false); eapol_sm_notify_portValid(wpa_s->eapol, false); } if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_DPP || wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed || already_authorized || wpa_s->drv_authorized_port) eapol_sm_notify_eap_success(wpa_s->eapol, false); /* 802.1X::portControl = Auto */ eapol_sm_notify_portEnabled(wpa_s->eapol, true); wpa_s->eapol_received = 0; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_IBSS)) { if (wpa_s->current_ssid && wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { /* * Set the key after having received joined-IBSS event * from the driver. */ wpa_supplicant_set_wpa_none_key(wpa_s, wpa_s->current_ssid); } wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); } else if (!ft_completed) { /* Timeout for receiving the first EAPOL packet */ wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); } wpa_supplicant_cancel_scan(wpa_s); if (ft_completed) { /* * FT protocol completed - make sure EAPOL state machine ends * up in authenticated. */ wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, true); eapol_sm_notify_eap_success(wpa_s->eapol, true); } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { /* * We are done; the driver will take care of RSN 4-way * handshake. */ wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, true); eapol_sm_notify_eap_success(wpa_s->eapol, true); } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) && wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { /* * The driver will take care of RSN 4-way handshake, so we need * to allow EAPOL supplicant to complete its work without * waiting for WPA supplicant. */ eapol_sm_notify_portValid(wpa_s->eapol, true); } wpa_s->last_eapol_matches_bssid = 0; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->rsne_override_eapol) { wpa_printf(MSG_DEBUG, "TESTING: RSNE EAPOL-Key msg 2/4 override"); wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, wpabuf_head(wpa_s->rsne_override_eapol), wpabuf_len(wpa_s->rsne_override_eapol)); } if (wpa_s->rsnxe_override_eapol) { wpa_printf(MSG_DEBUG, "TESTING: RSNXE EAPOL-Key msg 2/4 override"); wpa_sm_set_assoc_rsnxe(wpa_s->wpa, wpabuf_head(wpa_s->rsnxe_override_eapol), wpabuf_len(wpa_s->rsnxe_override_eapol)); } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->pending_eapol_rx) { struct os_reltime now, age; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age); if (age.sec == 0 && age.usec < 200000 && os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL " "frame that was received just before " "association notification"); wpa_supplicant_rx_eapol( wpa_s, wpa_s->pending_eapol_rx_src, wpabuf_head(wpa_s->pending_eapol_rx), wpabuf_len(wpa_s->pending_eapol_rx)); } wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = NULL; } #ifdef CONFIG_WEP if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) && wpa_s->current_ssid && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); } #endif /* CONFIG_WEP */ #ifdef CONFIG_IBSS_RSN if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_IBSS && wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE && wpa_s->ibss_rsn == NULL) { wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid); if (!wpa_s->ibss_rsn) { wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); } #endif /* CONFIG_IBSS_RSN */ wpas_wps_notify_assoc(wpa_s, bssid); if (data) { wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, &data->assoc_info.wmm_params); if (wpa_s->reassoc_same_bss) wmm_ac_restore_tspecs(wpa_s); } #if defined(CONFIG_FILS) || defined(CONFIG_MBO) bss = wpa_bss_get_bssid(wpa_s, bssid); #endif /* CONFIG_FILS || CONFIG_MBO */ #ifdef CONFIG_FILS if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) { const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss); if (fils_cache_id) wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id); } #endif /* CONFIG_FILS */ #ifdef CONFIG_MBO wpas_mbo_check_pmf(wpa_s, bss, wpa_s->current_ssid); #endif /* CONFIG_MBO */ #ifdef CONFIG_DPP2 wpa_s->dpp_pfs_fallback = 0; #endif /* CONFIG_DPP2 */ } static int disconnect_reason_recoverable(u16 reason_code) { return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY || reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA || reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA; } static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, u16 reason_code, int locally_generated) { const u8 *bssid; if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* * At least Host AP driver and a Prism3 card seemed to be * generating streams of disconnected events when configuring * IBSS for WPA-None. Ignore them for now. */ return; } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; if (!is_zero_ether_addr(bssid) || wpa_s->wpa_state >= WPA_AUTHENTICATING) { wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d%s", MAC2STR(bssid), reason_code, locally_generated ? " locally_generated=1" : ""); } } static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code, int locally_generated) { if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE || !wpa_s->new_connection || !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_key_mgmt_sae(wpa_s->key_mgmt)) return 0; /* Not in initial 4-way handshake with PSK */ /* * It looks like connection was lost while trying to go through PSK * 4-way handshake. Filter out known disconnection cases that are caused * by something else than PSK mismatch to avoid confusing reports. */ if (locally_generated) { if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS) return 0; } return 1; } static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, u16 reason_code, int locally_generated) { const u8 *bssid; int authenticating; u8 prev_pending_bssid[ETH_ALEN]; struct wpa_bss *fast_reconnect = NULL; struct wpa_ssid *fast_reconnect_ssid = NULL; struct wpa_ssid *last_ssid; struct wpa_bss *curr = NULL; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* * At least Host AP driver and a Prism3 card seemed to be * generating streams of disconnected events when configuring * IBSS for WPA-None. Ignore them for now. */ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in " "IBSS/WPA-None mode"); return; } if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING && reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY && locally_generated) /* * Remove the inactive AP (which is probably out of range) from * the BSS list after marking disassociation. In particular * mac80211-based drivers use the * WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in * locally generated disconnection events for cases where the * AP does not reply anymore. */ curr = wpa_s->current_bss; if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); if (wpas_p2p_4way_hs_failed(wpa_s) > 0) return; /* P2P group removed */ wpas_auth_failed(wpa_s, "WRONG_KEY"); #ifdef CONFIG_DPP2 wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_AUTH_FAILURE); #endif /* CONFIG_DPP2 */ } if (!wpa_s->disconnected && (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS || wpas_wps_searching(wpa_s) || wpas_wps_reenable_networks_pending(wpa_s))) { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " "reconnect (wps=%d/%d wpa_state=%d)", wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, wpas_wps_searching(wpa_s), wpa_s->wpa_state); if (wpa_s->wpa_state == WPA_COMPLETED && wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_INFRA && (wpa_s->own_reconnect_req || (!locally_generated && disconnect_reason_recoverable(reason_code)))) { /* * It looks like the AP has dropped association with * us, but could allow us to get back in. This is also * triggered for cases where local reconnection request * is used to force reassociation with the same BSS. * Try to reconnect to the same BSS without a full scan * to save time for some common cases. */ fast_reconnect = wpa_s->current_bss; fast_reconnect_ssid = wpa_s->current_ssid; } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) { wpa_supplicant_req_scan(wpa_s, 0, 100000); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new " "immediate scan"); } } else { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not " "try to re-connect"); wpa_s->reassociate = 0; wpa_s->disconnected = 1; if (!wpa_s->pno) wpa_supplicant_cancel_sched_scan(wpa_s); } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpas_connection_failed(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE); if (locally_generated) wpa_s->disconnect_reason = -reason_code; else wpa_s->disconnect_reason = reason_code; wpas_notify_disconnect_reason(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys"); wpa_clear_keys(wpa_s, wpa_s->bssid); } last_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); if (curr) wpa_bss_remove(wpa_s, curr, "Connection to AP lost"); if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); wpa_s->current_ssid = last_ssid; } if (fast_reconnect && !wpas_network_disabled(wpa_s, fast_reconnect_ssid) && !disallowed_bssid(wpa_s, fast_reconnect->bssid) && !disallowed_ssid(wpa_s, fast_reconnect->ssid, fast_reconnect->ssid_len) && !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) && !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect)) { #ifndef CONFIG_NO_SCAN_PROCESSING wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); if (wpa_supplicant_connect(wpa_s, fast_reconnect, fast_reconnect_ssid) < 0) { /* Recover through full scan */ wpa_supplicant_req_scan(wpa_s, 0, 100000); } #endif /* CONFIG_NO_SCAN_PROCESSING */ } else if (fast_reconnect) { /* * Could not reconnect to the same BSS due to network being * disabled. Use a new scan to match the alternative behavior * above, i.e., to continue automatic reconnection attempt in a * way that enforces disabled network rules. */ wpa_supplicant_req_scan(wpa_s, 0, 100000); } } #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (!wpa_s->pending_mic_error_report) return; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report"); wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); wpa_s->pending_mic_error_report = 0; } #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ static void wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int pairwise; struct os_reltime t; wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); pairwise = (data && data->michael_mic_failure.unicast); os_get_reltime(&t); if ((wpa_s->last_michael_mic_error.sec && !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) || wpa_s->pending_mic_error_report) { if (wpa_s->pending_mic_error_report) { /* * Send the pending MIC error report immediately since * we are going to start countermeasures and AP better * do the same. */ wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); } /* Send the new MIC error report immediately since we are going * to start countermeasures and AP better do the same. */ wpa_sm_key_request(wpa_s->wpa, 1, pairwise); /* initialize countermeasures */ wpa_s->countermeasures = 1; wpa_bssid_ignore_add(wpa_s, wpa_s->bssid); wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); /* * Need to wait for completion of request frame. We do not get * any callback for the message completion, so just wait a * short while and hope for the best. */ os_sleep(0, 10000); wpa_drv_set_countermeasures(wpa_s, 1); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_MICHAEL_MIC_FAILURE); eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); eloop_register_timeout(60, 0, wpa_supplicant_stop_countermeasures, wpa_s, NULL); /* TODO: mark the AP rejected for 60 second. STA is * allowed to associate with another AP.. */ } else { #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT if (wpa_s->mic_errors_seen) { /* * Reduce the effectiveness of Michael MIC error * reports as a means for attacking against TKIP if * more than one MIC failure is noticed with the same * PTK. We delay the transmission of the reports by a * random time between 0 and 60 seconds in order to * force the attacker wait 60 seconds before getting * the information on whether a frame resulted in a MIC * failure. */ u8 rval[4]; int sec; if (os_get_random(rval, sizeof(rval)) < 0) sec = os_random() % 60; else sec = WPA_GET_BE32(rval) % 60; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error " "report %d seconds", sec); wpa_s->pending_mic_error_report = 1; wpa_s->pending_mic_error_pairwise = pairwise; eloop_cancel_timeout( wpa_supplicant_delayed_mic_error_report, wpa_s, NULL); eloop_register_timeout( sec, os_random() % 1000000, wpa_supplicant_delayed_mic_error_report, wpa_s, NULL); } else { wpa_sm_key_request(wpa_s->wpa, 1, pairwise); } #else /* CONFIG_DELAYED_MIC_ERROR_REPORT */ wpa_sm_key_request(wpa_s->wpa, 1, pairwise); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ } wpa_s->last_michael_mic_error = t; wpa_s->mic_errors_seen++; } #ifdef CONFIG_TERMINATE_ONLASTIF static int any_interfaces(struct wpa_supplicant *head) { struct wpa_supplicant *wpa_s; for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next) if (!wpa_s->interface_removed) return 1; return 0; } #endif /* CONFIG_TERMINATE_ONLASTIF */ static void wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0) return; switch (data->interface_status.ievent) { case EVENT_INTERFACE_ADDED: if (!wpa_s->interface_removed) break; wpa_s->interface_removed = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added"); if (wpa_supplicant_driver_init(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " "driver after interface was added"); } #ifdef CONFIG_P2P if (!wpa_s->global->p2p && !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && wpas_p2p_add_p2pdev_interface( wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { wpa_printf(MSG_INFO, "P2P: Failed to enable P2P Device interface"); /* Try to continue without. P2P will be disabled. */ } #endif /* CONFIG_P2P */ break; case EVENT_INTERFACE_REMOVED: wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); wpa_s->interface_removed = 1; wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; #ifdef CONFIG_P2P if (wpa_s->global->p2p && wpa_s->global->p2p_init_wpa_s->parent == wpa_s && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) { wpa_dbg(wpa_s, MSG_DEBUG, "Removing P2P Device interface"); wpa_supplicant_remove_iface( wpa_s->global, wpa_s->global->p2p_init_wpa_s, 0); wpa_s->global->p2p_init_wpa_s = NULL; } #endif /* CONFIG_P2P */ #ifdef CONFIG_MATCH_IFACE if (wpa_s->matched) { wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); break; } #endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) eloop_terminate(); #endif /* CONFIG_TERMINATE_ONLASTIF */ break; } } #ifdef CONFIG_TDLS static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; switch (data->tdls.oper) { case TDLS_REQUEST_SETUP: wpa_tdls_remove(wpa_s->wpa, data->tdls.peer); if (wpa_tdls_is_external_setup(wpa_s->wpa)) wpa_tdls_start(wpa_s->wpa, data->tdls.peer); else wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer); break; case TDLS_REQUEST_TEARDOWN: if (wpa_tdls_is_external_setup(wpa_s->wpa)) wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer, data->tdls.reason_code); else wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, data->tdls.peer); break; case TDLS_REQUEST_DISCOVER: wpa_tdls_send_discovery_request(wpa_s->wpa, data->tdls.peer); break; } } #endif /* CONFIG_TDLS */ #ifdef CONFIG_WNM static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; switch (data->wnm.oper) { case WNM_OPER_SLEEP: wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request " "(action=%d, intval=%d)", data->wnm.sleep_action, data->wnm.sleep_intval); ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action, data->wnm.sleep_intval, NULL); break; } } #endif /* CONFIG_WNM */ #ifdef CONFIG_IEEE80211R static void wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) return; if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies, data->ft_ies.ies_len, data->ft_ies.ft_action, data->ft_ies.target_ap, data->ft_ies.ric_ies, data->ft_ies.ric_ies_len) < 0) { /* TODO: prevent MLME/driver from trying to associate? */ } } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IBSS_RSN static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid; if (wpa_s->wpa_state < WPA_ASSOCIATED) return; if (data == NULL) return; ssid = wpa_s->current_ssid; if (ssid == NULL) return; if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) return; ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); } static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) return; /* check if the ssid is correctly configured as IBSS/RSN */ if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) return; ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame, data->rx_mgmt.frame_len); } #endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_IEEE80211R static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, size_t len) { const u8 *sta_addr, *target_ap_addr; u16 status; wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len); if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) return; /* only SME case supported for now */ if (len < 1 + 2 * ETH_ALEN + 2) return; if (data[0] != 2) return; /* Only FT Action Response is supported for now */ sta_addr = data + 1; target_ap_addr = data + 1 + ETH_ALEN; status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN); wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR " TargetAP " MACSTR " status %u", MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR " in FT Action Response", MAC2STR(sta_addr)); return; } if (status) { wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates " "failure (status code %d)", status); /* TODO: report error to FT code(?) */ return; } if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2, len - (1 + 2 * ETH_ALEN + 2), 1, target_ap_addr, NULL, 0) < 0) return; #ifdef CONFIG_SME { struct wpa_bss *bss; bss = wpa_bss_get_bssid(wpa_s, target_ap_addr); if (bss) wpa_s->sme.freq = bss->freq; wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT; sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr, WLAN_AUTH_FT); } #endif /* CONFIG_SME */ } #endif /* CONFIG_IEEE80211R */ static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s, struct unprot_deauth *e) { wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame " "dropped: " MACSTR " -> " MACSTR " (reason code %u)", MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); } static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s, struct unprot_disassoc *e) { wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame " "dropped: " MACSTR " -> " MACSTR " (reason code %u)", MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); } static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr, u16 reason_code, int locally_generated, const u8 *ie, size_t ie_len, int deauth) { #ifdef CONFIG_AP if (wpa_s->ap_iface && addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr); return; } if (wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode"); return; } #endif /* CONFIG_AP */ if (!locally_generated) wpa_s->own_disconnect_req = 0; wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated); if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && eapol_sm_failed(wpa_s->eapol))) && !wpa_s->eap_expected_failure)) wpas_auth_failed(wpa_s, "AUTH_FAILED"); #ifdef CONFIG_P2P if (deauth && reason_code > 0) { if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len, locally_generated) > 0) { /* * The interface was removed, so cannot continue * processing any additional operations after this. */ return; } } #endif /* CONFIG_P2P */ wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, locally_generated); } static void wpas_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info) { u16 reason_code = 0; int locally_generated = 0; const u8 *addr = NULL; const u8 *ie = NULL; size_t ie_len = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); if (info) { addr = info->addr; ie = info->ie; ie_len = info->ie_len; reason_code = info->reason_code; locally_generated = info->locally_generated; wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s", reason_code, reason2str(reason_code), locally_generated ? " locally_generated=1" : ""); if (addr) wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(addr)); wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", ie, ie_len); } #ifdef CONFIG_AP if (wpa_s->ap_iface && info && info->addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr); return; } if (wpa_s->ap_iface) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode"); return; } #endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (info) { wpas_p2p_disassoc_notif( wpa_s, info->addr, reason_code, info->ie, info->ie_len, locally_generated); } #endif /* CONFIG_P2P */ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_disassoc(wpa_s, info); wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, ie, ie_len, 0); } static void wpas_event_deauth(struct wpa_supplicant *wpa_s, struct deauth_info *info) { u16 reason_code = 0; int locally_generated = 0; const u8 *addr = NULL; const u8 *ie = NULL; size_t ie_len = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification"); if (info) { addr = info->addr; ie = info->ie; ie_len = info->ie_len; reason_code = info->reason_code; locally_generated = info->locally_generated; wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s", reason_code, reason2str(reason_code), locally_generated ? " locally_generated=1" : ""); if (addr) { wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(addr)); } wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)", ie, ie_len); } wpa_reset_ft_completed(wpa_s->wpa); wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, ie, ie_len, 1); } static const char * reg_init_str(enum reg_change_initiator init) { switch (init) { case REGDOM_SET_BY_CORE: return "CORE"; case REGDOM_SET_BY_USER: return "USER"; case REGDOM_SET_BY_DRIVER: return "DRIVER"; case REGDOM_SET_BY_COUNTRY_IE: return "COUNTRY_IE"; case REGDOM_BEACON_HINT: return "BEACON_HINT"; } return "?"; } static const char * reg_type_str(enum reg_type type) { switch (type) { case REGDOM_TYPE_UNKNOWN: return "UNKNOWN"; case REGDOM_TYPE_COUNTRY: return "COUNTRY"; case REGDOM_TYPE_WORLD: return "WORLD"; case REGDOM_TYPE_CUSTOM_WORLD: return "CUSTOM_WORLD"; case REGDOM_TYPE_INTERSECTION: return "INTERSECTION"; } return "?"; } void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, struct channel_list_changed *info) { struct wpa_supplicant *ifs; u8 dfs_domain; /* * To allow backwards compatibility with higher level layers that * assumed the REGDOM_CHANGE event is sent over the initially added * interface. Find the highest parent of this interface and use it to * send the event. */ for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent) ; if (info) { wpa_msg(ifs, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", reg_init_str(info->initiator), reg_type_str(info->type), info->alpha2[0] ? " alpha2=" : "", info->alpha2[0] ? info->alpha2 : ""); } if (wpa_s->drv_priv == NULL) return; /* Ignore event during drv initialization */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { wpa_printf(MSG_DEBUG, "%s: Updating hw mode", ifs->ifname); free_hw_features(ifs); ifs->hw.modes = wpa_drv_get_hw_feature_data( ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain); /* Restart PNO/sched_scan with updated channel list */ if (ifs->pno) { wpas_stop_pno(ifs); wpas_start_pno(ifs); } else if (ifs->sched_scanning && !ifs->pno_sched_pending) { wpa_dbg(ifs, MSG_DEBUG, "Channel list changed - restart sched_scan"); wpas_scan_restart_sched_scan(ifs); } } wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER); } static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, const u8 *frame, size_t len, int freq, int rssi) { const struct ieee80211_mgmt *mgmt; const u8 *payload; size_t plen; u8 category; if (len < IEEE80211_HDRLEN + 2) return; mgmt = (const struct ieee80211_mgmt *) frame; payload = frame + IEEE80211_HDRLEN; category = *payload++; plen = len - IEEE80211_HDRLEN - 1; wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR " Category=%u DataLen=%d freq=%d MHz", MAC2STR(mgmt->sa), category, (int) plen, freq); if (category == WLAN_ACTION_WMM) { wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen); return; } #ifdef CONFIG_IEEE80211R if (category == WLAN_ACTION_FT) { ft_rx_action(wpa_s, payload, plen); return; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SME if (category == WLAN_ACTION_SA_QUERY) { sme_sa_query_rx(wpa_s, mgmt->da, mgmt->sa, payload, plen); return; } #endif /* CONFIG_SME */ #ifdef CONFIG_WNM if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action(wpa_s, mgmt, len); return; } #endif /* CONFIG_WNM */ #ifdef CONFIG_GAS if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC || mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) && gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid, mgmt->u.action.category, payload, plen, freq) == 0) return; #endif /* CONFIG_GAS */ #ifdef CONFIG_GAS_SERVER if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC || mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) && gas_server_rx(wpa_s->gas_server, mgmt->da, mgmt->sa, mgmt->bssid, mgmt->u.action.category, payload, plen, freq) == 0) return; #endif /* CONFIG_GAS_SERVER */ #ifdef CONFIG_TDLS if (category == WLAN_ACTION_PUBLIC && plen >= 4 && payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery Response from " MACSTR, MAC2STR(mgmt->sa)); return; } #endif /* CONFIG_TDLS */ #ifdef CONFIG_INTERWORKING if (category == WLAN_ACTION_QOS && plen >= 1 && payload[0] == QOS_QOS_MAP_CONFIG) { const u8 *pos = payload + 1; size_t qlen = plen - 1; wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from " MACSTR, MAC2STR(mgmt->sa)); if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 && qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET && pos[1] <= qlen - 2 && pos[1] >= 16) wpas_qos_map_set(wpa_s, pos + 2, pos[1]); return; } #endif /* CONFIG_INTERWORKING */ if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) { wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa, mgmt->da, payload + 1, plen - 1); return; } if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); return; } if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) { wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa, payload + 1, plen - 1, rssi); return; } #ifdef CONFIG_FST if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) { fst_rx_action(wpa_s->fst, mgmt, len); return; } #endif /* CONFIG_FST */ #ifdef CONFIG_DPP if (category == WLAN_ACTION_PUBLIC && plen >= 5 && payload[0] == WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(&payload[1]) == OUI_WFA && payload[4] == DPP_OUI_TYPE) { payload++; plen--; wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq); return; } #endif /* CONFIG_DPP */ if (category == WLAN_ACTION_ROBUST_AV_STREAMING && payload[0] == ROBUST_AV_MSCS_RESP) { wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa, payload + 1, plen - 1); return; } wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) mesh_mpm_action_rx(wpa_s, mgmt, len); } static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, union wpa_event_data *event) { struct wpa_freq_range_list *list; char *str = NULL; list = &event->freq_range; if (list->num) str = freq_range_list_str(list); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s", str ? str : ""); #ifdef CONFIG_P2P if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) { wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range", __func__); } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event"); /* * The update channel flow will also take care of moving a GO * from the unsafe frequency if needed. */ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_AVOID); } #endif /* CONFIG_P2P */ os_free(str); } static void wpa_supplicant_event_port_authorized(struct wpa_supplicant *wpa_s) { if (wpa_s->wpa_state == WPA_ASSOCIATED) { wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, true); eapol_sm_notify_eap_success(wpa_s->eapol, true); wpa_s->drv_authorized_port = 1; } } static unsigned int wpas_event_cac_ms(const struct wpa_supplicant *wpa_s, int freq) { size_t i; int j; for (i = 0; i < wpa_s->hw.num_modes; i++) { const struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; for (j = 0; j < mode->num_channels; j++) { const struct hostapd_channel_data *chan; chan = &mode->channels[j]; if (chan->freq == freq) return chan->dfs_cac_ms; } } return 0; } static void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { #if defined(NEED_AP_MLME) && defined(CONFIG_AP) if (wpa_s->ap_iface || wpa_s->ifmsh) { wpas_ap_event_dfs_cac_started(wpa_s, radar); } else #endif /* NEED_AP_MLME && CONFIG_AP */ { unsigned int cac_time = wpas_event_cac_ms(wpa_s, radar->freq); cac_time /= 1000; /* convert from ms to sec */ if (!cac_time) cac_time = 10 * 60; /* max timeout: 10 minutes */ /* Restart auth timeout: CAC time added to initial timeout */ wpas_auth_timeout_restart(wpa_s, cac_time); } } static void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { #if defined(NEED_AP_MLME) && defined(CONFIG_AP) if (wpa_s->ap_iface || wpa_s->ifmsh) { wpas_ap_event_dfs_cac_finished(wpa_s, radar); } else #endif /* NEED_AP_MLME && CONFIG_AP */ { /* Restart auth timeout with original value after CAC is * finished */ wpas_auth_timeout_restart(wpa_s, 0); } } static void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { #if defined(NEED_AP_MLME) && defined(CONFIG_AP) if (wpa_s->ap_iface || wpa_s->ifmsh) { wpas_ap_event_dfs_cac_aborted(wpa_s, radar); } else #endif /* NEED_AP_MLME && CONFIG_AP */ { /* Restart auth timeout with original value after CAC is * aborted */ wpas_auth_timeout_restart(wpa_s, 0); } } static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { wpa_dbg(wpa_s, MSG_DEBUG, "Connection authorized by device, previous state %d", wpa_s->wpa_state); wpa_supplicant_event_port_authorized(wpa_s); wpa_s->last_eapol_matches_bssid = 1; wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr); wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck, data->assoc_info.ptk_kck_len, data->assoc_info.ptk_kek, data->assoc_info.ptk_kek_len); #ifdef CONFIG_FILS if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) { struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid); const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss); /* Update ERP next sequence number */ eapol_sm_update_erp_next_seq_num( wpa_s->eapol, data->assoc_info.fils_erp_next_seq_num); if (data->assoc_info.fils_pmk && data->assoc_info.fils_pmkid) { /* Add the new PMK and PMKID to the PMKSA cache */ wpa_sm_pmksa_cache_add(wpa_s->wpa, data->assoc_info.fils_pmk, data->assoc_info.fils_pmk_len, data->assoc_info.fils_pmkid, wpa_s->bssid, fils_cache_id); } else if (data->assoc_info.fils_pmkid) { /* Update the current PMKSA used for this connection */ pmksa_cache_set_current(wpa_s->wpa, data->assoc_info.fils_pmkid, NULL, NULL, 0, NULL, 0); } } #endif /* CONFIG_FILS */ } static const char * connect_fail_reason(enum sta_connect_fail_reason_codes code) { switch (code) { case STA_CONNECT_FAIL_REASON_UNSPECIFIED: return ""; case STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: return "no_bss_found"; case STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: return "auth_tx_fail"; case STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: return "auth_no_ack_received"; case STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: return "auth_no_resp_received"; case STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: return "assoc_req_tx_fail"; case STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: return "assoc_no_ack_received"; case STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: return "assoc_no_resp_received"; default: return "unknown_reason"; } } static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { const u8 *bssid = data->assoc_reject.bssid; #ifdef CONFIG_MBO struct wpa_bss *reject_bss; #endif /* CONFIG_MBO */ if (!bssid || is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; #ifdef CONFIG_MBO if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) reject_bss = wpa_s->current_bss; else reject_bss = wpa_bss_get_bssid(wpa_s, bssid); #endif /* CONFIG_MBO */ if (data->assoc_reject.bssid) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT "bssid=" MACSTR " status_code=%u%s%s%s%s%s", MAC2STR(data->assoc_reject.bssid), data->assoc_reject.status_code, data->assoc_reject.timed_out ? " timeout" : "", data->assoc_reject.timeout_reason ? "=" : "", data->assoc_reject.timeout_reason ? data->assoc_reject.timeout_reason : "", data->assoc_reject.reason_code != STA_CONNECT_FAIL_REASON_UNSPECIFIED ? " qca_driver_reason=" : "", connect_fail_reason(data->assoc_reject.reason_code)); else wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT "status_code=%u%s%s%s%s%s", data->assoc_reject.status_code, data->assoc_reject.timed_out ? " timeout" : "", data->assoc_reject.timeout_reason ? "=" : "", data->assoc_reject.timeout_reason ? data->assoc_reject.timeout_reason : "", data->assoc_reject.reason_code != STA_CONNECT_FAIL_REASON_UNSPECIFIED ? " qca_driver_reason=" : "", connect_fail_reason(data->assoc_reject.reason_code)); wpa_s->assoc_status_code = data->assoc_reject.status_code; wpas_notify_assoc_status_code(wpa_s); #ifdef CONFIG_OWE if (data->assoc_reject.status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && wpa_s->current_ssid && wpa_s->current_ssid->owe_group == 0 && wpa_s->last_owe_group != 21) { struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_bss *bss = wpa_s->current_bss; if (!bss) { bss = wpa_supplicant_get_new_bss(wpa_s, bssid); if (!bss) { wpas_connection_failed(wpa_s, bssid); wpa_supplicant_mark_disassoc(wpa_s); return; } } wpa_printf(MSG_DEBUG, "OWE: Try next supported DH group"); wpas_connect_work_done(wpa_s); wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_connect(wpa_s, bss, ssid); return; } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 /* Try to follow AP's PFS policy. WLAN_STATUS_ASSOC_DENIED_UNSPEC is * the status code defined in the DPP R2 tech spec. * WLAN_STATUS_AKMP_NOT_VALID is addressed in the same manner as an * interoperability workaround with older hostapd implementation. */ if (DPP_VERSION > 1 && wpa_s->current_ssid && (wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP || ((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_DPP) && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)) && wpa_s->current_ssid->dpp_pfs == 0 && (data->assoc_reject.status_code == WLAN_STATUS_ASSOC_DENIED_UNSPEC || data->assoc_reject.status_code == WLAN_STATUS_AKMP_NOT_VALID)) { struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_bss *bss = wpa_s->current_bss; wpa_s->current_ssid->dpp_pfs_fallback ^= 1; if (!bss) bss = wpa_supplicant_get_new_bss(wpa_s, bssid); if (!bss || wpa_s->dpp_pfs_fallback) { wpa_printf(MSG_DEBUG, "DPP: Updated PFS policy for next try"); wpas_connection_failed(wpa_s, bssid); wpa_supplicant_mark_disassoc(wpa_s); return; } wpa_printf(MSG_DEBUG, "DPP: Try again with updated PFS policy"); wpa_s->dpp_pfs_fallback = 1; wpas_connect_work_done(wpa_s); wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_connect(wpa_s, bss, ssid); return; } #endif /* CONFIG_DPP2 */ #ifdef CONFIG_MBO if (data->assoc_reject.status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && reject_bss && data->assoc_reject.resp_ies) { const u8 *rssi_rej; rssi_rej = mbo_get_attr_from_ies( data->assoc_reject.resp_ies, data->assoc_reject.resp_ies_len, OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT); if (rssi_rej && rssi_rej[1] == 2) { wpa_printf(MSG_DEBUG, "OCE: RSSI-based association rejection from " MACSTR " (Delta RSSI: %u, Retry Delay: %u)", MAC2STR(reject_bss->bssid), rssi_rej[2], rssi_rej[3]); wpa_bss_tmp_disallow(wpa_s, reject_bss->bssid, rssi_rej[3], rssi_rej[2] + reject_bss->level); } } #endif /* CONFIG_MBO */ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { sme_event_assoc_reject(wpa_s, data); return; } /* Driver-based SME cases */ #ifdef CONFIG_SAE if (wpa_s->current_ssid && wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt) && !data->assoc_reject.timed_out) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE: Drop PMKSA cache entry"); wpa_sm_aborted_cached(wpa_s->wpa); wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); } #endif /* CONFIG_SAE */ #ifdef CONFIG_DPP if (wpa_s->current_ssid && wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP && !data->assoc_reject.timed_out) { wpa_dbg(wpa_s, MSG_DEBUG, "DPP: Drop PMKSA cache entry"); wpa_sm_aborted_cached(wpa_s->wpa); wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); } #endif /* CONFIG_DPP */ #ifdef CONFIG_FILS /* Update ERP next sequence number */ if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) { + fils_pmksa_cache_flush(wpa_s); eapol_sm_update_erp_next_seq_num( wpa_s->eapol, data->assoc_reject.fils_erp_next_seq_num); fils_connection_failure(wpa_s); } #endif /* CONFIG_FILS */ wpas_connection_failed(wpa_s, bssid); wpa_supplicant_mark_disassoc(wpa_s); } static void wpas_event_unprot_beacon(struct wpa_supplicant *wpa_s, struct unprot_beacon *data) { struct wpabuf *buf; int res; if (!data || wpa_s->wpa_state != WPA_COMPLETED || os_memcmp(data->sa, wpa_s->bssid, ETH_ALEN) != 0) return; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_UNPROT_BEACON MACSTR, MAC2STR(data->sa)); buf = wpabuf_alloc(4); if (!buf) return; wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); wpabuf_put_u8(buf, 1); /* Dialog Token */ wpabuf_put_u8(buf, WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE); res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, wpabuf_head(buf), wpabuf_len(buf), 0); if (res < 0) wpa_printf(MSG_DEBUG, "Failed to send WNM-Notification Request frame"); wpabuf_free(buf); } void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; int resched; struct os_reltime age, clear_at; #ifndef CONFIG_NO_STDOUT_DEBUG int level = MSG_DEBUG; #endif /* CONFIG_NO_STDOUT_DEBUG */ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && event != EVENT_INTERFACE_STATUS && event != EVENT_SCAN_RESULTS && event != EVENT_SCHED_SCAN_STOPPED) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore event %s (%d) while interface is disabled", event_to_string(event), event); return; } #ifndef CONFIG_NO_STDOUT_DEBUG if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) level = MSG_EXCESSIVE; } wpa_dbg(wpa_s, level, "Event %s (%d) received", event_to_string(event), event); #endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_AUTH: #ifdef CONFIG_FST if (!wpas_fst_update_mbie(wpa_s, data->auth.ies, data->auth.ies_len)) wpa_printf(MSG_DEBUG, "FST: MB IEs updated from auth IE"); #endif /* CONFIG_FST */ sme_event_auth(wpa_s, data); wpa_s->auth_status_code = data->auth.status_code; wpas_notify_auth_status_code(wpa_s); break; case EVENT_ASSOC: #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->ignore_auth_resp) { wpa_printf(MSG_INFO, "EVENT_ASSOC - ignore_auth_resp active!"); break; } if (wpa_s->testing_resend_assoc) { wpa_printf(MSG_INFO, "EVENT_DEAUTH - testing_resend_assoc"); break; } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->disconnected) { wpa_printf(MSG_INFO, "Ignore unexpected EVENT_ASSOC in disconnected state"); break; } wpa_supplicant_event_assoc(wpa_s, data); wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS; if (data && (data->assoc_info.authorized || (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && wpa_fils_is_completed(wpa_s->wpa)))) wpa_supplicant_event_assoc_auth(wpa_s, data); if (data) { wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u", data->assoc_info.subnet_status); } break; case EVENT_DISASSOC: wpas_event_disassoc(wpa_s, data ? &data->disassoc_info : NULL); break; case EVENT_DEAUTH: #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->ignore_auth_resp) { wpa_printf(MSG_INFO, "EVENT_DEAUTH - ignore_auth_resp active!"); break; } if (wpa_s->testing_resend_assoc) { wpa_printf(MSG_INFO, "EVENT_DEAUTH - testing_resend_assoc"); break; } #endif /* CONFIG_TESTING_OPTIONS */ wpas_event_deauth(wpa_s, data ? &data->deauth_info : NULL); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_STARTED: if (wpa_s->own_scan_requested || (data && !data->scan_info.external_scan)) { struct os_reltime diff; os_get_reltime(&wpa_s->scan_start_time); os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", diff.sec, diff.usec); wpa_s->own_scan_requested = 0; wpa_s->own_scan_running = 1; if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED "id=%u", wpa_s->manual_scan_id); } else { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } } else { wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); wpa_s->radio->external_scan_req_interface = wpa_s; wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } break; case EVENT_SCAN_RESULTS: if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_s->scan_res_handler = NULL; wpa_s->own_scan_running = 0; wpa_s->radio->external_scan_req_interface = NULL; wpa_s->last_scan_req = NORMAL_SCAN_REQ; break; } if (!(data && data->scan_info.external_scan) && os_reltime_initialized(&wpa_s->scan_start_time)) { struct os_reltime now, diff; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); wpa_s->scan_start_time.sec = 0; wpa_s->scan_start_time.usec = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds", diff.sec, diff.usec); } if (wpa_supplicant_event_scan_results(wpa_s, data)) break; /* interface may have been removed */ if (!(data && data->scan_info.external_scan)) wpa_s->own_scan_running = 0; if (data && data->scan_info.nl_scan_event) wpa_s->radio->external_scan_req_interface = NULL; radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: wpa_supplicant_event_associnfo(wpa_s, data); break; case EVENT_INTERFACE_STATUS: wpa_supplicant_event_interface_status(wpa_s, data); break; case EVENT_PMKID_CANDIDATE: wpa_supplicant_event_pmkid_candidate(wpa_s, data); break; #ifdef CONFIG_TDLS case EVENT_TDLS: wpa_supplicant_event_tdls(wpa_s, data); break; #endif /* CONFIG_TDLS */ #ifdef CONFIG_WNM case EVENT_WNM: wpa_supplicant_event_wnm(wpa_s, data); break; #endif /* CONFIG_WNM */ #ifdef CONFIG_IEEE80211R case EVENT_FT_RESPONSE: wpa_supplicant_event_ft_response(wpa_s, data); break; #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IBSS_RSN case EVENT_IBSS_RSN_START: wpa_supplicant_event_ibss_rsn_start(wpa_s, data); break; #endif /* CONFIG_IBSS_RSN */ case EVENT_ASSOC_REJECT: wpas_event_assoc_reject(wpa_s, data); break; case EVENT_AUTH_TIMED_OUT: /* It is possible to get this event from earlier connection */ if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_MESH) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore AUTH_TIMED_OUT in mesh configuration"); break; } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_auth_timed_out(wpa_s, data); break; case EVENT_ASSOC_TIMED_OUT: /* It is possible to get this event from earlier connection */ if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_MESH) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore ASSOC_TIMED_OUT in mesh configuration"); break; } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_timed_out(wpa_s, data); break; case EVENT_TX_STATUS: wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR " type=%d stype=%d", MAC2STR(data->tx_status.dst), data->tx_status.type, data->tx_status.stype); #ifdef CONFIG_PASN if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_AUTH && wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack) == 0) break; #endif /* CONFIG_PASN */ #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { #ifdef CONFIG_OFFCHANNEL if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_ACTION) offchannel_send_action_tx_status( wpa_s, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? OFFCHANNEL_SEND_ACTION_SUCCESS : OFFCHANNEL_SEND_ACTION_NO_ACK); #endif /* CONFIG_OFFCHANNEL */ break; } #endif /* CONFIG_AP */ #ifdef CONFIG_OFFCHANNEL wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" MACSTR, MAC2STR(wpa_s->p2pdev->pending_action_dst)); /* * Catch TX status events for Action frames we sent via group * interface in GO mode, or via standalone AP interface. * Note, wpa_s->p2pdev will be the same as wpa_s->parent, * except when the primary interface is used as a GO interface * (for drivers which do not have group interface concurrency) */ if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_ACTION && os_memcmp(wpa_s->p2pdev->pending_action_dst, data->tx_status.dst, ETH_ALEN) == 0) { offchannel_send_action_tx_status( wpa_s->p2pdev, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? OFFCHANNEL_SEND_ACTION_SUCCESS : OFFCHANNEL_SEND_ACTION_NO_ACK); break; } #endif /* CONFIG_OFFCHANNEL */ #ifdef CONFIG_AP switch (data->tx_status.type) { case WLAN_FC_TYPE_MGMT: ap_mgmt_tx_cb(wpa_s, data->tx_status.data, data->tx_status.data_len, data->tx_status.stype, data->tx_status.ack); break; case WLAN_FC_TYPE_DATA: ap_tx_status(wpa_s, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack); break; } #endif /* CONFIG_AP */ break; #ifdef CONFIG_AP case EVENT_EAPOL_TX_STATUS: ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst, data->eapol_tx_status.data, data->eapol_tx_status.data_len, data->eapol_tx_status.ack); break; case EVENT_DRIVER_CLIENT_POLL_OK: ap_client_poll_ok(wpa_s, data->client_poll.addr); break; case EVENT_RX_FROM_UNKNOWN: if (wpa_s->ap_iface == NULL) break; ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; #endif /* CONFIG_AP */ case EVENT_CH_SWITCH_STARTED: case EVENT_CH_SWITCH: if (!data || !wpa_s->current_ssid) break; wpa_msg(wpa_s, MSG_INFO, "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d", event == EVENT_CH_SWITCH ? WPA_EVENT_CHANNEL_SWITCH : WPA_EVENT_CHANNEL_SWITCH_STARTED, data->ch_switch.freq, data->ch_switch.ht_enabled, data->ch_switch.ch_offset, channel_width_to_string(data->ch_switch.ch_width), data->ch_switch.cf1, data->ch_switch.cf2); if (event == EVENT_CH_SWITCH_STARTED) break; wpa_s->assoc_freq = data->ch_switch.freq; wpa_s->current_ssid->frequency = data->ch_switch.freq; if (wpa_s->current_bss && wpa_s->current_bss->freq != data->ch_switch.freq) { wpa_s->current_bss->freq = data->ch_switch.freq; notify_bss_changes(wpa_s, WPA_BSS_FREQ_CHANGED_FLAG, wpa_s->current_bss); } #ifdef CONFIG_SME switch (data->ch_switch.ch_offset) { case 1: wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE; break; case -1: wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW; break; default: wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN; break; } #endif /* CONFIG_SME */ #ifdef CONFIG_AP if (wpa_s->current_ssid->mode == WPAS_MODE_AP || wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO || wpa_s->current_ssid->mode == WPAS_MODE_MESH || wpa_s->current_ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, data->ch_switch.ht_enabled, data->ch_switch.ch_offset, data->ch_switch.ch_width, data->ch_switch.cf1, data->ch_switch.cf2, 1); } #endif /* CONFIG_AP */ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_ch_switch(wpa_s); wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS); wnm_clear_coloc_intf_reporting(wpa_s); break; #ifdef CONFIG_AP #ifdef NEED_AP_MLME case EVENT_DFS_RADAR_DETECTED: if (data) wpas_ap_event_dfs_radar_detected(wpa_s, &data->dfs_event); break; case EVENT_DFS_NOP_FINISHED: if (data) wpas_ap_event_dfs_cac_nop_finished(wpa_s, &data->dfs_event); break; #endif /* NEED_AP_MLME */ #endif /* CONFIG_AP */ case EVENT_DFS_CAC_STARTED: if (data) wpas_event_dfs_cac_started(wpa_s, &data->dfs_event); break; case EVENT_DFS_CAC_FINISHED: if (data) wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event); break; case EVENT_DFS_CAC_ABORTED: if (data) wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event); break; case EVENT_RX_MGMT: { u16 fc, stype; const struct ieee80211_mgmt *mgmt; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->ext_mgmt_frame_handling) { struct rx_mgmt *rx = &data->rx_mgmt; size_t hex_len = 2 * rx->frame_len + 1; char *hex = os_malloc(hex_len); if (hex) { wpa_snprintf_hex(hex, hex_len, rx->frame, rx->frame_len); wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s", rx->freq, rx->datarate, rx->ssi_signal, hex); os_free(hex); } break; } #endif /* CONFIG_TESTING_OPTIONS */ mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { #endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (stype == WLAN_FC_STYPE_PROBE_REQ && data->rx_mgmt.frame_len > IEEE80211_HDRLEN) { const u8 *src = mgmt->sa; const u8 *ie; size_t ie_len; ie = data->rx_mgmt.frame + IEEE80211_HDRLEN; ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN; wpas_p2p_probe_req_rx( wpa_s, src, mgmt->da, mgmt->bssid, ie, ie_len, data->rx_mgmt.freq, data->rx_mgmt.ssi_signal); break; } #endif /* CONFIG_P2P */ #ifdef CONFIG_IBSS_RSN if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_IBSS && stype == WLAN_FC_STYPE_AUTH && data->rx_mgmt.frame_len >= 30) { wpa_supplicant_event_ibss_auth(wpa_s, data); break; } #endif /* CONFIG_IBSS_RSN */ if (stype == WLAN_FC_STYPE_ACTION) { wpas_event_rx_mgmt_action( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len, data->rx_mgmt.freq, data->rx_mgmt.ssi_signal); break; } if (wpa_s->ifmsh) { mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt); break; } #ifdef CONFIG_PASN if (stype == WLAN_FC_STYPE_AUTH && wpas_pasn_auth_rx(wpa_s, mgmt, data->rx_mgmt.frame_len) != -2) break; #endif /* CONFIG_PASN */ #ifdef CONFIG_SAE if (stype == WLAN_FC_STYPE_AUTH && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { sme_external_auth_mgmt_rx( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len); break; } #endif /* CONFIG_SAE */ wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " "management frame in non-AP mode"); break; #ifdef CONFIG_AP } if (stype == WLAN_FC_STYPE_PROBE_REQ && data->rx_mgmt.frame_len > IEEE80211_HDRLEN) { const u8 *ie; size_t ie_len; ie = data->rx_mgmt.frame + IEEE80211_HDRLEN; ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN; wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da, mgmt->bssid, ie, ie_len, data->rx_mgmt.ssi_signal); } ap_mgmt_rx(wpa_s, &data->rx_mgmt); #endif /* CONFIG_AP */ break; } case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) break; #ifdef CONFIG_AP if (wpa_s->ap_iface) { hostapd_probe_req_rx(wpa_s->ap_iface->bss[0], data->rx_probe_req.sa, data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, data->rx_probe_req.ssi_signal); break; } #endif /* CONFIG_AP */ wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa, data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, 0, data->rx_probe_req.ssi_signal); break; case EVENT_REMAIN_ON_CHANNEL: #ifdef CONFIG_OFFCHANNEL offchannel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); #endif /* CONFIG_OFFCHANNEL */ wpas_p2p_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); #ifdef CONFIG_DPP wpas_dpp_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); #endif /* CONFIG_DPP */ break; case EVENT_CANCEL_REMAIN_ON_CHANNEL: #ifdef CONFIG_OFFCHANNEL offchannel_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); #endif /* CONFIG_OFFCHANNEL */ wpas_p2p_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); #ifdef CONFIG_DPP wpas_dpp_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); #endif /* CONFIG_DPP */ break; case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, data->eapol_rx.data, data->eapol_rx.data_len); break; case EVENT_SIGNAL_CHANGE: wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE "above=%d signal=%d noise=%d txrate=%d", data->signal_change.above_threshold, data->signal_change.current_signal, data->signal_change.current_noise, data->signal_change.current_txrate); wpa_bss_update_level(wpa_s->current_bss, data->signal_change.current_signal); bgscan_notify_signal_change( wpa_s, data->signal_change.above_threshold, data->signal_change.current_signal, data->signal_change.current_noise, data->signal_change.current_txrate); break; case EVENT_INTERFACE_MAC_CHANGED: wpa_supplicant_update_mac_addr(wpa_s); break; case EVENT_INTERFACE_ENABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL); wpa_supplicant_update_mac_addr(wpa_s); wpa_supplicant_set_default_scan_ies(wpa_s); if (wpa_s->p2p_mgmt) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); break; } #ifdef CONFIG_AP if (!wpa_s->ap_iface) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } else wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); #else /* CONFIG_AP */ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); wpa_supplicant_req_scan(wpa_s, 0, 0); #endif /* CONFIG_AP */ } break; case EVENT_INTERFACE_DISABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group && wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) { /* * Mark interface disabled if this happens to end up not * being removed as a separate P2P group interface. */ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); /* * The interface was externally disabled. Remove * it assuming an external entity will start a * new session if needed. */ if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) wpas_p2p_interface_unavailable(wpa_s); else wpas_p2p_disconnect(wpa_s); /* * wpa_s instance may have been freed, so must not use * it here anymore. */ break; } if (wpa_s->p2p_scan_work && wpa_s->global->p2p && p2p_in_progress(wpa_s->global->p2p) > 1) { /* This radio work will be cancelled, so clear P2P * state as well. */ p2p_stop_find(wpa_s->global->p2p); } #endif /* CONFIG_P2P */ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* * Indicate disconnection to keep ctrl_iface events * consistent. */ wpa_supplicant_event_disassoc( wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1); } wpa_supplicant_mark_disassoc(wpa_s); os_reltime_age(&wpa_s->last_scan, &age); if (age.sec >= wpa_s->conf->scan_res_valid_for_connect) { clear_at.sec = wpa_s->conf->scan_res_valid_for_connect; clear_at.usec = 0; } else { struct os_reltime tmp; tmp.sec = wpa_s->conf->scan_res_valid_for_connect; tmp.usec = 0; os_reltime_sub(&tmp, &age, &clear_at); } eloop_register_timeout(clear_at.sec, clear_at.usec, wpas_clear_disabled_interface, wpa_s, NULL); radio_remove_works(wpa_s, NULL, 0); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); break; case EVENT_CHANNEL_LIST_CHANGED: wpa_supplicant_update_channel_list( wpa_s, &data->channel_list_changed); break; case EVENT_INTERFACE_UNAVAILABLE: wpas_p2p_interface_unavailable(wpa_s); break; case EVENT_BEST_CHANNEL: wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received " "(%d %d %d)", data->best_chan.freq_24, data->best_chan.freq_5, data->best_chan.freq_overall); wpa_s->best_24_freq = data->best_chan.freq_24; wpa_s->best_5_freq = data->best_chan.freq_5; wpa_s->best_overall_freq = data->best_chan.freq_overall; wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24, data->best_chan.freq_5, data->best_chan.freq_overall); break; case EVENT_UNPROT_DEAUTH: wpa_supplicant_event_unprot_deauth(wpa_s, &data->unprot_deauth); break; case EVENT_UNPROT_DISASSOC: wpa_supplicant_event_unprot_disassoc(wpa_s, &data->unprot_disassoc); break; case EVENT_STATION_LOW_ACK: #ifdef CONFIG_AP if (wpa_s->ap_iface && data) hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0], data->low_ack.addr); #endif /* CONFIG_AP */ #ifdef CONFIG_TDLS if (data) wpa_tdls_disable_unreachable_link(wpa_s->wpa, data->low_ack.addr); #endif /* CONFIG_TDLS */ break; case EVENT_IBSS_PEER_LOST: #ifdef CONFIG_IBSS_RSN ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer); #endif /* CONFIG_IBSS_RSN */ break; case EVENT_DRIVER_GTK_REKEY: if (os_memcmp(data->driver_gtk_rekey.bssid, wpa_s->bssid, ETH_ALEN)) break; if (!wpa_s->wpa) break; wpa_sm_update_replay_ctr(wpa_s->wpa, data->driver_gtk_rekey.replay_ctr); break; case EVENT_SCHED_SCAN_STOPPED: wpa_s->sched_scanning = 0; resched = wpa_s->scanning && wpas_scan_scheduled(wpa_s); wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) break; /* * If the driver stopped scanning without being requested to, * request a new scan to continue scanning for networks. */ if (!wpa_s->sched_scan_stop_req && wpa_s->wpa_state == WPA_SCANNING) { wpa_dbg(wpa_s, MSG_DEBUG, "Restart scanning after unexpected sched_scan stop event"); wpa_supplicant_req_scan(wpa_s, 1, 0); break; } wpa_s->sched_scan_stop_req = 0; /* * Start a new sched scan to continue searching for more SSIDs * either if timed out or PNO schedule scan is pending. */ if (wpa_s->sched_scan_timed_out) { wpa_supplicant_req_sched_scan(wpa_s); } else if (wpa_s->pno_sched_pending) { wpa_s->pno_sched_pending = 0; wpas_start_pno(wpa_s); } else if (resched) { wpa_supplicant_req_scan(wpa_s, 0, 0); } break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS wpas_wps_start_pbc(wpa_s, NULL, 0, 0); #endif /* CONFIG_WPS */ break; case EVENT_AVOID_FREQUENCIES: wpa_supplicant_notify_avoid_freq(wpa_s, data); break; case EVENT_CONNECT_FAILED_REASON: #ifdef CONFIG_AP if (!wpa_s->ap_iface || !data) break; hostapd_event_connect_failed_reason( wpa_s->ap_iface->bss[0], data->connect_failed_reason.addr, data->connect_failed_reason.code); #endif /* CONFIG_AP */ break; case EVENT_NEW_PEER_CANDIDATE: #ifdef CONFIG_MESH if (!wpa_s->ifmsh || !data) break; wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer, data->mesh_peer.ies, data->mesh_peer.ie_len); #endif /* CONFIG_MESH */ break; case EVENT_SURVEY: #ifdef CONFIG_AP if (!wpa_s->ap_iface) break; hostapd_event_get_survey(wpa_s->ap_iface, &data->survey_results); #endif /* CONFIG_AP */ break; case EVENT_ACS_CHANNEL_SELECTED: #ifdef CONFIG_AP #ifdef CONFIG_ACS if (!wpa_s->ap_iface) break; hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0], &data->acs_selected_channels); #endif /* CONFIG_ACS */ #endif /* CONFIG_AP */ break; case EVENT_P2P_LO_STOP: #ifdef CONFIG_P2P wpa_s->p2p_lo_started = 0; wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d", data->p2p_lo_stop.reason_code); #endif /* CONFIG_P2P */ break; case EVENT_BEACON_LOSS: if (!wpa_s->current_bss || !wpa_s->current_ssid) break; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS); bgscan_notify_beacon_loss(wpa_s); break; case EVENT_EXTERNAL_AUTH: #ifdef CONFIG_SAE if (!wpa_s->current_ssid) { wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL"); break; } sme_external_auth_trigger(wpa_s, data); #endif /* CONFIG_SAE */ break; case EVENT_PORT_AUTHORIZED: wpa_supplicant_event_port_authorized(wpa_s); break; case EVENT_STATION_OPMODE_CHANGED: #ifdef CONFIG_AP if (!wpa_s->ap_iface || !data) break; hostapd_event_sta_opmode_changed(wpa_s->ap_iface->bss[0], data->sta_opmode.addr, data->sta_opmode.smps_mode, data->sta_opmode.chan_width, data->sta_opmode.rx_nss); #endif /* CONFIG_AP */ break; case EVENT_UNPROT_BEACON: wpas_event_unprot_beacon(wpa_s, &data->unprot_beacon); break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; } } void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s; if (event != EVENT_INTERFACE_STATUS) return; wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname); if (wpa_s && wpa_s->driver->get_ifindex) { unsigned int ifindex; ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv); if (ifindex != data->interface_status.ifindex) { wpa_dbg(wpa_s, MSG_DEBUG, "interface status ifindex %d mismatch (%d)", ifindex, data->interface_status.ifindex); return; } } #ifdef CONFIG_MATCH_IFACE else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) { struct wpa_interface *wpa_i; wpa_i = wpa_supplicant_match_iface( ctx, data->interface_status.ifname); if (!wpa_i) return; wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL); os_free(wpa_i); } #endif /* CONFIG_MATCH_IFACE */ if (wpa_s) wpa_supplicant_event(wpa_s, event, data); } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index e3d3c1d71029..62c9a26a3490 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -1,9883 +1,10043 @@ /* * wpa_supplicant - P2P * Copyright (c) 2009-2010, Atheros Communications * Copyright (c) 2010-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "common/ieee802_11_common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "wps/wps_i.h" #include "p2p/p2p.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/sta_info.h" #include "ap/ap_drv_ops.h" #include "ap/wps_hostapd.h" #include "ap/p2p_hostapd.h" #include "ap/dfs.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "ap.h" #include "config_ssid.h" #include "config.h" #include "notify.h" #include "scan.h" #include "bss.h" #include "offchannel.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" #include "wifi_display.h" /* * How many times to try to scan to find the GO before giving up on join * request. */ #define P2P_MAX_JOIN_SCAN_ATTEMPTS 10 #define P2P_AUTO_PD_SCAN_ATTEMPTS 5 /** * Defines time interval in seconds when a GO needs to evacuate a frequency that * it is currently using, but is no longer valid for P2P use cases. */ #define P2P_GO_FREQ_CHANGE_TIME 5 /** * Defines CSA parameters which are used when GO evacuates the no longer valid * channel (and if the driver supports channel switch). */ #define P2P_GO_CSA_COUNT 7 #define P2P_GO_CSA_BLOCK_TX 0 #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client * role has been lost. */ #define P2P_MAX_CLIENT_IDLE 10 #endif /* P2P_MAX_CLIENT_IDLE */ #ifndef P2P_MAX_INITIAL_CONN_WAIT /* * How many seconds to wait for initial 4-way handshake to get completed after * WPS provisioning step or after the re-invocation of a persistent group on a * P2P Client. */ #define P2P_MAX_INITIAL_CONN_WAIT 10 #endif /* P2P_MAX_INITIAL_CONN_WAIT */ #ifndef P2P_MAX_INITIAL_CONN_WAIT_GO /* * How many seconds to wait for initial 4-way handshake to get completed after * WPS provisioning step on the GO. This controls the extra time the P2P * operation is considered to be in progress (e.g., to delay other scans) after * WPS provisioning has been completed on the GO during group formation. */ #define P2P_MAX_INITIAL_CONN_WAIT_GO 10 #endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */ #ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE /* * How many seconds to wait for initial 4-way handshake to get completed after * re-invocation of a persistent group on the GO when the client is expected * to connect automatically (no user interaction). */ #define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15 #endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */ #define P2P_MGMT_DEVICE_PREFIX "p2p-dev-" /* * How many seconds to wait to re-attempt to move GOs, in case previous attempt * was not possible. */ #define P2P_RECONSIDER_GO_MOVE_DELAY 30 enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNKNOWN, P2P_GROUP_REMOVAL_SILENT, P2P_GROUP_REMOVAL_FORMATION_FAILED, P2P_GROUP_REMOVAL_REQUESTED, P2P_GROUP_REMOVAL_IDLE_TIMEOUT, P2P_GROUP_REMOVAL_UNAVAILABLE, P2P_GROUP_REMOVAL_GO_ENDING_SESSION, P2P_GROUP_REMOVAL_PSK_FAILURE, P2P_GROUP_REMOVAL_FREQ_CONFLICT, P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL }; static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx); static struct wpa_supplicant * wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, int go); static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, int *force_freq, int *pref_freq, int go, unsigned int *pref_freq_list, unsigned int *num_pref_freq); static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, const u8 *dev_addr, enum p2p_wps_method wps_method, int auto_join, int freq, const u8 *ssid, size_t ssid_len); static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s); static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s); static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s); static void wpas_p2p_group_formation_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx); static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, int group_added); static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); static void wpas_stop_listen(void *ctx); static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type); static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, int already_deleted); static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num); static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx); static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq); static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num, enum wpas_p2p_channel_update_trig trig); static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx); /* * Get the number of concurrent channels that the HW can operate, but that are * currently not in use by any of the wpa_supplicant interfaces. */ static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s) { int *freqs; int num, unused; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); if (!freqs) return -1; num = get_shared_radio_freqs(wpa_s, freqs, wpa_s->num_multichan_concurrent); os_free(freqs); unused = wpa_s->num_multichan_concurrent - num; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused); return unused; } /* * Get the frequencies that are currently in use by one or more of the virtual * interfaces, and that are also valid for P2P operation. */ static unsigned int wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *p2p_freqs, unsigned int len) { struct wpa_used_freq_data *freqs; unsigned int num, i, j; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (!freqs) return 0; num = get_shared_radio_freqs_data(wpa_s, freqs, wpa_s->num_multichan_concurrent); os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len); for (i = 0, j = 0; i < num && j < len; i++) { if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq)) p2p_freqs[j++] = freqs[i]; } os_free(freqs); dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j); return j; } static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s, int freq) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; /* Use the wpa_s used to control the P2P Device operation */ wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpa_s->conf->p2p_ignore_shared_freq && freq > 0 && wpa_s->num_multichan_concurrent > 1 && wpas_p2p_num_unused_channels(wpa_s) > 0) { wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration", freq); freq = 0; } p2p_set_own_freq_preference(wpa_s->global->p2p, freq); } static void wpas_p2p_scan_res_handled(struct wpa_supplicant *wpa_s) { unsigned int delay = wpas_p2p_search_delay(wpa_s); /* In case of concurrent P2P and external scans, delay P2P search. */ if (external_scan_running(wpa_s->radio)) { delay = wpa_s->conf->p2p_search_delay; wpa_printf(MSG_DEBUG, "P2P: Delay next P2P search by %d ms to let externally triggered scan complete", delay); } p2p_scan_res_handled(wpa_s->global->p2p, delay); } static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { size_t i; if (wpa_s->p2p_scan_work) { struct wpa_radio_work *work = wpa_s->p2p_scan_work; wpa_s->p2p_scan_work = NULL; radio_work_done(work); } if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)", (int) scan_res->num); for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; struct os_reltime time_tmp_age, entry_ts; const u8 *ies; size_t ies_len; time_tmp_age.sec = bss->age / 1000; time_tmp_age.usec = (bss->age % 1000) * 1000; os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts); ies = (const u8 *) (bss + 1); ies_len = bss->ie_len; if (bss->beacon_ie_len > 0 && !wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for " MACSTR, MAC2STR(bss->bssid)); ies = ies + ies_len; ies_len = bss->beacon_ie_len; } if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid, bss->freq, &entry_ts, bss->level, ies, ies_len) > 0) break; } wpas_p2p_scan_res_handled(wpa_s); } static void wpas_p2p_scan_res_fail_handler(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_scan_work) { struct wpa_radio_work *work = wpa_s->p2p_scan_work; wpa_s->p2p_scan_work = NULL; radio_work_done(work); } if (wpa_s->global->p2p_disabled || !wpa_s->global->p2p) return; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to get scan results - try to continue"); wpas_p2p_scan_res_handled(wpa_s); } static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; struct wpa_driver_scan_params *params = work->ctx; int ret; if (deinit) { if (!work->started) { wpa_scan_free_params(params); return; } wpa_s->p2p_scan_work = NULL; return; } if (wpa_s->clear_driver_scan_cache) { wpa_printf(MSG_DEBUG, "Request driver to clear scan cache due to local BSS flush"); params->only_new_results = 1; } - if (wpa_s->conf->p2p_6ghz_disable && !params->freqs) { + if (!params->p2p_include_6ghz && !params->freqs) { wpa_printf(MSG_DEBUG, - "P2P: 6 GHz disabled - update the scan frequency list"); + "P2P: Exclude 6 GHz channels - update the scan frequency list"); wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params, 0); wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params, 0); } ret = wpa_drv_scan(wpa_s, params); if (ret == 0) wpa_s->curr_scan_cookie = params->scan_cookie; wpa_scan_free_params(params); work->ctx = NULL; if (ret) { radio_work_done(work); p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret); return; } p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret); os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; wpa_s->scan_res_fail_handler = wpas_p2p_scan_res_fail_handler; wpa_s->own_scan_requested = 1; wpa_s->clear_driver_scan_cache = 0; wpa_s->p2p_scan_work = work; } static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s, int freq) { if (wpa_s->global->p2p_24ghz_social_channels && (freq == 2412 || freq == 2437 || freq == 2462)) { /* * Search all social channels regardless of whether these have * been disabled for P2P operating channel use to avoid missing * peers. */ return 1; } return p2p_supported_freq(wpa_s->global->p2p, freq); } static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id, u16 pw_id) + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id, + bool include_6ghz) { struct wpa_supplicant *wpa_s = ctx; struct wpa_driver_scan_params *params = NULL; struct wpabuf *wps_ie, *ies; unsigned int num_channels = 0; int social_channels_freq[] = { 2412, 2437, 2462, 60480 }; size_t ielen; u8 *n, i; unsigned int bands; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; if (wpa_s->p2p_scan_work) { wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending"); return -1; } params = os_zalloc(sizeof(*params)); if (params == NULL) return -1; /* P2P Wildcard SSID */ params->num_ssids = 1; n = os_malloc(P2P_WILDCARD_SSID_LEN); if (n == NULL) goto fail; os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); params->ssids[0].ssid = n; params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; wpa_s->wps->dev.p2p = 1; wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, wpa_s->wps->uuid, WPS_REQ_ENROLLEE, num_req_dev_types, req_dev_types); if (wps_ie == NULL) goto fail; - + if (!wpa_s->conf->p2p_6ghz_disable) + params->p2p_include_6ghz = include_6ghz; switch (type) { case P2P_SCAN_SOCIAL: params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1, sizeof(int)); if (params->freqs == NULL) goto fail; for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) { if (wpas_p2p_search_social_channel( wpa_s, social_channels_freq[i])) params->freqs[num_channels++] = social_channels_freq[i]; } params->freqs[num_channels++] = 0; break; case P2P_SCAN_FULL: break; case P2P_SCAN_SPECIFIC: params->freqs = os_calloc(2, sizeof(int)); if (params->freqs == NULL) goto fail; params->freqs[0] = freq; params->freqs[1] = 0; break; case P2P_SCAN_SOCIAL_PLUS_ONE: params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2, sizeof(int)); if (params->freqs == NULL) goto fail; for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) { if (wpas_p2p_search_social_channel( wpa_s, social_channels_freq[i])) params->freqs[num_channels++] = social_channels_freq[i]; } if (p2p_supported_freq(wpa_s->global->p2p, freq)) params->freqs[num_channels++] = freq; params->freqs[num_channels++] = 0; break; } ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); if (ies == NULL) { wpabuf_free(wps_ie); goto fail; } wpabuf_put_buf(ies, wps_ie); wpabuf_free(wps_ie); bands = wpas_get_bands(wpa_s, params->freqs); p2p_scan_ie(wpa_s->global->p2p, ies, dev_id, bands); params->p2p_probe = 1; n = os_malloc(wpabuf_len(ies)); if (n == NULL) { wpabuf_free(ies); goto fail; } os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); params->extra_ies = n; params->extra_ies_len = wpabuf_len(ies); wpabuf_free(ies); radio_remove_works(wpa_s, "p2p-scan", 0); if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, params) < 0) goto fail; return 0; fail: wpa_scan_free_params(params); return -1; } static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface) { switch (p2p_group_interface) { case P2P_GROUP_INTERFACE_PENDING: return WPA_IF_P2P_GROUP; case P2P_GROUP_INTERFACE_GO: return WPA_IF_P2P_GO; case P2P_GROUP_INTERFACE_CLIENT: return WPA_IF_P2P_CLIENT; } return WPA_IF_P2P_GROUP; } static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len, int *go) { struct wpa_ssid *s; for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 0 || !s->p2p_group || s->ssid_len != ssid_len || os_memcmp(ssid, s->ssid, ssid_len) != 0) continue; if (s->mode == WPAS_MODE_P2P_GO && s != wpa_s->current_ssid) continue; if (go) *go = s->mode == WPAS_MODE_P2P_GO; return wpa_s; } } return NULL; } static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Complete previously requested removal of %s", wpa_s->ifname); wpas_p2p_disconnect(wpa_s); } static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s, struct wpa_supplicant *calling_wpa_s) { if (calling_wpa_s == wpa_s && wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { /* * The calling wpa_s instance is going to be removed. Do that * from an eloop callback to keep the instance available until * the caller has returned. This may be needed, e.g., to provide * control interface responses on the per-interface socket. */ if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect, wpa_s, NULL) < 0) return -1; return 0; } return wpas_p2p_disconnect(wpa_s); } /* Determine total number of clients in active groups where we are the GO */ static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s) { unsigned int count = 0; struct wpa_ssid *s; for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { for (s = wpa_s->conf->ssid; s; s = s->next) { wpa_printf(MSG_DEBUG, "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", wpa_s, s, s->disabled, s->p2p_group, s->mode); if (!s->disabled && s->p2p_group && s->mode == WPAS_MODE_P2P_GO) { count += p2p_get_group_num_members( wpa_s->p2p_group); } } } return count; } static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s) { return !wpa_s->p2p_mgmt && wpa_s->current_ssid && !wpa_s->current_ssid->disabled && wpa_s->current_ssid->p2p_group && wpa_s->current_ssid->p2p_persistent_group; } static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s) { return p2p_is_active_persistent_group(wpa_s) && wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO; } /* Find an interface for a P2P group where we are the GO */ static struct wpa_supplicant * wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) { struct wpa_supplicant *save = NULL; if (!wpa_s) return NULL; for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (!p2p_is_active_persistent_go(wpa_s)) continue; /* Prefer a group with connected clients */ if (p2p_get_group_num_members(wpa_s->p2p_group)) return wpa_s; save = wpa_s; } /* No group with connected clients, so pick the one without (if any) */ return save; } static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s) { return p2p_is_active_persistent_group(wpa_s) && wpa_s->current_ssid->mode == WPAS_MODE_INFRA; } /* Find an interface for a P2P group where we are the P2P Client */ static struct wpa_supplicant * wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s) { for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (p2p_is_active_persistent_cli(wpa_s)) return wpa_s; } return NULL; } /* Find a persistent group where we are the GO */ static struct wpa_ssid * wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s) { struct wpa_ssid *s; for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO) return s; } return NULL; } static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role, unsigned int *force_freq, unsigned int *pref_freq) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; u8 conncap = P2PS_SETUP_NONE; unsigned int owned_members = 0; struct wpa_supplicant *go_wpa_s, *cli_wpa_s; struct wpa_ssid *persistent_go; int p2p_no_group_iface; unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role); if (force_freq) *force_freq = 0; if (pref_freq) *pref_freq = 0; size = P2P_MAX_PREF_CHANNELS; if (force_freq && pref_freq && !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq, (int *) pref_freq, 0, pref_freq_list, &size)) wpas_p2p_set_own_freq_preference(wpa_s, *force_freq ? *force_freq : *pref_freq); /* * For non-concurrent capable devices: * If persistent_go, then no new. * If GO, then no client. * If client, then no GO. */ go_wpa_s = wpas_p2p_get_go_group(wpa_s); if (go_wpa_s) owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group); persistent_go = wpas_p2p_get_persistent_go(wpa_s); p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s); cli_wpa_s = wpas_p2p_get_cli_group(wpa_s); wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p", go_wpa_s, owned_members, cli_wpa_s, persistent_go); /* If not concurrent, restrict our choices */ if (p2p_no_group_iface) { wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface"); if (cli_wpa_s) return P2PS_SETUP_NONE; if (go_wpa_s) { if (role == P2PS_SETUP_CLIENT || incoming == P2PS_SETUP_GROUP_OWNER || p2p_client_limit_reached(go_wpa_s->p2p_group)) return P2PS_SETUP_NONE; return P2PS_SETUP_GROUP_OWNER; } if (persistent_go) { if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) { if (!incoming) return P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT; if (incoming == P2PS_SETUP_NEW) { u8 r; if (os_get_random(&r, sizeof(r)) < 0 || (r & 1)) return P2PS_SETUP_CLIENT; return P2PS_SETUP_GROUP_OWNER; } } } } /* If a required role has been specified, handle it here */ if (role && role != P2PS_SETUP_NEW) { switch (incoming) { case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: /* * Peer has an active GO, so if the role allows it and * we do not have any active roles, become client. */ if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && !cli_wpa_s) return P2PS_SETUP_CLIENT; /* fall through */ case P2PS_SETUP_NONE: case P2PS_SETUP_NEW: conncap = role; goto grp_owner; case P2PS_SETUP_GROUP_OWNER: /* * Must be a complimentary role - cannot be a client to * more than one peer. */ if (incoming == role || cli_wpa_s) return P2PS_SETUP_NONE; return P2PS_SETUP_CLIENT; case P2PS_SETUP_CLIENT: /* Must be a complimentary role */ if (incoming != role) { conncap = P2PS_SETUP_GROUP_OWNER; goto grp_owner; } /* fall through */ default: return P2PS_SETUP_NONE; } } /* * For now, we only will support ownership of one group, and being a * client of one group. Therefore, if we have either an existing GO * group, or an existing client group, we will not do a new GO * negotiation, but rather try to re-use the existing groups. */ switch (incoming) { case P2PS_SETUP_NONE: case P2PS_SETUP_NEW: if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else if (!owned_members) conncap = P2PS_SETUP_NEW; else if (incoming == P2PS_SETUP_NONE) conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT; else conncap = P2PS_SETUP_CLIENT; break; case P2PS_SETUP_CLIENT: conncap = P2PS_SETUP_GROUP_OWNER; break; case P2PS_SETUP_GROUP_OWNER: if (!cli_wpa_s) conncap = P2PS_SETUP_CLIENT; break; case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else { u8 r; if (os_get_random(&r, sizeof(r)) < 0 || (r & 1)) conncap = P2PS_SETUP_CLIENT; else conncap = P2PS_SETUP_GROUP_OWNER; } break; default: return P2PS_SETUP_NONE; } grp_owner: if ((conncap & P2PS_SETUP_GROUP_OWNER) || (!incoming && (conncap & P2PS_SETUP_NEW))) { if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group)) conncap &= ~P2PS_SETUP_GROUP_OWNER; s = wpas_p2p_get_persistent_go(wpa_s); if (!s && !go_wpa_s && p2p_no_group_iface) { p2p_set_intended_addr(wpa_s->global->p2p, wpa_s->p2p_mgmt ? wpa_s->parent->own_addr : wpa_s->own_addr); } else if (!s && !go_wpa_s) { if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GROUP) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new interface for the group"); return P2PS_SETUP_NONE; } wpa_s->global->pending_group_iface_for_p2ps = 1; p2p_set_intended_addr(wpa_s->global->p2p, wpa_s->pending_interface_addr); } } return conncap; } static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, enum p2p_group_removal_reason removal_reason) { struct wpa_ssid *ssid; char *gtype; const char *reason; ssid = wpa_s->current_ssid; if (ssid == NULL) { /* * The current SSID was not known, but there may still be a * pending P2P group interface waiting for provisioning or a * P2P group that is trying to reconnect. */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->p2p_group && ssid->disabled != 2) break; ssid = ssid->next; } if (ssid == NULL && wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE) { wpa_printf(MSG_ERROR, "P2P: P2P group interface " "not found"); return -1; } } if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO) gtype = "GO"; else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || (ssid && ssid->mode == WPAS_MODE_INFRA)) { wpa_s->reassociate = 0; wpa_s->disconnected = 1; gtype = "client"; } else gtype = "GO"; if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); if (os_strcmp(gtype, "client") == 0) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal, wpa_s, NULL)) { wpa_printf(MSG_DEBUG, "P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal"); removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE; eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL); } } if (wpa_s->cross_connect_in_use) { wpa_s->cross_connect_in_use = 0; wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); } switch (removal_reason) { case P2P_GROUP_REMOVAL_REQUESTED: reason = " reason=REQUESTED"; break; case P2P_GROUP_REMOVAL_FORMATION_FAILED: reason = " reason=FORMATION_FAILED"; break; case P2P_GROUP_REMOVAL_IDLE_TIMEOUT: reason = " reason=IDLE"; break; case P2P_GROUP_REMOVAL_UNAVAILABLE: reason = " reason=UNAVAILABLE"; break; case P2P_GROUP_REMOVAL_GO_ENDING_SESSION: reason = " reason=GO_ENDING_SESSION"; break; case P2P_GROUP_REMOVAL_PSK_FAILURE: reason = " reason=PSK_FAILURE"; break; case P2P_GROUP_REMOVAL_FREQ_CONFLICT: reason = " reason=FREQ_CONFLICT"; break; default: reason = ""; break; } if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", wpa_s->ifname, gtype, reason); } if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout"); if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL) > 0) { wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " "timeout"); wpa_s->p2p_in_provisioning = 0; wpas_p2p_group_formation_failed(wpa_s, 1); } wpa_s->p2p_in_invitation = 0; eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL); /* * Make sure wait for the first client does not remain active after the * group has been removed. */ wpa_s->global->p2p_go_wait_client.sec = 0; if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { struct wpa_global *global; char *ifname; enum wpa_driver_if_type type; wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s", wpa_s->ifname); global = wpa_s->global; ifname = os_strdup(wpa_s->ifname); type = wpas_p2p_if_type(wpa_s->p2p_group_interface); eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL); wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); wpa_s = global->ifaces; if (wpa_s && ifname) wpa_drv_if_remove(wpa_s, type, ifname); os_free(ifname); return 1; } /* * The primary interface was used for P2P group operations, so * need to reset its p2pdev. */ wpa_s->p2pdev = wpa_s->parent; if (!wpa_s->p2p_go_group_formation_completed) { wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; } wpa_s->show_group_started = 0; os_free(wpa_s->go_params); wpa_s->go_params = NULL; os_free(wpa_s->p2p_group_common_freqs); wpa_s->p2p_group_common_freqs = NULL; wpa_s->p2p_group_common_freqs_num = 0; wpa_s->p2p_go_do_acs = 0; wpa_s->waiting_presence_resp = 0; wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); if (ssid && (ssid->p2p_group || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) { int id = ssid->id; if (ssid == wpa_s->current_ssid) { wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->current_ssid = NULL; } /* * Networks objects created during any P2P activities are not * exposed out as they might/will confuse certain non-P2P aware * applications since these network objects won't behave like * regular ones. * * Likewise, we don't send out network removed signals for such * network objects. */ wpa_config_remove_network(wpa_s->conf, id); wpa_supplicant_clear_status(wpa_s); wpa_supplicant_cancel_sched_scan(wpa_s); } else { wpa_printf(MSG_DEBUG, "P2P: Temporary group network not " "found"); } if (wpa_s->ap_iface) wpa_supplicant_ap_deinit(wpa_s); else wpa_drv_deinit_p2p_cli(wpa_s); os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); return 0; } static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, u8 *go_dev_addr, const u8 *ssid, size_t ssid_len) { struct wpa_bss *bss; const u8 *bssid; struct wpabuf *p2p; u8 group_capab; const u8 *addr; if (wpa_s->go_params) bssid = wpa_s->go_params->peer_interface_addr; else bssid = wpa_s->bssid; bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len); if (bss == NULL && wpa_s->go_params && !is_zero_ether_addr(wpa_s->go_params->peer_device_addr)) bss = wpa_bss_get_p2p_dev_addr( wpa_s, wpa_s->go_params->peer_device_addr); if (bss == NULL) { u8 iface_addr[ETH_ALEN]; if (p2p_get_interface_addr(wpa_s->global->p2p, bssid, iface_addr) == 0) bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len); } if (bss == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " "group is persistent - BSS " MACSTR " not found", MAC2STR(bssid)); return 0; } p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); if (p2p == NULL) p2p = wpa_bss_get_vendor_ie_multi_beacon(bss, P2P_IE_VENDOR_TYPE); if (p2p == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " "group is persistent - BSS " MACSTR " did not include P2P IE", MAC2STR(bssid)); wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs", wpa_bss_ie_ptr(bss), bss->ie_len); wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs", wpa_bss_ie_ptr(bss) + bss->ie_len, bss->beacon_ie_len); return 0; } group_capab = p2p_get_group_capab(p2p); addr = p2p_get_go_dev_addr(p2p); wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: " "group_capab=0x%x", group_capab); if (addr) { os_memcpy(go_dev_addr, addr, ETH_ALEN); wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR, MAC2STR(addr)); } else os_memset(go_dev_addr, 0, ETH_ALEN); wpabuf_free(p2p); wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x " "go_dev_addr=" MACSTR, MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr)); return !!(group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP); } static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *go_dev_addr) { struct wpa_ssid *s; int changed = 0; wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent " "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr)); for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled == 2 && os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 && s->ssid_len == ssid->ssid_len && os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0) break; } if (s) { wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group " "entry"); if (ssid->passphrase && !s->passphrase) changed = 1; else if (ssid->passphrase && s->passphrase && os_strcmp(ssid->passphrase, s->passphrase) != 0) changed = 1; } else { wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group " "entry"); changed = 1; s = wpa_config_add_network(wpa_s->conf); if (s == NULL) return -1; /* * Instead of network_added we emit persistent_group_added * notification. Also to keep the defense checks in * persistent_group obj registration method, we set the * relevant flags in s to designate it as a persistent group. */ s->p2p_group = 1; s->p2p_persistent_group = 1; wpas_notify_persistent_group_added(wpa_s, s); wpa_config_set_network_defaults(s); } s->p2p_group = 1; s->p2p_persistent_group = 1; s->disabled = 2; s->bssid_set = 1; os_memcpy(s->bssid, go_dev_addr, ETH_ALEN); s->mode = ssid->mode; s->auth_alg = WPA_AUTH_ALG_OPEN; s->key_mgmt = WPA_KEY_MGMT_PSK; s->proto = WPA_PROTO_RSN; s->pbss = ssid->pbss; s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; s->export_keys = 1; if (ssid->passphrase) { os_free(s->passphrase); s->passphrase = os_strdup(ssid->passphrase); } if (ssid->psk_set) { s->psk_set = 1; os_memcpy(s->psk, ssid->psk, 32); } if (s->passphrase && !s->psk_set) wpa_config_update_psk(s); if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) { os_free(s->ssid); s->ssid = os_malloc(ssid->ssid_len); } if (s->ssid) { s->ssid_len = ssid->ssid_len; os_memcpy(s->ssid, ssid->ssid, s->ssid_len); } if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) { dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list); wpa_s->global->add_psk = NULL; changed = 1; } if (changed && wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } return s->id; } static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, const u8 *addr) { struct wpa_ssid *ssid, *s; u8 *n; size_t i; int found = 0; struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) return; for (s = p2p_wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) continue; if (s->ssid_len == ssid->ssid_len && os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0) break; } if (s == NULL) return; for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) { if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr, ETH_ALEN) != 0) continue; if (i == s->num_p2p_clients - 1) return; /* already the most recent entry */ /* move the entry to mark it most recent */ os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN, s->p2p_client_list + (i + 1) * 2 * ETH_ALEN, (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN); os_memcpy(s->p2p_client_list + (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr, ETH_ALEN); os_memset(s->p2p_client_list + (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN, 0xff, ETH_ALEN); found = 1; break; } if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) { n = os_realloc_array(s->p2p_client_list, s->num_p2p_clients + 1, 2 * ETH_ALEN); if (n == NULL) return; os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr, ETH_ALEN); os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN, 0xff, ETH_ALEN); s->p2p_client_list = n; s->num_p2p_clients++; } else if (!found && s->p2p_client_list) { /* Not enough room for an additional entry - drop the oldest * entry */ os_memmove(s->p2p_client_list, s->p2p_client_list + 2 * ETH_ALEN, (s->num_p2p_clients - 1) * 2 * ETH_ALEN); os_memcpy(s->p2p_client_list + (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr, ETH_ALEN); os_memset(s->p2p_client_list + (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN, 0xff, ETH_ALEN); } if (p2p_wpa_s->conf->update_config && wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s, int go, struct wpa_ssid *ssid, int freq, const u8 *psk, const char *passphrase, const u8 *go_dev_addr, int persistent, const char *extra) { const char *ssid_txt; char psk_txt[65]; if (psk) wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32); else psk_txt[0] = '\0'; if (ssid) ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len); else ssid_txt = ""; if (passphrase && passphrase[0] == '\0') passphrase = NULL; /* * Include PSK/passphrase only in the control interface message and * leave it out from the debug log entry. */ wpa_msg_global_ctrl(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_STARTED "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr=" MACSTR "%s%s", wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq, psk ? " psk=" : "", psk_txt, passphrase ? " passphrase=\"" : "", passphrase ? passphrase : "", passphrase ? "\"" : "", MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "", extra); wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED "%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s", wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq, MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "", extra); } static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, int success, int already_deleted) { struct wpa_ssid *ssid; int client; int persistent; u8 go_dev_addr[ETH_ALEN]; /* * This callback is likely called for the main interface. Update wpa_s * to use the group interface if a new interface was created for the * group. */ if (wpa_s->global->p2p_group_formation) wpa_s = wpa_s->global->p2p_group_formation; if (wpa_s->p2p_go_group_formation_completed) { wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; } else if (wpa_s->p2p_in_provisioning && !success) { wpa_msg(wpa_s, MSG_DEBUG, "P2P: Stop provisioning state due to failure"); wpa_s->p2p_in_provisioning = 0; } wpa_s->p2p_in_invitation = 0; wpa_s->group_formation_reported = 1; if (!success) { wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_notify_p2p_group_formation_failure(wpa_s, ""); if (already_deleted) return; wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FORMATION_FAILED); return; } wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); ssid = wpa_s->current_ssid; if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { ssid->mode = WPAS_MODE_P2P_GO; p2p_group_notif_formation_done(wpa_s->p2p_group); wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL); } persistent = 0; if (ssid) { client = ssid->mode == WPAS_MODE_INFRA; if (ssid->mode == WPAS_MODE_P2P_GO) { persistent = ssid->p2p_persistent_group; os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); } else persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, ssid->ssid_len); } else { client = wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT; os_memset(go_dev_addr, 0, ETH_ALEN); } wpa_s->show_group_started = 0; if (client) { /* * Indicate event only after successfully completed 4-way * handshake, i.e., when the interface is ready for data * packets. */ wpa_s->show_group_started = 1; } else { wpas_p2p_group_started(wpa_s, 1, ssid, ssid ? ssid->frequency : 0, ssid && ssid->passphrase == NULL && ssid->psk_set ? ssid->psk : NULL, ssid ? ssid->passphrase : NULL, go_dev_addr, persistent, ""); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); } if (persistent) wpas_p2p_store_persistent_group(wpa_s->p2pdev, ssid, go_dev_addr); else { os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; } if (!client) { wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0, NULL); os_get_reltime(&wpa_s->global->p2p_go_wait_client); } } struct send_action_work { unsigned int freq; u8 dst[ETH_ALEN]; u8 src[ETH_ALEN]; u8 bssid[ETH_ALEN]; size_t len; unsigned int wait_time; u8 buf[0]; }; static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s) { struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx; wpa_printf(MSG_DEBUG, "P2P: Free Action frame radio work @%p (freq=%u dst=" MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)", wpa_s->p2p_send_action_work, awork->freq, MAC2STR(awork->dst), MAC2STR(awork->src), MAC2STR(awork->bssid), awork->wait_time); wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame", awork->buf, awork->len); os_free(awork); wpa_s->p2p_send_action_work->ctx = NULL; radio_work_done(wpa_s->p2p_send_action_work); wpa_s->p2p_send_action_work = NULL; } static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (!wpa_s->p2p_send_action_work) return; wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out"); wpas_p2p_free_send_action_work(wpa_s); } static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_send_action_work) { struct send_action_work *awork; awork = wpa_s->p2p_send_action_work->ctx; wpa_printf(MSG_DEBUG, "P2P: Clear Action TX work @%p (wait_time=%u)", wpa_s->p2p_send_action_work, awork->wait_time); if (awork->wait_time == 0) { wpas_p2p_free_send_action_work(wpa_s); } else { /* * In theory, this should not be needed, but number of * places in the P2P code is still using non-zero wait * time for the last Action frame in the sequence and * some of these do not call send_action_done(). */ eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); eloop_register_timeout( 0, awork->wait_time * 1000, wpas_p2p_send_action_work_timeout, wpa_s, NULL); } } } static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result) { enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS; wpas_p2p_action_tx_clear(wpa_s); if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) return; switch (result) { case OFFCHANNEL_SEND_ACTION_SUCCESS: res = P2P_SEND_ACTION_SUCCESS; break; case OFFCHANNEL_SEND_ACTION_NO_ACK: res = P2P_SEND_ACTION_NO_ACK; break; case OFFCHANNEL_SEND_ACTION_FAILED: res = P2P_SEND_ACTION_FAILED; break; } p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res); if (result != OFFCHANNEL_SEND_ACTION_SUCCESS && wpa_s->pending_pd_before_join && (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) && wpa_s->p2p_fallback_to_go_neg) { wpa_s->pending_pd_before_join = 0; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req " "during p2p_connect-auto"); wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=no-ACK-to-PD-Req"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); return; } } static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; struct send_action_work *awork = work->ctx; if (deinit) { if (work->started) { eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); wpa_s->p2p_send_action_work = NULL; offchannel_send_action_done(wpa_s); } os_free(awork); return; } if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src, awork->bssid, awork->buf, awork->len, awork->wait_time, wpas_p2p_send_action_tx_status, 1) < 0) { os_free(awork); radio_work_done(work); return; } wpa_s->p2p_send_action_work = work; } static int wpas_send_action_work(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time) { struct send_action_work *awork; if (radio_work_pending(wpa_s, "p2p-send-action")) { wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending"); return -1; } awork = os_zalloc(sizeof(*awork) + len); if (awork == NULL) return -1; awork->freq = freq; os_memcpy(awork->dst, dst, ETH_ALEN); os_memcpy(awork->src, src, ETH_ALEN); os_memcpy(awork->bssid, bssid, ETH_ALEN); awork->len = len; awork->wait_time = wait_time; os_memcpy(awork->buf, buf, len); if (radio_add_work(wpa_s, freq, "p2p-send-action", 1, wpas_send_action_cb, awork) < 0) { os_free(awork); return -1; } return 0; } static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time, int *scheduled) { struct wpa_supplicant *wpa_s = ctx; int listen_freq = -1, send_freq = -1; if (scheduled) *scheduled = 0; if (wpa_s->p2p_listen_work) listen_freq = wpa_s->p2p_listen_work->freq; if (wpa_s->p2p_send_action_work) send_freq = wpa_s->p2p_send_action_work->freq; if (listen_freq != (int) freq && send_freq != (int) freq) { int res; wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)", listen_freq, send_freq, freq); res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, len, wait_time); if (res == 0 && scheduled) *scheduled = 1; return res; } wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX"); return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len, wait_time, wpas_p2p_send_action_tx_status, 1); } static void wpas_send_action_done(void *ctx) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->p2p_send_action_work) { eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); os_free(wpa_s->p2p_send_action_work->ctx); radio_work_done(wpa_s->p2p_send_action_work); wpa_s->p2p_send_action_work = NULL; } offchannel_send_action_done(wpa_s); } static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params) { if (wpa_s->go_params == NULL) { wpa_s->go_params = os_malloc(sizeof(*params)); if (wpa_s->go_params == NULL) return -1; } os_memcpy(wpa_s->go_params, params, sizeof(*params)); return 0; } static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res) { wpa_s->group_formation_reported = 0; wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR " dev_addr " MACSTR " wps_method %d", MAC2STR(res->peer_interface_addr), MAC2STR(res->peer_device_addr), res->wps_method); wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID", res->ssid, res->ssid_len); wpa_supplicant_ap_deinit(wpa_s); wpas_copy_go_neg_results(wpa_s, res); if (res->wps_method == WPS_PBC) { wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1, 0); #ifdef CONFIG_WPS_NFC } else if (res->wps_method == WPS_NFC) { wpas_wps_start_nfc(wpa_s, res->peer_device_addr, res->peer_interface_addr, wpa_s->p2pdev->p2p_oob_dev_pw, wpa_s->p2pdev->p2p_oob_dev_pw_id, 1, wpa_s->p2pdev->p2p_oob_dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER ? wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL, NULL, 0, 0); #endif /* CONFIG_WPS_NFC */ } else { u16 dev_pw_id = DEV_PW_DEFAULT; if (wpa_s->p2p_wps_method == WPS_P2PS) dev_pw_id = DEV_PW_P2PS_DEFAULT; if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD) dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED; wpas_wps_start_pin(wpa_s, res->peer_interface_addr, wpa_s->p2p_pin, 1, dev_pw_id); } } static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct wpa_ssid *persistent; struct psk_list_entry *psk; struct hostapd_data *hapd; if (!wpa_s->ap_iface) return; persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid, ssid->ssid_len); if (persistent == NULL) return; hapd = wpa_s->ap_iface->bss[0]; dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry, list) { struct hostapd_wpa_psk *hpsk; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for " MACSTR " psk=%d", MAC2STR(psk->addr), psk->p2p); hpsk = os_zalloc(sizeof(*hpsk)); if (hpsk == NULL) break; os_memcpy(hpsk->psk, psk->psk, PMK_LEN); if (psk->p2p) os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN); else os_memcpy(hpsk->addr, psk->addr, ETH_ALEN); hpsk->next = hapd->conf->ssid.wpa_psk; hapd->conf->ssid.wpa_psk = hpsk; } } static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s) { char buf[20 + P2P_MAX_CHANNELS * 6]; char *pos, *end; unsigned int i; int res; pos = buf; end = pos + sizeof(buf); for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { res = os_snprintf(pos, end - pos, " %d", wpa_s->p2p_group_common_freqs[i]); if (os_snprintf_error(end - pos, res)) break; pos += res; } *pos = '\0'; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies:%s", buf); } static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params) { unsigned int i, len = int_array_len(wpa_s->go_params->freq_list); wpa_s->p2p_group_common_freqs_num = 0; os_free(wpa_s->p2p_group_common_freqs); wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int)); if (!wpa_s->p2p_group_common_freqs) return; for (i = 0; i < len; i++) { if (!wpa_s->go_params->freq_list[i]) break; wpa_s->p2p_group_common_freqs[i] = wpa_s->go_params->freq_list[i]; } wpa_s->p2p_group_common_freqs_num = i; } static void p2p_config_write(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->p2pdev->conf->update_config && wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); #endif /* CONFIG_NO_CONFIG_WRITE */ } static void p2p_go_configured(void *ctx, void *data) { struct wpa_supplicant *wpa_s = ctx; struct p2p_go_neg_results *params = data; struct wpa_ssid *ssid; wpa_s->ap_configured_cb = NULL; wpa_s->ap_configured_cb_ctx = NULL; wpa_s->ap_configured_cb_data = NULL; if (!wpa_s->go_params) { wpa_printf(MSG_ERROR, "P2P: p2p_go_configured() called with wpa_s->go_params == NULL"); return; } p2p_go_save_group_common_freqs(wpa_s, params); p2p_go_dump_common_freqs(wpa_s); ssid = wpa_s->current_ssid; if (ssid && ssid->mode == WPAS_MODE_P2P_GO) { wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning"); if (wpa_s->global->p2p_group_formation == wpa_s) wpa_s->global->p2p_group_formation = NULL; wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency, params->passphrase[0] == '\0' ? params->psk : NULL, params->passphrase, wpa_s->global->p2p_dev_addr, params->persistent_group, ""); wpa_s->group_formation_reported = 1; if (wpa_s->p2pdev->p2ps_method_config_any) { if (is_zero_ether_addr(wpa_s->p2pdev->p2ps_join_addr)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2PS: Setting default PIN for ANY"); wpa_supplicant_ap_wps_pin(wpa_s, NULL, "12345670", NULL, 0, 0); } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2PS: Setting default PIN for " MACSTR, MAC2STR(wpa_s->p2pdev->p2ps_join_addr)); wpa_supplicant_ap_wps_pin( wpa_s, wpa_s->p2pdev->p2ps_join_addr, "12345670", NULL, 0, 0); } wpa_s->p2pdev->p2ps_method_config_any = 0; } os_get_reltime(&wpa_s->global->p2p_go_wait_client); if (params->persistent_group) { wpas_p2p_store_persistent_group( wpa_s->p2pdev, ssid, wpa_s->global->p2p_dev_addr); wpas_p2p_add_psk_list(wpa_s, ssid); } wpas_notify_p2p_group_started(wpa_s, ssid, params->persistent_group, 0, NULL); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); if (wpa_s->p2p_first_connection_timeout) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Start group formation timeout of %d seconds until first data connection on GO", wpa_s->p2p_first_connection_timeout); wpa_s->p2p_go_group_formation_completed = 0; wpa_s->global->p2p_group_formation = wpa_s; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); eloop_register_timeout( wpa_s->p2p_first_connection_timeout, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); } return; } wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning"); if (wpa_supplicant_ap_mac_addr_filter(wpa_s, params->peer_interface_addr)) { wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address " "filtering"); return; } if (params->wps_method == WPS_PBC) { wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr, params->peer_device_addr); #ifdef CONFIG_WPS_NFC } else if (params->wps_method == WPS_NFC) { if (wpa_s->p2pdev->p2p_oob_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && !wpa_s->p2pdev->p2p_oob_dev_pw) { wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); return; } wpas_ap_wps_add_nfc_pw( wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id, wpa_s->p2pdev->p2p_oob_dev_pw, wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ? wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL); #endif /* CONFIG_WPS_NFC */ } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, wpa_s->p2p_pin, NULL, 0, 0); os_free(wpa_s->go_params); wpa_s->go_params = NULL; } /** * wpas_p2p_freq_to_edmg_channel - Convert frequency into EDMG channel * @freq: Frequency (MHz) to convert * @op_class: Buffer for returning operating class * @op_edmg_channel: Buffer for returning channel number * Returns: 0 on success, -1 on failure * * This can be used to find the highest channel bonding which includes the * specified frequency. */ static int wpas_p2p_freq_to_edmg_channel(struct wpa_supplicant *wpa_s, unsigned int freq, u8 *op_class, u8 *op_edmg_channel) { struct hostapd_hw_modes *hwmode; struct ieee80211_edmg_config edmg; unsigned int i; enum chan_width chanwidth[] = { CHAN_WIDTH_8640, CHAN_WIDTH_6480, CHAN_WIDTH_4320, }; if (!wpa_s->hw.modes) return -1; hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, HOSTAPD_MODE_IEEE80211AD, false); if (!hwmode) { wpa_printf(MSG_ERROR, "Unsupported AP mode: HOSTAPD_MODE_IEEE80211AD"); return -1; } /* Find the highest EDMG channel bandwidth to start the P2P GO */ for (i = 0; i < ARRAY_SIZE(chanwidth); i++) { if (ieee80211_chaninfo_to_channel(freq, chanwidth[i], 0, op_class, op_edmg_channel) < 0) continue; hostapd_encode_edmg_chan(1, *op_edmg_channel, 0, &edmg); if (edmg.channels && ieee802_edmg_is_allowed(hwmode->edmg, edmg)) { wpa_printf(MSG_DEBUG, "Freq %u to EDMG channel %u at opclass %u", freq, *op_edmg_channel, *op_class); return 0; } } return -1; } int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params) { u8 op_channel, op_class; int freq; /* Try social channel as primary channel frequency */ freq = (!params->freq) ? 58320 + 1 * 2160 : params->freq; if (wpas_p2p_freq_to_edmg_channel(wpa_s, freq, &op_class, &op_channel) == 0) { wpa_printf(MSG_DEBUG, "Freq %d will be used to set an EDMG connection (channel=%u opclass=%u)", freq, op_channel, op_class); params->freq = freq; return 0; } return -1; } static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, int group_formation) { struct wpa_ssid *ssid; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO"); if (wpas_copy_go_neg_results(wpa_s, params) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation " "results"); return; } ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO"); return; } wpa_s->show_group_started = 0; wpa_s->p2p_go_group_formation_completed = 0; wpa_s->group_formation_reported = 0; os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->p2p_group = 1; ssid->p2p_persistent_group = !!params->persistent_group; ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : WPAS_MODE_P2P_GO; ssid->frequency = params->freq; ssid->ht40 = params->ht40; ssid->vht = params->vht; ssid->max_oper_chwidth = params->max_oper_chwidth; ssid->vht_center_freq2 = params->vht_center_freq2; ssid->he = params->he; if (params->edmg) { u8 op_channel, op_class; if (!wpas_p2p_freq_to_edmg_channel(wpa_s, params->freq, &op_class, &op_channel)) { ssid->edmg_channel = op_channel; ssid->enable_edmg = params->edmg; } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not match EDMG channel, freq %d, for GO", params->freq); } } ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); ssid->ssid_len = params->ssid_len; } ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; + if (is_6ghz_freq(ssid->frequency) && + is_p2p_6ghz_capable(wpa_s->global->p2p)) { + ssid->auth_alg |= WPA_AUTH_ALG_SAE; + ssid->key_mgmt = WPA_KEY_MGMT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt"); + } else { + p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false); + } ssid->proto = WPA_PROTO_RSN; ssid->pairwise_cipher = WPA_CIPHER_CCMP; ssid->group_cipher = WPA_CIPHER_CCMP; if (params->freq > 56160) { /* * Enable GCMP instead of CCMP as pairwise_cipher and * group_cipher in 60 GHz. */ ssid->pairwise_cipher = WPA_CIPHER_GCMP; ssid->group_cipher = WPA_CIPHER_GCMP; /* P2P GO in 60 GHz is always a PCP (PBSS) */ ssid->pbss = 1; } if (os_strlen(params->passphrase) > 0) { ssid->passphrase = os_strdup(params->passphrase); if (ssid->passphrase == NULL) { wpa_msg_global(wpa_s, MSG_ERROR, "P2P: Failed to copy passphrase for GO"); wpa_config_remove_network(wpa_s->conf, ssid->id); return; } } else ssid->passphrase = NULL; ssid->psk_set = params->psk_set; if (ssid->psk_set) os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk)); else if (ssid->passphrase) wpa_config_update_psk(ssid); ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity; wpa_s->ap_configured_cb = p2p_go_configured; wpa_s->ap_configured_cb_ctx = wpa_s; wpa_s->ap_configured_cb_data = wpa_s->go_params; wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_s->connect_without_scan = ssid; wpa_s->reassociate = 1; wpa_s->disconnected = 0; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to " "start GO)"); wpa_supplicant_req_scan(wpa_s, 0, 0); } static void wpas_p2p_clone_config(struct wpa_supplicant *dst, const struct wpa_supplicant *src) { struct wpa_config *d; const struct wpa_config *s; d = dst->conf; s = src->conf; #define C(n) \ do { \ if (s->n && !d->n) \ d->n = os_strdup(s->n); \ } while (0) C(device_name); C(manufacturer); C(model_name); C(model_number); C(serial_number); C(config_methods); #undef C os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN); os_memcpy(d->sec_device_type, s->sec_device_type, sizeof(d->sec_device_type)); d->num_sec_device_types = s->num_sec_device_types; d->p2p_group_idle = s->p2p_group_idle; d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy; d->p2p_intra_bss = s->p2p_intra_bss; d->persistent_reconnect = s->persistent_reconnect; d->max_num_sta = s->max_num_sta; d->pbc_in_m1 = s->pbc_in_m1; d->ignore_old_scan_res = s->ignore_old_scan_res; d->beacon_int = s->beacon_int; d->dtim_period = s->dtim_period; d->p2p_go_ctwindow = s->p2p_go_ctwindow; d->disassoc_low_ack = s->disassoc_low_ack; d->disable_scan_offload = s->disable_scan_offload; d->passive_scan = s->passive_scan; if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey && !d->wps_nfc_pw_from_config) { wpabuf_free(d->wps_nfc_dh_privkey); wpabuf_free(d->wps_nfc_dh_pubkey); d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); } d->p2p_cli_probe = s->p2p_cli_probe; d->go_interworking = s->go_interworking; d->go_access_network_type = s->go_access_network_type; d->go_internet = s->go_internet; d->go_venue_group = s->go_venue_group; d->go_venue_type = s->go_venue_type; d->p2p_add_cli_chan = s->p2p_add_cli_chan; } static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s, char *ifname, size_t len) { char *ifname_ptr = wpa_s->ifname; if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX, os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1; } os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx); if (os_strlen(ifname) >= IFNAMSIZ && os_strlen(wpa_s->ifname) < IFNAMSIZ) { int res; /* Try to avoid going over the IFNAMSIZ length limit */ res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx); if (os_snprintf_error(len, res) && len) ifname[len - 1] = '\0'; } } static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type) { char ifname[120], force_ifname[120]; if (wpa_s->pending_interface_name[0]) { wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists " "- skip creation of a new one"); if (is_zero_ether_addr(wpa_s->pending_interface_addr)) { wpa_printf(MSG_DEBUG, "P2P: Pending virtual address " "unknown?! ifname='%s'", wpa_s->pending_interface_name); return -1; } return 0; } wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname)); force_ifname[0] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group", ifname); wpa_s->p2p_group_idx++; wpa_s->pending_interface_type = type; if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname, wpa_s->pending_interface_addr, NULL) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to create new group " "interface"); return -1; } if (wpa_s->conf->p2p_interface_random_mac_addr) { random_mac_addr(wpa_s->pending_interface_addr); wpa_printf(MSG_DEBUG, "P2P: Generate random MAC address " MACSTR " for the group", MAC2STR(wpa_s->pending_interface_addr)); } if (force_ifname[0]) { wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s", force_ifname); os_strlcpy(wpa_s->pending_interface_name, force_ifname, sizeof(wpa_s->pending_interface_name)); } else os_strlcpy(wpa_s->pending_interface_name, ifname, sizeof(wpa_s->pending_interface_name)); wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr " MACSTR, wpa_s->pending_interface_name, MAC2STR(wpa_s->pending_interface_addr)); return 0; } static void wpas_p2p_remove_pending_group_interface( struct wpa_supplicant *wpa_s) { if (!wpa_s->pending_interface_name[0] || is_zero_ether_addr(wpa_s->pending_interface_addr)) return; /* No pending virtual interface */ wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s", wpa_s->pending_interface_name); wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type, wpa_s->pending_interface_name); os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); wpa_s->pending_interface_name[0] = '\0'; wpa_s->global->pending_group_iface_for_p2ps = 0; } static struct wpa_supplicant * wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) { struct wpa_interface iface; struct wpa_supplicant *group_wpa_s; if (!wpa_s->pending_interface_name[0]) { wpa_printf(MSG_ERROR, "P2P: No pending group interface"); if (!wpas_p2p_create_iface(wpa_s)) return NULL; /* * Something has forced us to remove the pending interface; try * to create a new one and hope for the best that we will get * the same local address. */ if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : WPA_IF_P2P_CLIENT) < 0) return NULL; } os_memset(&iface, 0, sizeof(iface)); iface.ifname = wpa_s->pending_interface_name; iface.driver = wpa_s->driver->name; if (wpa_s->conf->ctrl_interface == NULL && wpa_s->parent != wpa_s && wpa_s->p2p_mgmt && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface; else iface.ctrl_interface = wpa_s->conf->ctrl_interface; iface.driver_param = wpa_s->conf->driver_param; group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); if (group_wpa_s == NULL) { wpa_printf(MSG_ERROR, "P2P: Failed to create new " "wpa_supplicant interface"); return NULL; } wpa_s->pending_interface_name[0] = '\0'; group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO : P2P_GROUP_INTERFACE_CLIENT; wpa_s->global->p2p_group_formation = group_wpa_s; wpa_s->global->pending_group_iface_for_p2ps = 0; wpas_p2p_clone_config(group_wpa_s, wpa_s); if (wpa_s->conf->p2p_interface_random_mac_addr) { if (wpa_drv_set_mac_addr(group_wpa_s, wpa_s->pending_interface_addr) < 0) { wpa_msg(group_wpa_s, MSG_INFO, "Failed to set random MAC address"); wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s, 0); return NULL; } if (wpa_supplicant_update_mac_addr(group_wpa_s) < 0) { wpa_msg(group_wpa_s, MSG_INFO, "Could not update MAC address information"); wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s, 0); return NULL; } wpa_printf(MSG_DEBUG, "P2P: Using random MAC address " MACSTR " for the group", MAC2STR(wpa_s->pending_interface_addr)); } return group_wpa_s; } static void wpas_p2p_group_formation_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); wpas_p2p_group_formation_failed(wpa_s, 0); } static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, int already_deleted) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); wpas_group_formation_completed(wpa_s, 0, already_deleted); } static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure"); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); wpa_s->global->p2p_fail_on_wps_complete = 0; } void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) { if (wpa_s->global->p2p_group_formation != wpa_s) return; /* Speed up group formation timeout since this cannot succeed */ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); } static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) { struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *group_wpa_s; if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_drv_cancel_remain_on_channel(wpa_s); wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; } if (res->status) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", res->status); wpas_notify_p2p_go_neg_completed(wpa_s, res); wpas_p2p_remove_pending_group_interface(wpa_s); return; } if (!res->role_go) { /* Inform driver of the operating channel of GO. */ wpa_drv_set_prob_oper_freq(wpa_s, res->freq); } if (wpa_s->p2p_go_ht40) res->ht40 = 1; if (wpa_s->p2p_go_vht) res->vht = 1; if (wpa_s->p2p_go_he) res->he = 1; if (wpa_s->p2p_go_edmg) res->edmg = 1; res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth; res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s " "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR " wps_method=%s", res->role_go ? "GO" : "client", res->freq, res->ht40, MAC2STR(res->peer_device_addr), MAC2STR(res->peer_interface_addr), p2p_wps_method_text(res->wps_method)); wpas_notify_p2p_go_neg_completed(wpa_s, res); if (res->role_go && wpa_s->p2p_persistent_id >= 0) { struct wpa_ssid *ssid; ssid = wpa_config_get_network(wpa_s->conf, wpa_s->p2p_persistent_id); if (ssid && ssid->disabled == 2 && ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) { size_t len = os_strlen(ssid->passphrase); wpa_printf(MSG_DEBUG, "P2P: Override passphrase based " "on requested persistent group"); os_memcpy(res->passphrase, ssid->passphrase, len); res->passphrase[len] = '\0'; } } if (wpa_s->create_p2p_iface) { group_wpa_s = wpas_p2p_init_group_interface(wpa_s, res->role_go); if (group_wpa_s == NULL) { wpas_p2p_remove_pending_group_interface(wpa_s); eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); wpas_p2p_group_formation_failed(wpa_s, 1); return; } os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); wpa_s->pending_interface_name[0] = '\0'; } else { group_wpa_s = wpa_s->parent; wpa_s->global->p2p_group_formation = group_wpa_s; if (group_wpa_s != wpa_s) wpas_p2p_clone_config(group_wpa_s, wpa_s); } group_wpa_s->p2p_in_provisioning = 1; group_wpa_s->p2pdev = wpa_s; if (group_wpa_s != wpa_s) { os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, sizeof(group_wpa_s->p2p_pin)); group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; } if (res->role_go) { wpas_start_wps_go(group_wpa_s, res, 1); } else { os_get_reltime(&group_wpa_s->scan_min_time); wpas_start_wps_enrollee(group_wpa_s, res); } wpa_s->global->p2p_long_listen = 0; eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_register_timeout(15 + res->peer_config_timeout / 100, (res->peer_config_timeout % 100) * 10000, wpas_p2p_group_formation_timeout, wpa_s, NULL); } static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id, u8 go_intent) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR " dev_passwd_id=%u go_intent=%u", MAC2STR(src), dev_passwd_id, go_intent); wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent); } static void wpas_dev_found(void *ctx, const u8 *addr, const struct p2p_peer_info *info, int new_device) { #ifndef CONFIG_NO_STDOUT_DEBUG struct wpa_supplicant *wpa_s = ctx; char devtype[WPS_DEV_TYPE_BUFSIZE]; char *wfd_dev_info_hex = NULL; #ifdef CONFIG_WIFI_DISPLAY wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems, WFD_SUBELEM_DEVICE_INFO); #endif /* CONFIG_WIFI_DISPLAY */ if (info->p2ps_instance) { char str[256]; const u8 *buf = wpabuf_head(info->p2ps_instance); size_t len = wpabuf_len(info->p2ps_instance); while (len) { u32 id; u16 methods; u8 str_len; if (len < 4 + 2 + 1) break; id = WPA_GET_LE32(buf); buf += sizeof(u32); methods = WPA_GET_BE16(buf); buf += sizeof(u16); str_len = *buf++; if (str_len > len - 4 - 2 - 1) break; os_memcpy(str, buf, str_len); str[str_len] = '\0'; buf += str_len; len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8); wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR " p2p_dev_addr=" MACSTR " pri_dev_type=%s name='%s'" " config_methods=0x%x" " dev_capab=0x%x" " group_capab=0x%x" " adv_id=%x asp_svc=%s%s", MAC2STR(addr), MAC2STR(info->p2p_device_addr), wps_dev_type_bin2str( info->pri_dev_type, devtype, sizeof(devtype)), info->device_name, methods, info->dev_capab, info->group_capab, id, str, info->vendor_elems ? " vendor_elems=1" : ""); } goto done; } wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR " p2p_dev_addr=" MACSTR " pri_dev_type=%s name='%s' config_methods=0x%x " "dev_capab=0x%x group_capab=0x%x%s%s%s new=%d", MAC2STR(addr), MAC2STR(info->p2p_device_addr), wps_dev_type_bin2str(info->pri_dev_type, devtype, sizeof(devtype)), info->device_name, info->config_methods, info->dev_capab, info->group_capab, wfd_dev_info_hex ? " wfd_dev_info=0x" : "", wfd_dev_info_hex ? wfd_dev_info_hex : "", info->vendor_elems ? " vendor_elems=1" : "", new_device); done: os_free(wfd_dev_info_hex); #endif /* CONFIG_NO_STDOUT_DEBUG */ wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device); } static void wpas_dev_lost(void *ctx, const u8 *dev_addr) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr)); wpas_notify_p2p_device_lost(wpa_s, dev_addr); } static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->p2p_scan_work && wpas_abort_ongoing_scan(wpa_s) < 0) wpa_printf(MSG_DEBUG, "P2P: Abort ongoing scan failed"); wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); wpas_notify_p2p_find_stopped(wpa_s); } struct wpas_p2p_listen_work { unsigned int freq; unsigned int duration; struct wpabuf *probe_resp_ie; }; static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork) { if (lwork == NULL) return; wpabuf_free(lwork->probe_resp_ie); os_free(lwork); } static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s) { struct wpas_p2p_listen_work *lwork; if (!wpa_s->p2p_listen_work) return; lwork = wpa_s->p2p_listen_work->ctx; wpas_p2p_listen_work_free(lwork); radio_work_done(wpa_s->p2p_listen_work); wpa_s->p2p_listen_work = NULL; } static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; struct wpas_p2p_listen_work *lwork = work->ctx; unsigned int duration; if (deinit) { if (work->started) { wpa_s->p2p_listen_work = NULL; wpas_stop_listen(wpa_s); } wpas_p2p_listen_work_free(lwork); return; } wpa_s->p2p_listen_work = work; wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL); if (wpa_drv_probe_req_report(wpa_s, 1) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to " "report received Probe Request frames"); wpas_p2p_listen_work_done(wpa_s); return; } wpa_s->pending_listen_freq = lwork->freq; wpa_s->pending_listen_duration = lwork->duration; duration = lwork->duration; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->extra_roc_dur) { wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u", duration, duration + wpa_s->extra_roc_dur); duration += wpa_s->extra_roc_dur; } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver " "to remain on channel (%u MHz) for Listen " "state", lwork->freq); wpas_p2p_listen_work_done(wpa_s); wpa_s->pending_listen_freq = 0; return; } wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = lwork->freq; } static int wpas_start_listen(void *ctx, unsigned int freq, unsigned int duration, const struct wpabuf *probe_resp_ie) { struct wpa_supplicant *wpa_s = ctx; struct wpas_p2p_listen_work *lwork; if (wpa_s->p2p_listen_work) { wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists"); return -1; } lwork = os_zalloc(sizeof(*lwork)); if (lwork == NULL) return -1; lwork->freq = freq; lwork->duration = duration; if (probe_resp_ie) { lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie); if (lwork->probe_resp_ie == NULL) { wpas_p2p_listen_work_free(lwork); return -1; } } if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb, lwork) < 0) { wpas_p2p_listen_work_free(lwork); return -1; } return 0; } static void wpas_stop_listen(void *ctx) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_drv_cancel_remain_on_channel(wpa_s); wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; } wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); /* * Don't cancel Probe Request RX reporting for a connected P2P Client * handling Probe Request frames. */ if (!wpa_s->p2p_cli_probe) wpa_drv_probe_req_report(wpa_s, 0); wpas_p2p_listen_work_done(wpa_s); } static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf, unsigned int freq) { struct wpa_supplicant *wpa_s = ctx; return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, freq, 0); } static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s, const u8 *peer, const char *params, unsigned int generated_pin) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s", MAC2STR(peer), generated_pin, params); } static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s, const u8 *peer, const char *params) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s", MAC2STR(peer), params); } static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, const u8 *dev_addr, const u8 *pri_dev_type, const char *dev_name, u16 supp_config_methods, u8 dev_capab, u8 group_capab, const u8 *group_id, size_t group_id_len) { struct wpa_supplicant *wpa_s = ctx; char devtype[WPS_DEV_TYPE_BUFSIZE]; char params[300]; u8 empty_dev_type[8]; unsigned int generated_pin = 0; struct wpa_supplicant *group = NULL; int res; if (group_id) { for (group = wpa_s->global->ifaces; group; group = group->next) { struct wpa_ssid *s = group->current_ssid; if (s != NULL && s->mode == WPAS_MODE_P2P_GO && group_id_len - ETH_ALEN == s->ssid_len && os_memcmp(group_id + ETH_ALEN, s->ssid, s->ssid_len) == 0) break; } } if (pri_dev_type == NULL) { os_memset(empty_dev_type, 0, sizeof(empty_dev_type)); pri_dev_type = empty_dev_type; } res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR " pri_dev_type=%s name='%s' config_methods=0x%x " "dev_capab=0x%x group_capab=0x%x%s%s", MAC2STR(dev_addr), wps_dev_type_bin2str(pri_dev_type, devtype, sizeof(devtype)), dev_name, supp_config_methods, dev_capab, group_capab, group ? " group=" : "", group ? group->ifname : ""); if (os_snprintf_error(sizeof(params), res)) wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated"); params[sizeof(params) - 1] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) { if (wps_generate_pin(&generated_pin) < 0) { wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN"); wpas_notify_p2p_provision_discovery( wpa_s, peer, 0 /* response */, P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0); return; } wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_KEYPAD) wpas_prov_disc_local_keypad(wpa_s, peer, params); else if (config_methods & WPS_CONFIG_PUSHBUTTON) wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR "%s", MAC2STR(peer), params); wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */, P2P_PROV_DISC_SUCCESS, config_methods, generated_pin); } static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) { struct wpa_supplicant *wpa_s = ctx; unsigned int generated_pin = 0; char params[20]; if (wpa_s->pending_pd_before_join && (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { wpa_s->pending_pd_before_join = 0; wpa_printf(MSG_DEBUG, "P2P: Starting pending " "join-existing-group operation"); wpas_p2p_join_start(wpa_s, 0, NULL, 0); return; } if (wpa_s->pending_pd_use == AUTO_PD_JOIN || wpa_s->pending_pd_use == AUTO_PD_GO_NEG) { int res; res = os_snprintf(params, sizeof(params), " peer_go=%d", wpa_s->pending_pd_use == AUTO_PD_JOIN); if (os_snprintf_error(sizeof(params), res)) params[sizeof(params) - 1] = '\0'; } else params[0] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) wpas_prov_disc_local_keypad(wpa_s, peer, params); else if (config_methods & WPS_CONFIG_KEYPAD) { if (wps_generate_pin(&generated_pin) < 0) { wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN"); wpas_notify_p2p_provision_discovery( wpa_s, peer, 0 /* response */, P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0); return; } wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_PUSHBUTTON) wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR "%s", MAC2STR(peer), params); wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, P2P_PROV_DISC_SUCCESS, config_methods, generated_pin); } static void wpas_prov_disc_fail(void *ctx, const u8 *peer, enum p2p_prov_disc_status status, u32 adv_id, const u8 *adv_mac, const char *deferred_session_resp) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->p2p_fallback_to_go_neg) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto " "failed - fall back to GO Negotiation"); wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=PD-failed"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); return; } if (status == P2P_PROV_DISC_TIMEOUT_JOIN) { wpa_s->pending_pd_before_join = 0; wpa_printf(MSG_DEBUG, "P2P: Starting pending " "join-existing-group operation (no ACK for PD " "Req attempts)"); wpas_p2p_join_start(wpa_s, 0, NULL, 0); return; } if (adv_id && adv_mac && deferred_session_resp) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE " p2p_dev_addr=" MACSTR " status=%d adv_id=%x" " deferred_session_resp='%s'", MAC2STR(peer), status, adv_id, deferred_session_resp); } else if (adv_id && adv_mac) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE " p2p_dev_addr=" MACSTR " status=%d adv_id=%x", MAC2STR(peer), status, adv_id); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE " p2p_dev_addr=" MACSTR " status=%d", MAC2STR(peer), status); } wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, status, 0, 0); } static int freq_included(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels, unsigned int freq) { if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) && wpas_p2p_go_is_peer_freq(wpa_s, freq)) return 1; return 0; } static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s) { unsigned int num = P2P_MAX_CHANNELS; int *common_freqs; int ret; p2p_go_dump_common_freqs(wpa_s); common_freqs = os_calloc(num, sizeof(int)); if (!common_freqs) return; ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num); if (ret < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to get group common freqs"); os_free(common_freqs); return; } os_free(wpa_s->p2p_group_common_freqs); wpa_s->p2p_group_common_freqs = common_freqs; wpa_s->p2p_group_common_freqs_num = num; p2p_go_dump_common_freqs(wpa_s); } /* * Check if the given frequency is one of the possible operating frequencies * set after the completion of the GO Negotiation. */ static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq) { unsigned int i; p2p_go_dump_common_freqs(wpa_s); /* assume no restrictions */ if (!wpa_s->p2p_group_common_freqs_num) return 1; for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { if (wpa_s->p2p_group_common_freqs[i] == freq) return 1; } return 0; } static int wpas_sta_check_ecsa(struct hostapd_data *hapd, struct sta_info *sta, void *ctx) { int *ecsa_support = ctx; *ecsa_support &= sta->ecsa_supported; return 0; } /* Check if all the peers support eCSA */ static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s) { int ecsa_support = 1; ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa, &ecsa_support); return ecsa_support; } /** * Pick the best frequency to use from all the currently used frequencies. */ static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num) { unsigned int i, c; /* find a candidate freq that is supported by P2P */ for (c = 0; c < num; c++) if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq)) break; if (c == num) return 0; /* once we have a candidate, try to find a 'better' one */ for (i = c + 1; i < num; i++) { if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq)) continue; /* * 1. Infrastructure station interfaces have higher preference. * 2. P2P Clients have higher preference. * 3. All others. */ if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) { c = i; break; } if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT)) c = i; } return freqs[c].freq; } static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, int *force_freq, int persistent_group, const struct p2p_channels *channels, int dev_pw_id) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; struct wpa_used_freq_data *freqs; struct wpa_supplicant *grp; int best_freq; if (!persistent_group) { wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR " to join an active group (SSID: %s)", MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len)); if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN) == 0 || os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) { wpa_printf(MSG_DEBUG, "P2P: Accept previously " "authorized invitation"); goto accept_inv; } #ifdef CONFIG_WPS_NFC if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled && dev_pw_id == wpa_s->p2p_oob_dev_pw_id) { wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag"); wpa_s->p2p_wps_method = WPS_NFC; wpa_s->pending_join_wps_method = WPS_NFC; os_memcpy(wpa_s->pending_join_dev_addr, go_dev_addr, ETH_ALEN); os_memcpy(wpa_s->pending_join_iface_addr, bssid, ETH_ALEN); goto accept_inv; } #endif /* CONFIG_WPS_NFC */ /* * Do not accept the invitation automatically; notify user and * request approval. */ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; } grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go); if (grp) { wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already " "running persistent group"); if (*go) os_memcpy(group_bssid, grp->own_addr, ETH_ALEN); goto accept_inv; } if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated " "invitation to re-invoke a persistent group"); os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); } else if (!wpa_s->conf->persistent_reconnect) return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled == 2 && os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 && s->ssid_len == ssid_len && os_memcmp(ssid, s->ssid, ssid_len) == 0) break; } if (!s) { wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR " requested reinvocation of an unknown group", MAC2STR(sa)); return P2P_SC_FAIL_UNKNOWN_GROUP; } if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) { *go = 1; if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { wpa_printf(MSG_DEBUG, "P2P: The only available " "interface is already in use - reject " "invitation"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } if (wpa_s->p2p_mgmt) os_memcpy(group_bssid, wpa_s->parent->own_addr, ETH_ALEN); else os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN); } else if (s->mode == WPAS_MODE_P2P_GO) { *go = 1; if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " "interface address for the group"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } os_memcpy(group_bssid, wpa_s->pending_interface_addr, ETH_ALEN); } accept_inv: wpas_p2p_set_own_freq_preference(wpa_s, 0); best_freq = 0; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (freqs) { int num_channels = wpa_s->num_multichan_concurrent; int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels); best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); os_free(freqs); } /* Get one of the frequencies currently in use */ if (best_freq > 0) { wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces"); wpas_p2p_set_own_freq_preference(wpa_s, best_freq); if (wpa_s->num_multichan_concurrent < 2 || wpas_p2p_num_unused_channels(wpa_s) < 1) { wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces"); *force_freq = best_freq; } } if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 && wpas_p2p_num_unused_channels(wpa_s) > 0) { if (*go == 0) { /* We are the client */ wpa_printf(MSG_DEBUG, "P2P: Peer was found to be " "running a GO but we are capable of MCC, " "figure out the best channel to use"); *force_freq = 0; } else if (!freq_included(wpa_s, channels, *force_freq)) { /* We are the GO, and *force_freq is not in the * intersection */ wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " "in intersection but we are capable of MCC, " "figure out the best channel to use", *force_freq); *force_freq = 0; } } return P2P_SC_SUCCESS; } static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, const u8 *ssid, size_t ssid_len, const u8 *go_dev_addr, u8 status, int op_freq) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled == 2 && s->ssid_len == ssid_len && os_memcmp(ssid, s->ssid, ssid_len) == 0) break; } if (status == P2P_SC_SUCCESS) { wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR " was accepted; op_freq=%d MHz, SSID=%s", MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len)); if (s) { int go = s->mode == WPAS_MODE_P2P_GO; if (go) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_ACCEPTED "sa=" MACSTR " persistent=%d freq=%d", MAC2STR(sa), s->id, op_freq); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_ACCEPTED "sa=" MACSTR " persistent=%d", MAC2STR(sa), s->id); } wpas_p2p_group_add_persistent( wpa_s, s, go, 0, op_freq, 0, wpa_s->conf->p2p_go_ht40, wpa_s->conf->p2p_go_vht, 0, wpa_s->conf->p2p_go_he, wpa_s->conf->p2p_go_edmg, NULL, go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, - 1); + 1, is_p2p_allow_6ghz(wpa_s->global->p2p)); } else if (bssid) { wpa_s->user_initiated_pd = 0; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_ACCEPTED "sa=" MACSTR " go_dev_addr=" MACSTR " bssid=" MACSTR " unknown-network", MAC2STR(sa), MAC2STR(go_dev_addr), MAC2STR(bssid)); wpas_p2p_join(wpa_s, bssid, go_dev_addr, wpa_s->p2p_wps_method, 0, op_freq, ssid, ssid_len); } return; } if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR " was rejected (status %u)", MAC2STR(sa), status); return; } if (!s) { if (bssid) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR " go_dev_addr=" MACSTR " bssid=" MACSTR " unknown-network", MAC2STR(sa), MAC2STR(go_dev_addr), MAC2STR(bssid)); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR " go_dev_addr=" MACSTR " unknown-network", MAC2STR(sa), MAC2STR(go_dev_addr)); } wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid, 0, op_freq); return; } if (s->mode == WPAS_MODE_P2P_GO && op_freq) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR " persistent=%d freq=%d", MAC2STR(sa), s->id, op_freq); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR " persistent=%d", MAC2STR(sa), s->id); } wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid, s->id, op_freq); } static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *peer, int inv) { size_t i; struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; if (ssid == NULL) return; for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) { if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer, ETH_ALEN) == 0) break; } if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) { if (ssid->mode != WPAS_MODE_P2P_GO && os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d " "due to invitation result", ssid->id); wpas_notify_network_removed(wpa_s, ssid); wpa_config_remove_network(wpa_s->conf, ssid->id); return; } return; /* Peer not found in client list */ } wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent " "group %d client list%s", MAC2STR(peer), ssid->id, inv ? " due to invitation result" : ""); os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN, ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN, (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN); ssid->num_p2p_clients--; if (p2p_wpa_s->conf->update_config && wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, const u8 *peer) { struct wpa_ssid *ssid; wpa_s = wpa_s->global->p2p_invite_group; if (wpa_s == NULL) return; /* No known invitation group */ ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) return; /* Not operating as a GO in persistent group */ ssid = wpas_p2p_get_persistent(wpa_s->p2pdev, peer, ssid->ssid, ssid->ssid_len); wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, const struct p2p_channels *channels, const u8 *peer, int neg_freq, int peer_oper_freq) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid; int freq; if (bssid) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT "status=%d " MACSTR, status, MAC2STR(bssid)); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT "status=%d ", status); } wpas_notify_p2p_invitation_result(wpa_s, status, bssid); wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR, status, MAC2STR(peer)); if (wpa_s->pending_invite_ssid_id == -1) { struct wpa_supplicant *group_if = wpa_s->global->p2p_invite_group; if (status == P2P_SC_FAIL_UNKNOWN_GROUP) wpas_remove_persistent_client(wpa_s, peer); /* * Invitation to an active group. If this is successful and we * are the GO, set the client wait to postpone some concurrent * operations and to allow provisioning and connection to happen * more quickly. */ if (status == P2P_SC_SUCCESS && group_if && group_if->current_ssid && group_if->current_ssid->mode == WPAS_MODE_P2P_GO) { os_get_reltime(&wpa_s->global->p2p_go_wait_client); #ifdef CONFIG_TESTING_OPTIONS if (group_if->p2p_go_csa_on_inv) { wpa_printf(MSG_DEBUG, "Testing: force P2P GO CSA after invitation"); eloop_cancel_timeout( wpas_p2p_reconsider_moving_go, wpa_s, NULL); eloop_register_timeout( 0, 50000, wpas_p2p_reconsider_moving_go, wpa_s, NULL); } #endif /* CONFIG_TESTING_OPTIONS */ } return; } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another " "invitation exchange to indicate readiness for " "re-invocation"); } if (status != P2P_SC_SUCCESS) { if (status == P2P_SC_FAIL_UNKNOWN_GROUP) { ssid = wpa_config_get_network( wpa_s->conf, wpa_s->pending_invite_ssid_id); wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } wpas_p2p_remove_pending_group_interface(wpa_s); return; } ssid = wpa_config_get_network(wpa_s->conf, wpa_s->pending_invite_ssid_id); if (ssid == NULL) { wpa_printf(MSG_ERROR, "P2P: Could not find persistent group " "data matching with invitation"); return; } /* * The peer could have missed our ctrl::ack frame for Invitation * Response and continue retransmitting the frame. To reduce the * likelihood of the peer not getting successful TX status for the * Invitation Response frame, wait a short time here before starting * the persistent group so that we will remain on the current channel to * acknowledge any possible retransmission from the peer. */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before " "starting persistent group"); os_sleep(0, 50000); if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO && freq_included(wpa_s, channels, neg_freq)) freq = neg_freq; else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO && freq_included(wpa_s, channels, peer_oper_freq)) freq = peer_oper_freq; else freq = 0; wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s", freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); wpas_p2p_group_add_persistent(wpa_s, ssid, ssid->mode == WPAS_MODE_P2P_GO, wpa_s->p2p_persistent_go_freq, freq, wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, channels, ssid->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0, 1); + 0, 1, + is_p2p_allow_6ghz(wpa_s->global->p2p)); } static int wpas_p2p_disallowed_freq(struct wpa_global *global, unsigned int freq) { if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq)) return 1; return freq_range_list_includes(&global->p2p_disallow_freq, freq); } static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan) { reg->channel[reg->channels] = chan; reg->channels++; } static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, struct p2p_channels *chan, struct p2p_channels *cli_chan) { int i, cla = 0; wpa_s->global->p2p_24ghz_social_channels = 1; os_memset(cli_chan, 0, sizeof(*cli_chan)); wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " "band"); /* Operating class 81 - 2.4 GHz band channels 1..13 */ chan->reg_class[cla].reg_class = 81; chan->reg_class[cla].channels = 0; for (i = 0; i < 11; i++) { if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], i + 1); } if (chan->reg_class[cla].channels) cla++; wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz " "band"); /* Operating class 115 - 5 GHz, channels 36-48 */ chan->reg_class[cla].reg_class = 115; chan->reg_class[cla].channels = 0; if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 36); if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 40); if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 44); if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 48); if (chan->reg_class[cla].channels) cla++; wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz " "band"); /* Operating class 124 - 5 GHz, channels 149,153,157,161 */ chan->reg_class[cla].reg_class = 124; chan->reg_class[cla].channels = 0; if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 149); if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 153); if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 157); if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5)) wpas_p2p_add_chan(&chan->reg_class[cla], 161); if (chan->reg_class[cla].channels) cla++; chan->reg_classes = cla; return 0; } -static int has_channel(struct wpa_global *global, - struct hostapd_hw_modes *mode, u8 chan, int *flags) +static enum chan_allowed has_channel(struct wpa_global *global, + struct hostapd_hw_modes *mode, u8 op_class, + u8 chan, int *flags) { int i; unsigned int freq; - freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) + - chan * 5; + freq = ieee80211_chan_to_freq(NULL, op_class, chan); if (wpas_p2p_disallowed_freq(global, freq)) return NOT_ALLOWED; for (i = 0; i < mode->num_channels; i++) { - if (mode->channels[i].chan == chan) { + if ((unsigned int) mode->channels[i].freq == freq) { if (flags) *flags = mode->channels[i].flag; if (mode->channels[i].flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_RADAR)) return NOT_ALLOWED; if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) return NO_IR; return ALLOWED; } } return NOT_ALLOWED; } static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, - u8 channel) + u8 channel, const u8 *center_channels, + size_t num_chan) { - u8 center_channels[] = { 42, 58, 106, 122, 138, 155, 171 }; size_t i; if (mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; - for (i = 0; i < ARRAY_SIZE(center_channels); i++) + for (i = 0; i < num_chan; i++) /* * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), * so the center channel is 6 channels away from the start/end. */ if (channel >= center_channels[i] - 6 && channel <= center_channels[i] + 6) return center_channels[i]; return 0; } +static const u8 center_channels_5ghz_80mhz[] = { 42, 58, 106, 122, 138, + 155, 171 }; +static const u8 center_channels_6ghz_80mhz[] = { 7, 23, 39, 55, 71, 87, 103, + 119, 135, 151, 167, 183, 199, + 215 }; + static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, - u8 channel, u8 bw) + u8 op_class, u8 channel, u8 bw) { u8 center_chan; int i, flags; enum chan_allowed res, ret = ALLOWED; + const u8 *chans; + size_t num_chans; + bool is_6ghz = is_6ghz_op_class(op_class); - center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel); + if (is_6ghz) { + chans = center_channels_6ghz_80mhz; + num_chans = ARRAY_SIZE(center_channels_6ghz_80mhz); + } else { + chans = center_channels_5ghz_80mhz; + num_chans = ARRAY_SIZE(center_channels_5ghz_80mhz); + } + center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel, + chans, num_chans); if (!center_chan) return NOT_ALLOWED; - if (center_chan >= 58 && center_chan <= 138) + if (!is_6ghz && center_chan >= 58 && center_chan <= 138) return NOT_ALLOWED; /* Do not allow DFS channels for P2P */ /* check all the channels are available */ for (i = 0; i < 4; i++) { int adj_chan = center_chan - 6 + i * 4; - res = has_channel(wpa_s->global, mode, adj_chan, &flags); + res = has_channel(wpa_s->global, mode, op_class, adj_chan, + &flags); if (res == NOT_ALLOWED) return NOT_ALLOWED; if (res == NO_IR) ret = NO_IR; if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) return NOT_ALLOWED; if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) return NOT_ALLOWED; if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) return NOT_ALLOWED; if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)) return NOT_ALLOWED; } return ret; } static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, - u8 channel) + u8 channel, const u8 *center_channels, + size_t num_chan) { - u8 center_channels[] = { 50, 114, 163 }; unsigned int i; if (mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; - for (i = 0; i < ARRAY_SIZE(center_channels); i++) + for (i = 0; i < num_chan; i++) /* * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), * so the center channel is 14 channels away from the start/end. */ if (channel >= center_channels[i] - 14 && channel <= center_channels[i] + 14) return center_channels[i]; return 0; } +static const u8 center_channels_5ghz_160mhz[] = { 50, 114, 163 }; +static const u8 center_channels_6ghz_160mhz[] = { 15, 47, 79, 111, 143, 175, + 207 }; + static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, - u8 channel, u8 bw) + u8 op_class, u8 channel, u8 bw) { u8 center_chan; int i, flags; enum chan_allowed res, ret = ALLOWED; + const u8 *chans; + size_t num_chans; - center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel); + if (is_6ghz_op_class(op_class)) { + chans = center_channels_6ghz_160mhz; + num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz); + } else { + chans = center_channels_5ghz_160mhz; + num_chans = ARRAY_SIZE(center_channels_5ghz_160mhz); + } + center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel, + chans, num_chans); if (!center_chan) return NOT_ALLOWED; /* VHT 160 MHz uses DFS channels in most countries. */ /* Check all the channels are available */ for (i = 0; i < 8; i++) { int adj_chan = center_chan - 14 + i * 4; - res = has_channel(wpa_s->global, mode, adj_chan, &flags); + res = has_channel(wpa_s->global, mode, op_class, adj_chan, + &flags); if (res == NOT_ALLOWED) return NOT_ALLOWED; if (res == NO_IR) ret = NO_IR; if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) return NOT_ALLOWED; if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) return NOT_ALLOWED; if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) return NOT_ALLOWED; if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) return NOT_ALLOWED; if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) return NOT_ALLOWED; if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) return NOT_ALLOWED; if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) return NOT_ALLOWED; if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)) return NOT_ALLOWED; } return ret; } static enum chan_allowed wpas_p2p_verify_edmg(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel) { struct ieee80211_edmg_config edmg; hostapd_encode_edmg_chan(1, channel, 0, &edmg); if (edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg)) return ALLOWED; return NOT_ALLOWED; } static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, - u8 channel, u8 bw) + u8 op_class, u8 channel, u8 bw) { int flag = 0; enum chan_allowed res, res2; - res2 = res = has_channel(wpa_s->global, mode, channel, &flag); + res2 = res = has_channel(wpa_s->global, mode, op_class, channel, &flag); if (bw == BW40MINUS) { if (!(flag & HOSTAPD_CHAN_HT40MINUS)) return NOT_ALLOWED; - res2 = has_channel(wpa_s->global, mode, channel - 4, NULL); + res2 = has_channel(wpa_s->global, mode, op_class, channel - 4, + NULL); } else if (bw == BW40PLUS) { if (!(flag & HOSTAPD_CHAN_HT40PLUS)) return NOT_ALLOWED; - res2 = has_channel(wpa_s->global, mode, channel + 4, NULL); + res2 = has_channel(wpa_s->global, mode, op_class, channel + 4, + NULL); } else if (bw == BW80) { - res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw); + res2 = wpas_p2p_verify_80mhz(wpa_s, mode, op_class, channel, + bw); } else if (bw == BW160) { - res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw); + res2 = wpas_p2p_verify_160mhz(wpa_s, mode, op_class, channel, + bw); } else if (bw == BW4320 || bw == BW6480 || bw == BW8640) { return wpas_p2p_verify_edmg(wpa_s, mode, channel); } if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) return NOT_ALLOWED; if (res == NO_IR || res2 == NO_IR) return NO_IR; return res; } static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, struct p2p_channels *chan, struct p2p_channels *cli_chan, bool p2p_disable_6ghz) { struct hostapd_hw_modes *mode; int cla, op, cli_cla; if (wpa_s->hw.modes == NULL) { wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching " "of all supported channels; assume dualband " "support"); return wpas_p2p_default_channels(wpa_s, chan, cli_chan); } cla = cli_cla = 0; for (op = 0; global_op_class[op].op_class; op++) { const struct oper_class_map *o = &global_op_class[op]; u8 ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; if (o->p2p == NO_P2P_SUPP || (is_6ghz_op_class(o->op_class) && p2p_disable_6ghz)) continue; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode, is_6ghz_op_class(o->op_class)); if (mode == NULL) continue; if (mode->mode == HOSTAPD_MODE_IEEE80211G) wpa_s->global->p2p_24ghz_social_channels = 1; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { enum chan_allowed res; /* Check for non-continuous jump in channel index * incrementation */ if ((o->op_class >= 128 && o->op_class <= 130) && ch < 149 && ch + o->inc > 149) ch = 149; - res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + res = wpas_p2p_verify_channel(wpa_s, mode, o->op_class, + ch, o->bw); if (res == ALLOWED) { if (reg == NULL) { if (cla == P2P_MAX_REG_CLASSES) continue; wpa_printf(MSG_DEBUG, "P2P: Add operating class %u", o->op_class); reg = &chan->reg_class[cla]; cla++; reg->reg_class = o->op_class; } if (reg->channels == P2P_MAX_REG_CLASS_CHANNELS) continue; reg->channel[reg->channels] = ch; reg->channels++; } else if (res == NO_IR && wpa_s->conf->p2p_add_cli_chan) { if (cli_reg == NULL) { if (cli_cla == P2P_MAX_REG_CLASSES) continue; wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)", o->op_class); cli_reg = &cli_chan->reg_class[cli_cla]; cli_cla++; cli_reg->reg_class = o->op_class; } if (cli_reg->channels == P2P_MAX_REG_CLASS_CHANNELS) continue; cli_reg->channel[cli_reg->channels] = ch; cli_reg->channels++; } } if (reg) { wpa_hexdump(MSG_DEBUG, "P2P: Channels", reg->channel, reg->channels); } if (cli_reg) { wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)", cli_reg->channel, cli_reg->channels); } } chan->reg_classes = cla; cli_chan->reg_classes = cli_cla; return 0; } int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel) { int op; enum chan_allowed ret; for (op = 0; global_op_class[op].op_class; op++) { const struct oper_class_map *o = &global_op_class[op]; u8 ch; if (o->p2p == NO_P2P_SUPP || (is_6ghz_op_class(o->op_class) && wpa_s->conf->p2p_6ghz_disable)) continue; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { if (o->mode != HOSTAPD_MODE_IEEE80211A || (o->bw != BW40PLUS && o->bw != BW40MINUS) || ch != channel) continue; - ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class, + ch, o->bw); if (ret == ALLOWED) return (o->bw == BW40MINUS) ? -1 : 1; } } return 0; } int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, u8 channel) + struct hostapd_hw_modes *mode, u8 channel, + u8 op_class) { - if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW80)) + const u8 *chans; + size_t num_chans; + + if (!wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80)) return 0; - return wpas_p2p_get_center_80mhz(wpa_s, mode, channel); + if (is_6ghz_op_class(op_class)) { + chans = center_channels_6ghz_80mhz; + num_chans = ARRAY_SIZE(center_channels_6ghz_80mhz); + } else { + chans = center_channels_5ghz_80mhz; + num_chans = ARRAY_SIZE(center_channels_5ghz_80mhz); + } + return wpas_p2p_get_center_80mhz(wpa_s, mode, channel, + chans, num_chans); } int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, u8 channel) + struct hostapd_hw_modes *mode, u8 channel, + u8 op_class) { - if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160)) + const u8 *chans; + size_t num_chans; + + if (!wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160)) return 0; - return wpas_p2p_get_center_160mhz(wpa_s, mode, channel); + if (is_6ghz_op_class(op_class)) { + chans = center_channels_6ghz_160mhz; + num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz); + } else { + chans = center_channels_5ghz_160mhz; + num_chans = ARRAY_SIZE(center_channels_5ghz_160mhz); + } + return wpas_p2p_get_center_160mhz(wpa_s, mode, channel, + chans, num_chans); } static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len) { struct wpa_supplicant *wpa_s = ctx; for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0) break; } if (wpa_s == NULL) return -1; return wpa_drv_get_noa(wpa_s, buf, buf_len); } struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len) { for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { struct wpa_ssid *s = wpa_s->current_ssid; if (s == NULL) continue; if (s->mode != WPAS_MODE_P2P_GO && s->mode != WPAS_MODE_AP && s->mode != WPAS_MODE_P2P_GROUP_FORMATION) continue; if (s->ssid_len != ssid_len || os_memcmp(ssid, s->ssid, ssid_len) != 0) continue; return wpa_s; } return NULL; } struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, const u8 *peer_dev_addr) { for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group)) continue; if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0) return wpa_s; } return NULL; } static int wpas_go_connected(void *ctx, const u8 *dev_addr) { struct wpa_supplicant *wpa_s = ctx; return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL; } static int wpas_is_concurrent_session_active(void *ctx) { struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *ifs; for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { if (ifs == wpa_s) continue; if (ifs->wpa_state > WPA_ASSOCIATED) return 1; } return 0; } static void wpas_p2p_debug_print(void *ctx, int level, const char *msg) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, level, "P2P: %s", msg); } int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, const char *conf_p2p_dev) { struct wpa_interface iface; struct wpa_supplicant *p2pdev_wpa_s; char ifname[100]; char force_name[100]; int ret; const u8 *if_addr = NULL; ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s", wpa_s->ifname); if (os_snprintf_error(sizeof(ifname), ret)) return -1; /* Cut length at the maximum size. Note that we don't need to ensure * collision free names here as the created interface is not a netdev. */ ifname[IFNAMSIZ - 1] = '\0'; force_name[0] = '\0'; wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE; if (wpa_s->conf->p2p_device_random_mac_addr == 2 && !is_zero_ether_addr(wpa_s->conf->p2p_device_persistent_mac_addr)) if_addr = wpa_s->conf->p2p_device_persistent_mac_addr; ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, if_addr, NULL, force_name, wpa_s->pending_interface_addr, NULL); if (ret < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface"); return ret; } os_strlcpy(wpa_s->pending_interface_name, ifname, sizeof(wpa_s->pending_interface_name)); os_memset(&iface, 0, sizeof(iface)); iface.p2p_mgmt = 1; iface.ifname = wpa_s->pending_interface_name; iface.driver = wpa_s->driver->name; iface.driver_param = wpa_s->conf->driver_param; /* * If a P2P Device configuration file was given, use it as the interface * configuration file (instead of using parent's configuration file. */ if (conf_p2p_dev) { iface.confname = conf_p2p_dev; iface.ctrl_interface = NULL; } else { iface.confname = wpa_s->confname; iface.ctrl_interface = wpa_s->conf->ctrl_interface; } p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); if (!p2pdev_wpa_s) { wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); return -1; } p2pdev_wpa_s->p2pdev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return 0; } static void wpas_presence_resp(void *ctx, const u8 *src, u8 status, const u8 *noa, size_t noa_len) { struct wpa_supplicant *wpa_s, *intf = ctx; char hex[100]; for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_s->waiting_presence_resp) break; } if (!wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response"); return; } wpa_s->waiting_presence_resp = 0; wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len); wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR " status=%u noa=%s", MAC2STR(src), status, hex); } static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, u8 *ret_ssid, size_t *ret_ssid_len, u8 *intended_iface_addr) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len); if (s) { os_memcpy(ret_ssid, s->ssid, s->ssid_len); *ret_ssid_len = s->ssid_len; os_memcpy(go_dev_addr, s->bssid, ETH_ALEN); if (s->mode != WPAS_MODE_P2P_GO) { os_memset(intended_iface_addr, 0, ETH_ALEN); } else if (wpas_p2p_create_iface(wpa_s)) { if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO)) return 0; os_memcpy(intended_iface_addr, wpa_s->pending_interface_addr, ETH_ALEN); } else { os_memcpy(intended_iface_addr, wpa_s->own_addr, ETH_ALEN); } return 1; } return 0; } static int wpas_get_go_info(void *ctx, u8 *intended_addr, u8 *ssid, size_t *ssid_len, int *group_iface, unsigned int *freq) { struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *go; struct wpa_ssid *s; /* * group_iface will be set to 1 only if a dedicated interface for P2P * role is required. First, we try to reuse an active GO. However, * if it is not present, we will try to reactivate an existing * persistent group and set group_iface to 1, so the caller will know * that the pending interface should be used. */ *group_iface = 0; if (freq) *freq = 0; go = wpas_p2p_get_go_group(wpa_s); if (!go) { s = wpas_p2p_get_persistent_go(wpa_s); *group_iface = wpas_p2p_create_iface(wpa_s); if (s) os_memcpy(intended_addr, s->bssid, ETH_ALEN); else return 0; } else { s = go->current_ssid; os_memcpy(intended_addr, go->own_addr, ETH_ALEN); if (freq) *freq = go->assoc_freq; } os_memcpy(ssid, s->ssid, s->ssid_len); *ssid_len = s->ssid_len; return 1; } static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go, const u8 *ssid, size_t ssid_len) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; int save_config = 0; size_t i; /* Start with our first choice of Persistent Groups */ while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) { if (go && ssid && ssid_len && s->ssid_len == ssid_len && os_memcmp(go, s->bssid, ETH_ALEN) == 0 && os_memcmp(ssid, s->ssid, ssid_len) == 0) break; /* Remove stale persistent group */ if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove stale persistent group id=%d", s->id); wpas_notify_persistent_group_removed(wpa_s, s); wpa_config_remove_network(wpa_s->conf, s->id); save_config = 1; continue; } for (i = 0; i < s->num_p2p_clients; i++) { if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, peer, ETH_ALEN) != 0) continue; os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN, s->p2p_client_list + (i + 1) * 2 * ETH_ALEN, (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN); break; } s->num_p2p_clients--; save_config = 1; } if (save_config) p2p_config_write(wpa_s); /* Return TRUE if valid SSID remains */ return s != NULL; } static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len, const u8 *feat_cap, size_t feat_cap_len) { static const char pref[] = " feature_cap="; int ret; buf[0] = '\0'; /* * We expect a feature capability to contain at least one byte to be * reported. The string buffer provided by the caller function is * expected to be big enough to contain all bytes of the attribute for * known specifications. This function truncates the reported bytes if * the feature capability data exceeds the string buffer size. */ if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2) return; os_memcpy(buf, pref, sizeof(pref)); ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1], buf_len - sizeof(pref) + 1, feat_cap, feat_cap_len); if (ret != (2 * (int) feat_cap_len)) wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated"); } static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, const u8 *adv_mac, const u8 *ses_mac, const u8 *grp_mac, u32 adv_id, u32 ses_id, u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, const u8 *feat_cap, size_t feat_cap_len, unsigned int freq, const u8 *group_ssid, size_t group_ssid_len) { struct wpa_supplicant *wpa_s = ctx; u8 mac[ETH_ALEN]; struct wpa_ssid *persistent_go, *stale, *s = NULL; int save_config = 0; struct wpa_supplicant *go_wpa_s; char feat_cap_str[256]; if (!dev) return; os_memset(mac, 0, ETH_ALEN); if (!adv_mac) adv_mac = mac; if (!ses_mac) ses_mac = mac; if (!grp_mac) grp_mac = mac; wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str), feat_cap, feat_cap_len); if (prov_start) { if (session_info == NULL) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_START MACSTR " adv_id=%x conncap=%x" " adv_mac=" MACSTR " session=%x mac=" MACSTR " dev_passwd_id=%d%s", MAC2STR(dev), adv_id, conncap, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), passwd_id, feat_cap_str); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_START MACSTR " adv_id=%x conncap=%x" " adv_mac=" MACSTR " session=%x mac=" MACSTR " dev_passwd_id=%d info='%s'%s", MAC2STR(dev), adv_id, conncap, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), passwd_id, session_info, feat_cap_str); } return; } go_wpa_s = wpas_p2p_get_go_group(wpa_s); persistent_go = wpas_p2p_get_persistent_go(wpa_s); if (status && status != P2P_SC_SUCCESS_DEFERRED) { if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); if (persistent_go && !persistent_go->num_p2p_clients) { /* remove empty persistent GO */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove empty persistent group id=%d", persistent_go->id); wpas_notify_persistent_group_removed(wpa_s, persistent_go); wpa_config_remove_network(wpa_s->conf, persistent_go->id); } wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR "%s", MAC2STR(dev), status, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), feat_cap_str); return; } /* Clean up stale persistent groups with this device */ if (persist_ssid && persist_ssid_size) s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, persist_ssid_size); if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO && is_zero_ether_addr(grp_mac)) { wpa_dbg(wpa_s, MSG_ERROR, "P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address"); return; } for (;;) { stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0); if (!stale) break; if (s && s->ssid_len == stale->ssid_len && os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 && os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0) break; /* Remove stale persistent group */ if (stale->mode != WPAS_MODE_P2P_GO || stale->num_p2p_clients <= 1) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove stale persistent group id=%d", stale->id); wpas_notify_persistent_group_removed(wpa_s, stale); wpa_config_remove_network(wpa_s->conf, stale->id); } else { size_t i; for (i = 0; i < stale->num_p2p_clients; i++) { if (os_memcmp(stale->p2p_client_list + i * ETH_ALEN, dev, ETH_ALEN) == 0) { os_memmove(stale->p2p_client_list + i * ETH_ALEN, stale->p2p_client_list + (i + 1) * ETH_ALEN, (stale->num_p2p_clients - i - 1) * ETH_ALEN); break; } } stale->num_p2p_clients--; } save_config = 1; } if (save_config) p2p_config_write(wpa_s); if (s) { if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); if (persistent_go && s != persistent_go && !persistent_go->num_p2p_clients) { /* remove empty persistent GO */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove empty persistent group id=%d", persistent_go->id); wpas_notify_persistent_group_removed(wpa_s, persistent_go); wpa_config_remove_network(wpa_s->conf, persistent_go->id); /* Save config */ } wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR " persist=%d%s", MAC2STR(dev), status, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), s->id, feat_cap_str); return; } wpa_s->global->pending_p2ps_group = 0; wpa_s->global->pending_p2ps_group_freq = 0; if (conncap == P2PS_SETUP_GROUP_OWNER) { /* * We need to copy the interface name. Simply saving a * pointer isn't enough, since if we use pending_interface_name * it will be overwritten when the group is added. */ char go_ifname[100]; go_ifname[0] = '\0'; if (!go_wpa_s) { if (!response_done) { wpa_s->global->pending_p2ps_group = 1; wpa_s->global->pending_p2ps_group_freq = freq; } if (!wpas_p2p_create_iface(wpa_s)) os_memcpy(go_ifname, wpa_s->ifname, sizeof(go_ifname)); else if (wpa_s->pending_interface_name[0]) os_memcpy(go_ifname, wpa_s->pending_interface_name, sizeof(go_ifname)); if (!go_ifname[0]) { wpas_p2ps_prov_complete( wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, dev, adv_mac, ses_mac, grp_mac, adv_id, ses_id, 0, 0, NULL, 0, 0, 0, NULL, NULL, 0, 0, NULL, 0); return; } /* If PD Resp complete, start up the GO */ if (response_done && persistent_go) { wpas_p2p_group_add_persistent( wpa_s, persistent_go, 0, 0, freq, 0, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0, 0); + 0, 0, false); } else if (response_done) { wpas_p2p_group_add(wpa_s, 1, freq, - 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, false); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN); wpa_s->p2ps_method_config_any = 1; } } else if (passwd_id == DEV_PW_P2PS_DEFAULT) { os_memcpy(go_ifname, go_wpa_s->ifname, sizeof(go_ifname)); if (is_zero_ether_addr(grp_mac)) { wpa_dbg(go_wpa_s, MSG_DEBUG, "P2P: Setting PIN-1 for ANY"); wpa_supplicant_ap_wps_pin(go_wpa_s, NULL, "12345670", NULL, 0, 0); } else { wpa_dbg(go_wpa_s, MSG_DEBUG, "P2P: Setting PIN-1 for " MACSTR, MAC2STR(grp_mac)); wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac, "12345670", NULL, 0, 0); } os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN); wpa_s->p2ps_method_config_any = 1; } wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR " dev_passwd_id=%d go=%s%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), passwd_id, go_ifname, feat_cap_str); return; } if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); if (persistent_go && !persistent_go->num_p2p_clients) { /* remove empty persistent GO */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove empty persistent group id=%d", persistent_go->id); wpas_notify_persistent_group_removed(wpa_s, persistent_go); wpa_config_remove_network(wpa_s->conf, persistent_go->id); } if (conncap == P2PS_SETUP_CLIENT) { char ssid_hex[32 * 2 + 1]; if (group_ssid) wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex), group_ssid, group_ssid_len); else ssid_hex[0] = '\0'; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR " dev_passwd_id=%d join=" MACSTR "%s%s%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), passwd_id, MAC2STR(grp_mac), feat_cap_str, group_ssid ? " group_ssid=" : "", ssid_hex); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR " dev_passwd_id=%d%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), passwd_id, feat_cap_str); } } static int _wpas_p2p_in_progress(void *ctx) { struct wpa_supplicant *wpa_s = ctx; return wpas_p2p_in_progress(wpa_s); } static int wpas_prov_disc_resp_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *persistent_go; unsigned int freq; if (!wpa_s->global->pending_p2ps_group) return 0; freq = wpa_s->global->pending_p2ps_group_freq; wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->global->pending_p2ps_group = 0; if (wpas_p2p_get_go_group(wpa_s)) return 0; persistent_go = wpas_p2p_get_persistent_go(wpa_s); if (persistent_go) { wpas_p2p_group_add_persistent( wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? - P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0, + is_p2p_allow_6ghz(wpa_s->global->p2p)); } else { - wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0, + is_p2p_allow_6ghz(wpa_s->global->p2p)); } return 1; } static int wpas_p2p_get_pref_freq_list(void *ctx, int go, unsigned int *len, unsigned int *freq_list) { struct wpa_supplicant *wpa_s = ctx; return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO : WPA_IF_P2P_CLIENT, len, freq_list); } int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) { u8 addr[ETH_ALEN] = {0}; if (wpa_s->conf->p2p_device_random_mac_addr == 0) return 0; if (wpa_s->conf->p2p_device_random_mac_addr == 2) { if (is_zero_ether_addr( wpa_s->conf->p2p_device_persistent_mac_addr) && !is_zero_ether_addr(wpa_s->own_addr)) { os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr, wpa_s->own_addr, ETH_ALEN); } return 0; } if (!wpa_s->conf->ssid) { if (random_mac_addr(addr) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to generate random MAC address"); return -EINVAL; } /* Store generated MAC address. */ os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr, addr, ETH_ALEN); } else { /* If there are existing saved groups, restore last MAC address. * if there is no last used MAC address, the last one is * factory MAC. */ if (is_zero_ether_addr( wpa_s->conf->p2p_device_persistent_mac_addr)) return 0; os_memcpy(addr, wpa_s->conf->p2p_device_persistent_mac_addr, ETH_ALEN); wpa_msg(wpa_s, MSG_DEBUG, "Restore last used MAC address."); } if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to set random MAC address"); return -EINVAL; } if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Could not update MAC address information"); return -EINVAL; } wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR, MAC2STR(addr)); return 0; } /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * Returns: 0 on success, -1 on failure */ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) { struct p2p_config p2p; int i; if (wpa_s->conf->p2p_disabled) return 0; if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) return 0; if (global->p2p) return 0; if (wpas_p2p_mac_setup(wpa_s) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize P2P random MAC address."); return -1; } os_memset(&p2p, 0, sizeof(p2p)); p2p.cb_ctx = wpa_s; p2p.debug_print = wpas_p2p_debug_print; p2p.p2p_scan = wpas_p2p_scan; p2p.send_action = wpas_send_action; p2p.send_action_done = wpas_send_action_done; p2p.go_neg_completed = wpas_go_neg_completed; p2p.go_neg_req_rx = wpas_go_neg_req_rx; p2p.dev_found = wpas_dev_found; p2p.dev_lost = wpas_dev_lost; p2p.find_stopped = wpas_find_stopped; p2p.start_listen = wpas_start_listen; p2p.stop_listen = wpas_stop_listen; p2p.send_probe_resp = wpas_send_probe_resp; p2p.sd_request = wpas_sd_request; p2p.sd_response = wpas_sd_response; p2p.prov_disc_req = wpas_prov_disc_req; p2p.prov_disc_resp = wpas_prov_disc_resp; p2p.prov_disc_fail = wpas_prov_disc_fail; p2p.invitation_process = wpas_invitation_process; p2p.invitation_received = wpas_invitation_received; p2p.invitation_result = wpas_invitation_result; p2p.get_noa = wpas_get_noa; p2p.go_connected = wpas_go_connected; p2p.presence_resp = wpas_presence_resp; p2p.is_concurrent_session_active = wpas_is_concurrent_session_active; p2p.is_p2p_in_progress = _wpas_p2p_in_progress; p2p.get_persistent_group = wpas_get_persistent_group; p2p.get_go_info = wpas_get_go_info; p2p.remove_stale_groups = wpas_remove_stale_groups; p2p.p2ps_prov_complete = wpas_p2ps_prov_complete; p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb; p2p.p2ps_group_capability = p2ps_group_capability; p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list; p2p.p2p_6ghz_disable = wpa_s->conf->p2p_6ghz_disable; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); p2p.dev_name = wpa_s->conf->device_name; p2p.manufacturer = wpa_s->conf->manufacturer; p2p.model_name = wpa_s->conf->model_name; p2p.model_number = wpa_s->conf->model_number; p2p.serial_number = wpa_s->conf->serial_number; if (wpa_s->wps) { os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16); p2p.config_methods = wpa_s->wps->config_methods; } if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels, p2p.p2p_6ghz_disable)) { wpa_printf(MSG_ERROR, "P2P: Failed to configure supported channel list"); return -1; } if (wpa_s->conf->p2p_listen_reg_class && wpa_s->conf->p2p_listen_channel) { p2p.reg_class = wpa_s->conf->p2p_listen_reg_class; p2p.channel = wpa_s->conf->p2p_listen_channel; p2p.channel_forced = 1; } else { /* * Pick one of the social channels randomly as the listen * channel. */ if (p2p_config_get_random_social(&p2p, &p2p.reg_class, &p2p.channel, &global->p2p_go_avoid_freq, &global->p2p_disallow_freq) != 0) { wpa_printf(MSG_INFO, "P2P: No social channels supported by the driver - do not enable P2P"); return 0; } p2p.channel_forced = 0; } wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d:%d", p2p.reg_class, p2p.channel); if (wpa_s->conf->p2p_oper_reg_class && wpa_s->conf->p2p_oper_channel) { p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class; p2p.op_channel = wpa_s->conf->p2p_oper_channel; p2p.cfg_op_channel = 1; wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: " "%d:%d", p2p.op_reg_class, p2p.op_channel); } else { /* * Use random operation channel from 2.4 GHz band social * channels (1, 6, 11) or band 60 GHz social channel (2) if no * other preference is indicated. */ if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class, &p2p.op_channel, NULL, NULL) != 0) { wpa_printf(MSG_INFO, "P2P: Failed to select random social channel as operation channel"); p2p.op_reg_class = 0; p2p.op_channel = 0; /* This will be overridden during group setup in * p2p_prepare_channel(), so allow setup to continue. */ } p2p.cfg_op_channel = 0; wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " "%d:%d", p2p.op_reg_class, p2p.op_channel); } if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) { p2p.pref_chan = wpa_s->conf->p2p_pref_chan; p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan; } if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) { os_memcpy(p2p.country, wpa_s->conf->country, 2); p2p.country[2] = 0x04; } else os_memcpy(p2p.country, "XX\x04", 3); os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types; os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type, p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN); p2p.concurrent_operations = !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT); p2p.max_peers = 100; if (wpa_s->conf->p2p_ssid_postfix) { p2p.ssid_postfix_len = os_strlen(wpa_s->conf->p2p_ssid_postfix); if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix)) p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix); os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix, p2p.ssid_postfix_len); } p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss; p2p.max_listen = wpa_s->max_remain_on_chan; if (wpa_s->conf->p2p_passphrase_len >= 8 && wpa_s->conf->p2p_passphrase_len <= 63) p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len; else p2p.passphrase_len = 8; global->p2p = p2p_init(&p2p); if (global->p2p == NULL) return -1; global->p2p_init_wpa_s = wpa_s; for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { if (wpa_s->conf->wps_vendor_ext[i] == NULL) continue; p2p_add_wps_vendor_extension( global->p2p, wpa_s->conf->wps_vendor_ext[i]); } p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq); return 0; } /** * wpas_p2p_deinit - Deinitialize per-interface P2P data * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * * This function deinitialize per-interface P2P data. */ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) { if (wpa_s->driver && wpa_s->drv_priv) wpa_drv_probe_req_report(wpa_s, 0); if (wpa_s->go_params) { /* Clear any stored provisioning info */ p2p_clear_provisioning_info( wpa_s->global->p2p, wpa_s->go_params->peer_device_addr); } os_free(wpa_s->go_params); wpa_s->go_params = NULL; eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); wpa_s->global->p2p_long_listen = 0; eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); wpas_p2p_remove_pending_group_interface(wpa_s); eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL); wpas_p2p_listen_work_done(wpa_s); if (wpa_s->p2p_send_action_work) { os_free(wpa_s->p2p_send_action_work->ctx); radio_work_done(wpa_s->p2p_send_action_work); wpa_s->p2p_send_action_work = NULL; } eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); wpabuf_free(wpa_s->p2p_oob_dev_pw); wpa_s->p2p_oob_dev_pw = NULL; os_free(wpa_s->p2p_group_common_freqs); wpa_s->p2p_group_common_freqs = NULL; wpa_s->p2p_group_common_freqs_num = 0; /* TODO: remove group interface from the driver if this wpa_s instance * is on top of a P2P group interface */ } /** * wpas_p2p_deinit_global - Deinitialize global P2P module * @global: Pointer to global data from wpa_supplicant_init() * * This function deinitializes the global (per device) P2P module. */ static void wpas_p2p_deinit_global(struct wpa_global *global) { struct wpa_supplicant *wpa_s, *tmp; wpa_s = global->ifaces; wpas_p2p_service_flush(global->p2p_init_wpa_s); /* Remove remaining P2P group interfaces */ while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) wpa_s = wpa_s->next; while (wpa_s) { tmp = global->ifaces; while (tmp && (tmp == wpa_s || tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) { tmp = tmp->next; } if (tmp == NULL) break; /* Disconnect from the P2P group and deinit the interface */ wpas_p2p_disconnect(tmp); } /* * Deinit GO data on any possibly remaining interface (if main * interface is used as GO). */ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_s->ap_iface) wpas_p2p_group_deinit(wpa_s); } p2p_deinit(global->p2p); global->p2p = NULL; global->p2p_init_wpa_s = NULL; } static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) { if (wpa_s->conf->p2p_no_group_iface) return 0; /* separate interface disabled per configuration */ if (wpa_s->drv_flags & (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P)) return 1; /* P2P group requires a new interface in every case */ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT)) return 0; /* driver does not support concurrent operations */ if (wpa_s->global->ifaces->next) return 1; /* more that one interface already in use */ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) return 1; /* this interface is already in use */ return 0; } static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, struct wpa_ssid *ssid, unsigned int pref_freq) { if (persistent_group && wpa_s->conf->persistent_reconnect) persistent_group = 2; /* * Increase GO config timeout if HT40 is used since it takes some time * to scan channels for coex purposes before the BSS can be started. */ p2p_set_config_timeout(wpa_s->global->p2p, wpa_s->p2p_go_ht40 ? 255 : 100, 20); return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method, go_intent, own_interface_addr, force_freq, persistent_group, ssid ? ssid->ssid : NULL, ssid ? ssid->ssid_len : 0, wpa_s->p2p_pd_before_go_neg, pref_freq, wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : 0); } static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, struct wpa_ssid *ssid, unsigned int pref_freq) { if (persistent_group && wpa_s->conf->persistent_reconnect) persistent_group = 2; return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, go_intent, own_interface_addr, force_freq, persistent_group, ssid ? ssid->ssid : NULL, ssid ? ssid->ssid_len : 0, pref_freq, wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : 0); } static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) { wpa_s->p2p_join_scan_count++; wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d", wpa_s->p2p_join_scan_count); if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) { wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR " for join operationg - stop join attempt", MAC2STR(wpa_s->pending_join_iface_addr)); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); if (wpa_s->p2p_auto_pd) { wpa_s->p2p_auto_pd = 0; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE " p2p_dev_addr=" MACSTR " status=N/A", MAC2STR(wpa_s->pending_join_dev_addr)); return; } if (wpa_s->p2p_fallback_to_go_neg) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Join operation failed - fall back to GO Negotiation"); wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=join-failed"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); return; } wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_notify_p2p_group_formation_failure(wpa_s, ""); } } static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq) { int res; unsigned int num, i; struct wpa_used_freq_data *freqs; if (wpas_p2p_num_unused_channels(wpa_s) > 0) { /* Multiple channels are supported and not all are in use */ return 0; } freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (!freqs) return 1; num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, wpa_s->num_multichan_concurrent); for (i = 0; i < num; i++) { if (freqs[i].freq == freq) { wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used", freq); res = 0; goto exit_free; } } wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies"); res = 1; exit_free: os_free(freqs); return res; } static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s, const u8 *peer_dev_addr) { struct wpa_bss *bss; int updated; bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr); if (bss == NULL) return -1; if (bss->last_update_idx < wpa_s->bss_update_idx) { wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the " "last scan"); return 0; } updated = os_reltime_before(&wpa_s->p2p_auto_started, &bss->last_update); wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at " "%ld.%06ld (%supdated in last scan)", bss->last_update.sec, bss->last_update.usec, updated ? "": "not "); return updated; } static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { struct wpa_bss *bss = NULL; int freq; u8 iface_addr[ETH_ALEN]; eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); if (wpa_s->global->p2p_disabled) return; wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin", scan_res ? (int) scan_res->num : -1, wpa_s->p2p_auto_join ? "auto_" : ""); if (scan_res) wpas_p2p_scan_res_handler(wpa_s, scan_res); if (wpa_s->p2p_auto_pd) { int join = wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr); if (join == 0 && wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) { wpa_s->auto_pd_scan_retry++; bss = wpa_bss_get_bssid_latest( wpa_s, wpa_s->pending_join_dev_addr); if (bss) { freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for " "the peer " MACSTR " at %d MHz", wpa_s->auto_pd_scan_retry, MAC2STR(wpa_s-> pending_join_dev_addr), freq); wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0); return; } } if (join < 0) join = 0; wpa_s->p2p_auto_pd = 0; wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG; wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d", MAC2STR(wpa_s->pending_join_dev_addr), join); if (p2p_prov_disc_req(wpa_s->global->p2p, wpa_s->pending_join_dev_addr, NULL, wpa_s->pending_pd_config_methods, join, 0, wpa_s->user_initiated_pd) < 0) { wpa_s->p2p_auto_pd = 0; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE " p2p_dev_addr=" MACSTR " status=N/A", MAC2STR(wpa_s->pending_join_dev_addr)); } return; } if (wpa_s->p2p_auto_join) { int join = wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr); if (join < 0) { wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " "running a GO -> use GO Negotiation"); wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=peer-not-running-GO"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0); + NULL, 0, + is_p2p_allow_6ghz(wpa_s->global->p2p)); return; } wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> " "try to join the group", join ? "" : " in older scan"); if (!join) { wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED); wpa_s->p2p_fallback_to_go_neg = 1; } } freq = p2p_get_oper_freq(wpa_s->global->p2p, wpa_s->pending_join_iface_addr); if (freq < 0 && p2p_get_interface_addr(wpa_s->global->p2p, wpa_s->pending_join_dev_addr, iface_addr) == 0 && os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0 && !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) { wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface " "address for join from " MACSTR " to " MACSTR " based on newly discovered P2P peer entry", MAC2STR(wpa_s->pending_join_iface_addr), MAC2STR(iface_addr)); os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN); freq = p2p_get_oper_freq(wpa_s->global->p2p, wpa_s->pending_join_iface_addr); } if (freq >= 0) { wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " "from P2P peer table: %d MHz", freq); } if (wpa_s->p2p_join_ssid_len) { wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " MACSTR " and SSID %s", MAC2STR(wpa_s->pending_join_iface_addr), wpa_ssid_txt(wpa_s->p2p_join_ssid, wpa_s->p2p_join_ssid_len)); bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, wpa_s->p2p_join_ssid, wpa_s->p2p_join_ssid_len); } else if (!bss) { wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " MACSTR, MAC2STR(wpa_s->pending_join_iface_addr)); bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr); } if (bss) { u8 dev_addr[ETH_ALEN]; freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " "from BSS table: %d MHz (SSID %s)", freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len, dev_addr) == 0 && os_memcmp(wpa_s->pending_join_dev_addr, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 && os_memcmp(dev_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")", MAC2STR(dev_addr), MAC2STR(wpa_s->pending_join_dev_addr)); os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN); } } if (freq > 0) { u16 method; if (wpas_check_freq_conflict(wpa_s, freq) > 0) { wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE "reason=FREQ_CONFLICT"); wpas_notify_p2p_group_formation_failure( wpa_s, "FREQ_CONFLICT"); return; } wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request " "prior to joining an existing group (GO " MACSTR " freq=%u MHz)", MAC2STR(wpa_s->pending_join_dev_addr), freq); wpa_s->pending_pd_before_join = 1; switch (wpa_s->pending_join_wps_method) { case WPS_PIN_DISPLAY: method = WPS_CONFIG_KEYPAD; break; case WPS_PIN_KEYPAD: method = WPS_CONFIG_DISPLAY; break; case WPS_PBC: method = WPS_CONFIG_PUSHBUTTON; break; case WPS_P2PS: method = WPS_CONFIG_P2PS; break; default: method = 0; break; } if ((p2p_get_provisioning_info(wpa_s->global->p2p, wpa_s->pending_join_dev_addr) == method)) { /* * We have already performed provision discovery for * joining the group. Proceed directly to join * operation without duplicated provision discovery. */ wpa_printf(MSG_DEBUG, "P2P: Provision discovery " "with " MACSTR " already done - proceed to " "join", MAC2STR(wpa_s->pending_join_dev_addr)); wpa_s->pending_pd_before_join = 0; goto start; } if (p2p_prov_disc_req(wpa_s->global->p2p, wpa_s->pending_join_dev_addr, NULL, method, 1, freq, wpa_s->user_initiated_pd) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " "Discovery Request before joining an " "existing group"); wpa_s->pending_pd_before_join = 0; goto start; } return; } wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later"); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); wpas_p2p_check_join_scan_limit(wpa_s); return; start: /* Start join operation immediately */ wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid, wpa_s->p2p_join_ssid_len); } static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len) { int ret; struct wpa_driver_scan_params params; struct wpabuf *wps_ie, *ies; size_t ielen; int freqs[2] = { 0, 0 }; unsigned int bands; os_memset(¶ms, 0, sizeof(params)); /* P2P Wildcard SSID */ params.num_ssids = 1; if (ssid && ssid_len) { params.ssids[0].ssid = ssid; params.ssids[0].ssid_len = ssid_len; os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len); wpa_s->p2p_join_ssid_len = ssid_len; } else { params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; wpa_s->p2p_join_ssid_len = 0; } wpa_s->wps->dev.p2p = 1; wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev, wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0, NULL); if (wps_ie == NULL) { wpas_p2p_scan_res_join(wpa_s, NULL); return; } if (!freq) { int oper_freq; /* * If freq is not provided, check the operating freq of the GO * and use a single channel scan on if possible. */ oper_freq = p2p_get_oper_freq(wpa_s->global->p2p, wpa_s->pending_join_iface_addr); if (oper_freq > 0) freq = oper_freq; } if (freq > 0) { freqs[0] = freq; params.freqs = freqs; - } else if (wpa_s->conf->p2p_6ghz_disable) { + } else if (wpa_s->conf->p2p_6ghz_disable || + !is_p2p_allow_6ghz(wpa_s->global->p2p)) { wpa_printf(MSG_DEBUG, "P2P: 6 GHz disabled - update the scan frequency list"); wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms, 0); wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, 0); } ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); if (ies == NULL) { wpabuf_free(wps_ie); wpas_p2p_scan_res_join(wpa_s, NULL); return; } wpabuf_put_buf(ies, wps_ie); wpabuf_free(wps_ie); bands = wpas_get_bands(wpa_s, freqs); p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); params.p2p_probe = 1; params.extra_ies = wpabuf_head(ies); params.extra_ies_len = wpabuf_len(ies); if (wpa_s->clear_driver_scan_cache) { wpa_printf(MSG_DEBUG, "Request driver to clear scan cache due to local BSS flush"); params.only_new_results = 1; } /* * Run a scan to update BSS table and start Provision Discovery once * the new scan results become available. */ ret = wpa_drv_scan(wpa_s, ¶ms); - if (wpa_s->conf->p2p_6ghz_disable && params.freqs != freqs) + if (params.freqs != freqs) os_free(params.freqs); if (!ret) { os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_res_handler = wpas_p2p_scan_res_join; wpa_s->own_scan_requested = 1; wpa_s->clear_driver_scan_cache = 0; } wpabuf_free(ies); if (ret) { wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - " "try again later"); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); wpas_p2p_check_join_scan_limit(wpa_s); } } static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0); } static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, const u8 *dev_addr, enum p2p_wps_method wps_method, int auto_join, int op_freq, const u8 *ssid, size_t ssid_len) { wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface " MACSTR " dev " MACSTR " op_freq=%d)%s", MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq, auto_join ? " (auto_join)" : ""); if (ssid && ssid_len) { wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s", wpa_ssid_txt(ssid, ssid_len)); } wpa_s->p2p_auto_pd = 0; wpa_s->p2p_auto_join = !!auto_join; os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN); os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN); wpa_s->pending_join_wps_method = wps_method; /* Make sure we are not running find during connection establishment */ wpas_p2p_stop_find(wpa_s); wpa_s->p2p_join_scan_count = 0; wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len); return 0; } static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len) { struct wpa_supplicant *group; struct p2p_go_neg_results res; struct wpa_bss *bss; group = wpas_p2p_get_group_iface(wpa_s, 0, 0); if (group == NULL) return -1; if (group != wpa_s) { os_memcpy(group->p2p_pin, wpa_s->p2p_pin, sizeof(group->p2p_pin)); group->p2p_wps_method = wpa_s->p2p_wps_method; } /* * Need to mark the current interface for p2p_group_formation * when a separate group interface is not used. This is needed * to allow p2p_cancel stop a pending p2p_connect-join. * wpas_p2p_init_group_interface() addresses this for the case * where a separate group interface is used. */ if (group == wpa_s->parent) wpa_s->global->p2p_group_formation = group; group->p2p_in_provisioning = 1; group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg; os_memset(&res, 0, sizeof(res)); os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, ETH_ALEN); res.wps_method = wpa_s->pending_join_wps_method; if (freq && ssid && ssid_len) { res.freq = freq; res.ssid_len = ssid_len; os_memcpy(res.ssid, ssid, ssid_len); } else { if (ssid && ssid_len) { bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, ssid, ssid_len); } else { bss = wpa_bss_get_bssid_latest( wpa_s, wpa_s->pending_join_iface_addr); } if (bss) { res.freq = bss->freq; res.ssid_len = bss->ssid_len; os_memcpy(res.ssid, bss->ssid, bss->ssid_len); wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)", bss->freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); } else if (ssid && ssid_len) { res.ssid_len = ssid_len; os_memcpy(res.ssid, ssid, ssid_len); wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)", wpa_ssid_txt(ssid, ssid_len)); } } if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to " "starting client"); wpa_drv_cancel_remain_on_channel(wpa_s); wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; } wpas_start_wps_enrollee(group, &res); /* * Allow a longer timeout for join-a-running-group than normal 15 * second group formation timeout since the GO may not have authorized * our connection yet. */ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout, wpa_s, NULL); return 0; } static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, int *force_freq, int *pref_freq, int go, unsigned int *pref_freq_list, unsigned int *num_pref_freq) { struct wpa_used_freq_data *freqs; int res, best_freq, num_unused; unsigned int freq_in_use = 0, num, i, max_pref_freq; max_pref_freq = *num_pref_freq; *num_pref_freq = 0; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (!freqs) return -1; num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, wpa_s->num_multichan_concurrent); /* * It is possible that the total number of used frequencies is bigger * than the number of frequencies used for P2P, so get the system wide * number of unused frequencies. */ num_unused = wpas_p2p_num_unused_channels(wpa_s); wpa_printf(MSG_DEBUG, "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d", freq, wpa_s->num_multichan_concurrent, num, num_unused); if (freq > 0) { int ret; if (go) ret = p2p_supported_freq(wpa_s->global->p2p, freq); else ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq); if (!ret) { if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes)) { /* * If freq is a DFS channel and DFS is offloaded * to the driver, allow P2P GO to use it. */ wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver", freq); } else { wpa_printf(MSG_DEBUG, "P2P: The forced channel (%u MHz) is not supported for P2P uses", freq); res = -3; goto exit_free; } } for (i = 0; i < num; i++) { if (freqs[i].freq == freq) freq_in_use = 1; } if (num_unused <= 0 && !freq_in_use) { wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels", freq); res = -2; goto exit_free; } wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " "requested channel (%u MHz)", freq); *force_freq = freq; goto exit_ok; } best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) { enum wpa_driver_if_type iface_type; if (go) iface_type = WPA_IF_P2P_GO; else iface_type = WPA_IF_P2P_CLIENT; wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d", best_freq, go); res = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &max_pref_freq, pref_freq_list); + if (!is_p2p_allow_6ghz(wpa_s->global->p2p)) + max_pref_freq = p2p_remove_6ghz_channels(pref_freq_list, + max_pref_freq); + if (!res && max_pref_freq > 0) { *num_pref_freq = max_pref_freq; i = 0; while (i < *num_pref_freq && (!p2p_supported_freq(wpa_s->global->p2p, pref_freq_list[i]) || wpas_p2p_disallowed_freq(wpa_s->global, pref_freq_list[i]))) { wpa_printf(MSG_DEBUG, "P2P: preferred_freq_list[%d]=%d is disallowed", i, pref_freq_list[i]); i++; } if (i != *num_pref_freq) { best_freq = pref_freq_list[i]; wpa_printf(MSG_DEBUG, "P2P: Using preferred_freq_list[%d]=%d", i, best_freq); } else { wpa_printf(MSG_DEBUG, "P2P: All driver preferred frequencies are disallowed for P2P use"); *num_pref_freq = 0; } } else { wpa_printf(MSG_DEBUG, "P2P: No preferred frequency list available"); } } /* We have a candidate frequency to use */ if (best_freq > 0) { if (*pref_freq == 0 && num_unused > 0) { wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using", best_freq); *pref_freq = best_freq; } else { wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use", best_freq); *force_freq = best_freq; } } else if (num_unused > 0) { wpa_printf(MSG_DEBUG, "P2P: Current operating channels are not available for P2P. Try to use another channel"); *force_freq = 0; } else { wpa_printf(MSG_DEBUG, "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group"); res = -2; goto exit_free; } exit_ok: res = 0; exit_free: os_free(freqs); return res; } +static bool is_p2p_6ghz_supported(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + if (wpa_s->conf->p2p_6ghz_disable || + !get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, + HOSTAPD_MODE_IEEE80211A, true)) + return false; + + if (!p2p_wfd_enabled(wpa_s->global->p2p)) + return false; + if (peer_addr && !p2p_peer_wfd_enabled(wpa_s->global->p2p, peer_addr)) + return false; + + return true; +} + + +static int wpas_p2p_check_6ghz(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, bool allow_6ghz, int freq) +{ + if (allow_6ghz && is_p2p_6ghz_supported(wpa_s, peer_addr)) { + wpa_printf(MSG_DEBUG, + "P2P: Allow connection on 6 GHz channels"); + p2p_set_6ghz_dev_capab(wpa_s->global->p2p, true); + } else { + if (is_6ghz_freq(freq)) + return -2; + p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false); + } + + return 0; +} + + /** * wpas_p2p_connect - Request P2P Group Formation to be started * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @peer_addr: Address of the peer P2P Device * @pin: PIN to use during provisioning or %NULL to indicate PBC mode * @persistent_group: Whether to create a persistent group * @auto_join: Whether to select join vs. GO Negotiation automatically * @join: Whether to join an existing group (as a client) instead of starting * Group Owner negotiation; @peer_addr is BSSID in that case * @auth: Whether to only authorize the connection instead of doing that and * initiating Group Owner negotiation * @go_intent: GO Intent or -1 to use default * @freq: Frequency for the group or 0 for auto-selection * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode * @persistent_id: Persistent group credentials to use for forcing GO * parameters or -1 to generate new values (SSID/passphrase) * @pd: Whether to send Provision Discovery prior to GO Negotiation as an * interoperability workaround when initiating group formation * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support * @vht_chwidth: Channel width supported by GO operating with VHT support * (CHANWIDTH_*). * @group_ssid: Specific Group SSID for join or %NULL if not set * @group_ssid_len: Length of @group_ssid in octets + * @allow_6ghz: Allow P2P connection on 6 GHz channels * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified * failure, -2 on failure due to channel not currently available, * -3 if forced channel is not supported */ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, int auth, int go_intent, int freq, unsigned int vht_center_freq2, int persistent_id, int pd, int ht40, int vht, unsigned int vht_chwidth, int he, int edmg, - const u8 *group_ssid, size_t group_ssid_len) + const u8 *group_ssid, size_t group_ssid_len, + bool allow_6ghz) { int force_freq = 0, pref_freq = 0; int ret = 0, res; enum wpa_driver_if_type iftype; const u8 *if_addr; struct wpa_ssid *ssid = NULL; unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; if (persistent_id >= 0) { ssid = wpa_config_get_network(wpa_s->conf, persistent_id); if (ssid == NULL || ssid->disabled != 2 || ssid->mode != WPAS_MODE_P2P_GO) return -1; } - if (is_6ghz_freq(freq) && wpa_s->conf->p2p_6ghz_disable) + if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq)) return -2; os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; wpa_s->global->p2p_fail_on_wps_complete = 0; wpa_s->global->pending_p2ps_group = 0; wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->p2ps_method_config_any = 0; if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; if (!auth) wpa_s->global->p2p_long_listen = 0; wpa_s->p2p_wps_method = wps_method; wpa_s->p2p_persistent_group = !!persistent_group; wpa_s->p2p_persistent_id = persistent_id; wpa_s->p2p_go_intent = go_intent; wpa_s->p2p_connect_freq = freq; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->p2p_pd_before_go_neg = !!pd; wpa_s->p2p_go_ht40 = !!ht40; wpa_s->p2p_go_vht = !!vht; wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; wpa_s->p2p_go_max_oper_chwidth = vht_chwidth; wpa_s->p2p_go_he = !!he; wpa_s->p2p_go_edmg = !!edmg; if (pin) os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); else if (wps_method == WPS_PIN_DISPLAY) { if (wps_generate_pin((unsigned int *) &ret) < 0) return -1; res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d", ret); if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res)) wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", wpa_s->p2p_pin); } else if (wps_method == WPS_P2PS) { /* Force the P2Ps default PIN to be used */ os_strlcpy(wpa_s->p2p_pin, "12345670", sizeof(wpa_s->p2p_pin)); } else wpa_s->p2p_pin[0] = '\0'; if (join || auto_join) { u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN]; if (auth) { wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to " "connect a running group from " MACSTR, MAC2STR(peer_addr)); os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); return ret; } os_memcpy(dev_addr, peer_addr, ETH_ALEN); if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr, iface_addr) < 0) { os_memcpy(iface_addr, peer_addr, ETH_ALEN); p2p_get_dev_addr(wpa_s->global->p2p, peer_addr, dev_addr); } if (auto_join) { os_get_reltime(&wpa_s->p2p_auto_started); wpa_printf(MSG_DEBUG, "P2P: Auto join started at " "%ld.%06ld", wpa_s->p2p_auto_started.sec, wpa_s->p2p_auto_started.usec); } wpa_s->user_initiated_pd = 1; if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, auto_join, freq, group_ssid, group_ssid_len) < 0) return -1; return ret; } size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, go_intent == 15, pref_freq_list, &size); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq ? force_freq : pref_freq); p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); if (wpa_s->create_p2p_iface) { /* Prepare to add a new interface for the group */ iftype = WPA_IF_P2P_GROUP; if (go_intent == 15) iftype = WPA_IF_P2P_GO; if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " "interface for the group"); return -1; } if_addr = wpa_s->pending_interface_addr; } else { if (wpa_s->p2p_mgmt) if_addr = wpa_s->parent->own_addr; else if_addr = wpa_s->own_addr; os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); } if (auth) { if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, go_intent, if_addr, force_freq, persistent_group, ssid, pref_freq) < 0) return -1; return ret; } if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent, if_addr, force_freq, persistent_group, ssid, pref_freq) < 0) { if (wpa_s->create_p2p_iface) wpas_p2p_remove_pending_group_interface(wpa_s); return -1; } return ret; } /** * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @freq: Frequency of the channel in MHz * @duration: Duration of the stay on the channel in milliseconds * * This callback is called when the driver indicates that it has started the * requested remain-on-channel duration. */ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int duration) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)", wpa_s->off_channel_freq, wpa_s->pending_listen_freq, wpa_s->roc_waiting_drv_freq, freq, duration); if (wpa_s->off_channel_freq && wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq, wpa_s->pending_listen_duration); wpa_s->pending_listen_freq = 0; } else { wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)", wpa_s->off_channel_freq, wpa_s->pending_listen_freq, freq, duration); } } int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout) { /* Limit maximum Listen state time based on driver limitation. */ if (timeout > wpa_s->max_remain_on_chan) timeout = wpa_s->max_remain_on_chan; return p2p_listen(wpa_s->global->p2p, timeout); } /** * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @freq: Frequency of the channel in MHz * * This callback is called when the driver indicates that a remain-on-channel * operation has been completed, i.e., the duration on the requested channel * has timed out. */ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq) { wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " "(p2p_long_listen=%d ms pending_action_tx=%p)", wpa_s->global->p2p_long_listen, offchannel_pending_action_tx(wpa_s)); wpas_p2p_listen_work_done(wpa_s); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; if (wpa_s->global->p2p_long_listen > 0) wpa_s->global->p2p_long_listen -= wpa_s->max_remain_on_chan; if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) return; /* P2P module started a new operation */ if (offchannel_pending_action_tx(wpa_s)) return; if (wpa_s->global->p2p_long_listen > 0) { wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); wpas_p2p_listen_start(wpa_s, wpa_s->global->p2p_long_listen); } else { /* * When listen duration is over, stop listen & update p2p_state * to IDLE. */ p2p_stop_listen(wpa_s->global->p2p); } } /** * wpas_p2p_group_remove - Remove a P2P group * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @ifname: Network interface name of the group interface or "*" to remove all * groups * Returns: 0 on success, -1 on failure * * This function is used to remove a P2P group. This can be used to disconnect * from a group in which the local end is a P2P Client or to end a P2P Group in * case the local end is the Group Owner. If a virtual network interface was * created for this group, that interface will be removed. Otherwise, only the * configured P2P group network will be removed from the interface. */ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) { struct wpa_global *global = wpa_s->global; struct wpa_supplicant *calling_wpa_s = wpa_s; if (os_strcmp(ifname, "*") == 0) { struct wpa_supplicant *prev; bool calling_wpa_s_group_removed = false; wpa_s = global->ifaces; while (wpa_s) { prev = wpa_s; wpa_s = wpa_s->next; if (prev->p2p_group_interface != NOT_P2P_GROUP_INTERFACE || (prev->current_ssid && prev->current_ssid->p2p_group)) { wpas_p2p_disconnect_safely(prev, calling_wpa_s); if (prev == calling_wpa_s) calling_wpa_s_group_removed = true; } } if (!calling_wpa_s_group_removed && (calling_wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE || (calling_wpa_s->current_ssid && calling_wpa_s->current_ssid->p2p_group))) { wpa_printf(MSG_DEBUG, "Remove calling_wpa_s P2P group"); wpas_p2p_disconnect_safely(calling_wpa_s, calling_wpa_s); } return 0; } for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) break; } return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s); } static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) { unsigned int r; if (!wpa_s->conf->num_p2p_pref_chan && !freq) { unsigned int i, size = P2P_MAX_PREF_CHANNELS; unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; int res; res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, &size, pref_freq_list); + if (!is_p2p_allow_6ghz(wpa_s->global->p2p)) + size = p2p_remove_6ghz_channels(pref_freq_list, size); + if (!res && size > 0) { i = 0; while (i < size && (!p2p_supported_freq(wpa_s->global->p2p, pref_freq_list[i]) || wpas_p2p_disallowed_freq(wpa_s->global, pref_freq_list[i]))) { wpa_printf(MSG_DEBUG, "P2P: preferred_freq_list[%d]=%d is disallowed", i, pref_freq_list[i]); i++; } if (i != size) { freq = pref_freq_list[i]; wpa_printf(MSG_DEBUG, "P2P: Using preferred_freq_list[%d]=%d", i, freq); } else { wpa_printf(MSG_DEBUG, "P2P: All driver preferred frequencies are disallowed for P2P use"); } } else { wpa_printf(MSG_DEBUG, "P2P: No preferred frequency list available"); } } if (freq == 2) { wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " "band"); if (wpa_s->best_24_freq > 0 && p2p_supported_freq_go(wpa_s->global->p2p, wpa_s->best_24_freq)) { freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " "channel: %d MHz", freq); } else { if (os_get_random((u8 *) &r, sizeof(r)) < 0) return -1; freq = 2412 + (r % 3) * 25; wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band " "channel: %d MHz", freq); } } if (freq == 5) { wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz " "band"); if (wpa_s->best_5_freq > 0 && p2p_supported_freq_go(wpa_s->global->p2p, wpa_s->best_5_freq)) { freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " "channel: %d MHz", freq); } else { const int freqs[] = { /* operating class 115 */ 5180, 5200, 5220, 5240, /* operating class 124 */ 5745, 5765, 5785, 5805, }; unsigned int i, num_freqs = ARRAY_SIZE(freqs); if (os_get_random((u8 *) &r, sizeof(r)) < 0) return -1; /* * Most of the 5 GHz channels require DFS. Only * operating classes 115 and 124 are available possibly * without that requirement. Check these for * availability starting from a randomly picked * position. */ for (i = 0; i < num_freqs; i++, r++) { freq = freqs[r % num_freqs]; if (p2p_supported_freq_go(wpa_s->global->p2p, freq)) break; } if (i >= num_freqs) { wpa_printf(MSG_DEBUG, "P2P: Could not select " "5 GHz channel for P2P group"); return -1; } wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band " "channel: %d MHz", freq); } } if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) { if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes)) { /* * If freq is a DFS channel and DFS is offloaded to the * driver, allow P2P GO to use it. */ wpa_printf(MSG_DEBUG, "P2P: " "%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded", __func__, freq); return freq; } wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " "(%u MHz) is not supported for P2P uses", freq); return -1; } return freq; } static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels, int freq) { if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) && p2p_supported_freq_go(wpa_s->global->p2p, freq) && freq_included(wpa_s, channels, freq)) return 1; return 0; } static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, const struct p2p_channels *channels) { unsigned int i, r; /* try all channels in operating class 115 */ for (i = 0; i < 4; i++) { params->freq = 5180 + i * 20; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } /* try all channels in operating class 124 */ for (i = 0; i < 4; i++) { params->freq = 5745 + i * 20; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } /* try social channel class 180 channel 2 */ params->freq = 58320 + 1 * 2160; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; /* try all channels in reg. class 180 */ for (i = 0; i < 4; i++) { params->freq = 58320 + i * 2160; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } /* try some random selection of the social channels */ if (os_get_random((u8 *) &r, sizeof(r)) < 0) return; for (i = 0; i < 3; i++) { params->freq = 2412 + ((r + i) % 3) * 25; if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } /* try all other channels in operating class 81 */ for (i = 0; i < 11; i++) { params->freq = 2412 + i * 5; /* skip social channels; covered in the previous loop */ if (params->freq == 2412 || params->freq == 2437 || params->freq == 2462) continue; if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } params->freq = 0; wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed"); return; out: wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)", params->freq); } static int wpas_same_band(int freq1, int freq2) { enum hostapd_hw_mode mode1, mode2; u8 chan1, chan2; mode1 = ieee80211_freq_to_chan(freq1, &chan1); mode2 = ieee80211_freq_to_chan(freq2, &chan2); if (mode1 == NUM_HOSTAPD_MODES) return 0; return mode1 == mode2; } static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, int freq, int vht_center_freq2, int ht40, int vht, int max_oper_chwidth, int he, int edmg, const struct p2p_channels *channels) { struct wpa_used_freq_data *freqs; unsigned int cand; unsigned int num, i; int ignore_no_freqs = 0; int unused_channels = wpas_p2p_num_unused_channels(wpa_s) > 0; os_memset(params, 0, sizeof(*params)); params->role_go = 1; params->ht40 = ht40; params->vht = vht; params->he = he; params->max_oper_chwidth = max_oper_chwidth; params->vht_center_freq2 = vht_center_freq2; params->edmg = edmg; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (!freqs) return -1; num = get_shared_radio_freqs_data(wpa_s, freqs, wpa_s->num_multichan_concurrent); if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO && wpa_s->wpa_state == WPA_COMPLETED) { wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO", __func__); /* * If the frequency selection is done for an active P2P GO that * is not sharing a frequency, allow to select a new frequency * even if there are no unused frequencies as we are about to * move the P2P GO so its frequency can be re-used. */ for (i = 0; i < num; i++) { if (freqs[i].freq == wpa_s->current_ssid->frequency && freqs[i].flags == 0) { ignore_no_freqs = 1; break; } } } /* Try to use EDMG channel */ if (params->edmg) { if (wpas_p2p_try_edmg_channel(wpa_s, params) == 0) goto success; params->edmg = 0; } /* try using the forced freq */ if (freq) { if (wpas_p2p_disallowed_freq(wpa_s->global, freq) || !freq_included(wpa_s, channels, freq)) { wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz disallowed", freq); goto fail; } if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes)) { /* * If freq is a DFS channel and DFS is offloaded * to the driver, allow P2P GO to use it. */ wpa_printf(MSG_DEBUG, "P2P: %s: The forced channel for GO (%u MHz) requires DFS and DFS is offloaded", __func__, freq); } else { wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO (%u MHz) is not supported for P2P uses", freq); goto fail; } } for (i = 0; i < num; i++) { if (freqs[i].freq == freq) { wpa_printf(MSG_DEBUG, "P2P: forced freq (%d MHz) is also shared", freq); params->freq = freq; goto success; } } if (!ignore_no_freqs && !unused_channels) { wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use", freq); goto fail; } wpa_printf(MSG_DEBUG, "P2P: force GO freq (%d MHz) on a free channel", freq); params->freq = freq; goto success; } /* consider using one of the shared frequencies */ if (num && (!wpa_s->conf->p2p_ignore_shared_freq || !unused_channels)) { cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { wpa_printf(MSG_DEBUG, "P2P: Use shared freq (%d MHz) for GO", cand); params->freq = cand; goto success; } /* try using one of the shared freqs */ for (i = 0; i < num; i++) { if (wpas_p2p_supported_freq_go(wpa_s, channels, freqs[i].freq)) { wpa_printf(MSG_DEBUG, "P2P: Use shared freq (%d MHz) for GO", freqs[i].freq); params->freq = freqs[i].freq; goto success; } } } if (!ignore_no_freqs && !unused_channels) { wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); goto fail; } /* try using the setting from the configuration file */ if (wpa_s->conf->p2p_oper_reg_class == 81 && wpa_s->conf->p2p_oper_channel >= 1 && wpa_s->conf->p2p_oper_channel <= 11 && wpas_p2p_supported_freq_go( wpa_s, channels, 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " "frequency %d MHz", params->freq); goto success; } if ((wpa_s->conf->p2p_oper_reg_class == 115 || wpa_s->conf->p2p_oper_reg_class == 116 || wpa_s->conf->p2p_oper_reg_class == 117 || wpa_s->conf->p2p_oper_reg_class == 124 || wpa_s->conf->p2p_oper_reg_class == 125 || wpa_s->conf->p2p_oper_reg_class == 126 || wpa_s->conf->p2p_oper_reg_class == 127) && wpas_p2p_supported_freq_go(wpa_s, channels, 5000 + 5 * wpa_s->conf->p2p_oper_channel)) { params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " "frequency %d MHz", params->freq); goto success; } /* Try using best channels */ if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_overall_freq > 0 && wpas_p2p_supported_freq_go(wpa_s, channels, wpa_s->best_overall_freq)) { params->freq = wpa_s->best_overall_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " "channel %d MHz", params->freq); goto success; } if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_24_freq > 0 && wpas_p2p_supported_freq_go(wpa_s, channels, wpa_s->best_24_freq)) { params->freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " "channel %d MHz", params->freq); goto success; } if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_5_freq > 0 && wpas_p2p_supported_freq_go(wpa_s, channels, wpa_s->best_5_freq)) { params->freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " "channel %d MHz", params->freq); goto success; } /* try using preferred channels */ cand = p2p_get_pref_freq(wpa_s->global->p2p, channels); if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { params->freq = cand; wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " "channels", params->freq); goto success; } /* Try using a channel that allows VHT to be used with 80 MHz */ if (wpa_s->hw.modes && wpa_s->p2p_group_common_freqs) { for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { enum hostapd_hw_mode mode; struct hostapd_hw_modes *hwmode; u8 chan; + u8 op_class; cand = wpa_s->p2p_group_common_freqs[i]; + op_class = is_6ghz_freq(cand) ? 133 : 128; mode = ieee80211_freq_to_chan(cand, &chan); hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, mode, is_6ghz_freq(cand)); if (!hwmode || - wpas_p2p_verify_channel(wpa_s, hwmode, chan, - BW80) != ALLOWED) + wpas_p2p_verify_channel(wpa_s, hwmode, op_class, + chan, BW80) != ALLOWED) continue; if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { params->freq = cand; wpa_printf(MSG_DEBUG, "P2P: Use freq %d MHz common with the peer and allowing VHT80", params->freq); goto success; } } } /* Try using a channel that allows HT to be used with 40 MHz on the same * band so that CSA can be used */ if (wpa_s->current_ssid && wpa_s->hw.modes && wpa_s->p2p_group_common_freqs) { for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { enum hostapd_hw_mode mode; struct hostapd_hw_modes *hwmode; - u8 chan; + u8 chan, op_class; + bool is_6ghz, supported = false; + is_6ghz = is_6ghz_freq(cand); cand = wpa_s->p2p_group_common_freqs[i]; mode = ieee80211_freq_to_chan(cand, &chan); hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, - mode, is_6ghz_freq(cand)); + mode, is_6ghz); if (!wpas_same_band(wpa_s->current_ssid->frequency, cand) || - !hwmode || - (wpas_p2p_verify_channel(wpa_s, hwmode, chan, - BW40MINUS) != ALLOWED && - wpas_p2p_verify_channel(wpa_s, hwmode, chan, - BW40PLUS) != ALLOWED)) + !hwmode) + continue; + if (is_6ghz && + wpas_p2p_verify_channel(wpa_s, hwmode, 132, chan, + BW40) == ALLOWED) + supported = true; + + if (!is_6ghz && + ieee80211_freq_to_channel_ext( + cand, -1, CHANWIDTH_USE_HT, &op_class, + &chan) != NUM_HOSTAPD_MODES && + wpas_p2p_verify_channel( + wpa_s, hwmode, op_class, chan, + BW40MINUS) == ALLOWED) + supported = true; + + if (!supported && !is_6ghz && + ieee80211_freq_to_channel_ext( + cand, 1, CHANWIDTH_USE_HT, &op_class, + &chan) != NUM_HOSTAPD_MODES && + wpas_p2p_verify_channel( + wpa_s, hwmode, op_class, chan, + BW40PLUS) == ALLOWED) + supported = true; + + if (!supported) continue; + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { params->freq = cand; wpa_printf(MSG_DEBUG, "P2P: Use freq %d MHz common with the peer, allowing HT40, and maintaining same band", params->freq); goto success; } } } /* Try using one of the group common freqs on the same band so that CSA * can be used */ if (wpa_s->current_ssid && wpa_s->p2p_group_common_freqs) { for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { cand = wpa_s->p2p_group_common_freqs[i]; if (!wpas_same_band(wpa_s->current_ssid->frequency, cand)) continue; if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { params->freq = cand; wpa_printf(MSG_DEBUG, "P2P: Use freq %d MHz common with the peer and maintaining same band", params->freq); goto success; } } } /* Try using one of the group common freqs */ if (wpa_s->p2p_group_common_freqs) { for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { cand = wpa_s->p2p_group_common_freqs[i]; if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { params->freq = cand; wpa_printf(MSG_DEBUG, "P2P: Use freq %d MHz common with the peer", params->freq); goto success; } } } /* no preference, select some channel */ wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels); if (params->freq == 0) { wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use"); goto fail; } success: os_free(freqs); return 0; fail: os_free(freqs); return -1; } static struct wpa_supplicant * wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, int go) { struct wpa_supplicant *group_wpa_s; if (!wpas_p2p_create_iface(wpa_s)) { if (wpa_s->p2p_mgmt) { /* * We may be called on the p2p_dev interface which * cannot be used for group operations, so always use * the primary interface. */ wpa_s->parent->p2pdev = wpa_s; wpa_s = wpa_s->parent; } wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use primary interface for group operations"); wpa_s->p2p_first_connection_timeout = 0; if (wpa_s != wpa_s->p2pdev) wpas_p2p_clone_config(wpa_s, wpa_s->p2pdev); return wpa_s; } if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : WPA_IF_P2P_CLIENT) < 0) { wpa_msg_global(wpa_s, MSG_ERROR, "P2P: Failed to add group interface"); return NULL; } group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go); if (group_wpa_s == NULL) { wpa_msg_global(wpa_s, MSG_ERROR, "P2P: Failed to initialize group interface"); wpas_p2p_remove_pending_group_interface(wpa_s); return NULL; } if (go && wpa_s->p2p_go_do_acs) { group_wpa_s->p2p_go_do_acs = wpa_s->p2p_go_do_acs; group_wpa_s->p2p_go_acs_band = wpa_s->p2p_go_acs_band; wpa_s->p2p_go_do_acs = 0; } wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s", group_wpa_s->ifname); group_wpa_s->p2p_first_connection_timeout = 0; return group_wpa_s; } /** * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @persistent_group: Whether to create a persistent group * @freq: Frequency for the group or 0 to indicate no hardcoding * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80 * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support * @vht_chwidth: channel bandwidth for GO operating with VHT support * @edmg: Start GO with EDMG support + * @allow_6ghz: Allow P2P group creation on a 6 GHz channel * Returns: 0 on success, -1 on failure * * This function creates a new P2P group with the local end as the Group Owner, * i.e., without using Group Owner Negotiation. */ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int vht_center_freq2, int ht40, int vht, - int max_oper_chwidth, int he, int edmg) + int max_oper_chwidth, int he, int edmg, + bool allow_6ghz) { struct p2p_go_neg_results params; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq)) + return -1; os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; /* Make sure we are not running find during connection establishment */ wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); wpas_p2p_stop_find_oper(wpa_s); if (!wpa_s->p2p_go_do_acs) { freq = wpas_p2p_select_go_freq(wpa_s, freq); if (freq < 0) return -1; } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, ht40, vht, max_oper_chwidth, he, edmg, NULL)) return -1; p2p_go_params(wpa_s->global->p2p, ¶ms); params.persistent_group = persistent_group; wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1); if (wpa_s == NULL) return -1; wpas_start_wps_go(wpa_s, ¶ms, 0); return 0; } static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, struct wpa_ssid *params, int addr_allocated, int freq, int force_scan) { struct wpa_ssid *ssid; wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); if (wpa_s == NULL) return -1; if (force_scan) os_get_reltime(&wpa_s->scan_min_time); wpa_s->p2p_last_4way_hs_fail = NULL; wpa_supplicant_ap_deinit(wpa_s); ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->proto = WPA_PROTO_RSN; ssid->pbss = params->pbss; ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->ssid = os_malloc(params->ssid_len); if (ssid->ssid == NULL) { wpa_config_remove_network(wpa_s->conf, ssid->id); return -1; } os_memcpy(ssid->ssid, params->ssid, params->ssid_len); ssid->ssid_len = params->ssid_len; ssid->p2p_group = 1; ssid->export_keys = 1; if (params->psk_set) { os_memcpy(ssid->psk, params->psk, 32); ssid->psk_set = 1; } if (params->passphrase) ssid->passphrase = os_strdup(params->passphrase); wpa_s->show_group_started = 1; wpa_s->p2p_in_invitation = 1; wpa_s->p2p_invite_go_freq = freq; wpa_s->p2p_go_group_formation_completed = 0; wpa_s->global->p2p_group_formation = wpa_s; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); wpa_supplicant_select_network(wpa_s, ssid); return 0; } int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, int vht_center_freq2, int ht40, int vht, int max_oper_chwidth, int he, int edmg, const struct p2p_channels *channels, - int connection_timeout, int force_scan) + int connection_timeout, int force_scan, + bool allow_6ghz) { struct p2p_go_neg_results params; int go = 0, freq; if (ssid->disabled != 2 || ssid->ssid == NULL) return -1; if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) && go == (ssid->mode == WPAS_MODE_P2P_GO)) { wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is " "already running"); if (go == 0 && eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL)) { /* * This can happen if Invitation Response frame was lost * and the peer (GO of a persistent group) tries to * invite us again. Reschedule the timeout to avoid * terminating the wait for the connection too early * since we now know that the peer is still trying to * invite us instead of having already started the GO. */ wpa_printf(MSG_DEBUG, "P2P: Reschedule group formation timeout since peer is still trying to invite us"); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); } return 0; } os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; /* Make sure we are not running find during connection establishment */ wpas_p2p_stop_find_oper(wpa_s); wpa_s->p2p_fallback_to_go_neg = 0; if (ssid->mode == WPAS_MODE_P2P_GO) { if (force_freq > 0) { freq = wpas_p2p_select_go_freq(wpa_s, force_freq); if (freq < 0) return -1; } else { freq = wpas_p2p_select_go_freq(wpa_s, neg_freq); if (freq < 0 || (freq > 0 && !freq_included(wpa_s, channels, freq))) freq = 0; } } else if (ssid->mode == WPAS_MODE_INFRA) { freq = neg_freq; if (freq <= 0 || !freq_included(wpa_s, channels, freq)) { struct os_reltime now; struct wpa_bss *bss = wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid); os_get_reltime(&now); if (bss && !os_reltime_expired(&now, &bss->last_update, 5) && freq_included(wpa_s, channels, bss->freq)) freq = bss->freq; else freq = 0; } return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq, force_scan); } else { return -1; } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, ht40, vht, max_oper_chwidth, he, edmg, channels)) return -1; params.role_go = 1; params.psk_set = ssid->psk_set; if (params.psk_set) os_memcpy(params.psk, ssid->psk, sizeof(params.psk)); if (ssid->passphrase) { if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) { wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in " "persistent group"); return -1; } os_strlcpy(params.passphrase, ssid->passphrase, sizeof(params.passphrase)); } os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len); params.ssid_len = ssid->ssid_len; params.persistent_group = 1; wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1); if (wpa_s == NULL) return -1; p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS); wpa_s->p2p_first_connection_timeout = connection_timeout; wpas_start_wps_go(wpa_s, ¶ms, 0); return 0; } static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies, struct wpabuf *proberesp_ies) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->ap_iface) { struct hostapd_data *hapd = wpa_s->ap_iface->bss[0]; if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) { wpabuf_free(beacon_ies); wpabuf_free(proberesp_ies); return; } if (beacon_ies) { wpabuf_free(hapd->p2p_beacon_ie); hapd->p2p_beacon_ie = beacon_ies; } wpabuf_free(hapd->p2p_probe_resp_ie); hapd->p2p_probe_resp_ie = proberesp_ies; } else { wpabuf_free(beacon_ies); wpabuf_free(proberesp_ies); } wpa_supplicant_ap_update_beacon(wpa_s); } static void wpas_p2p_idle_update(void *ctx, int idle) { struct wpa_supplicant *wpa_s = ctx; if (!wpa_s->ap_iface) return; wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not "); if (idle) { if (wpa_s->global->p2p_fail_on_wps_complete && wpa_s->p2p_in_provisioning) { wpas_p2p_grpform_fail_after_wps(wpa_s); return; } wpas_p2p_set_group_idle_timeout(wpa_s); } else eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); } struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct p2p_group *group; struct p2p_group_config *cfg; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || !ssid->p2p_group) return NULL; cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) return NULL; if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect) cfg->persistent_group = 2; else if (ssid->p2p_persistent_group) cfg->persistent_group = 1; os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN); if (wpa_s->max_stations && wpa_s->max_stations < wpa_s->conf->max_num_sta) cfg->max_clients = wpa_s->max_stations; else cfg->max_clients = wpa_s->conf->max_num_sta; os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len); cfg->ssid_len = ssid->ssid_len; cfg->freq = ssid->frequency; cfg->cb_ctx = wpa_s; cfg->ie_update = wpas_p2p_ie_update; cfg->idle_update = wpas_p2p_idle_update; cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start) != 0; group = p2p_group_init(wpa_s->global->p2p, cfg); if (group == NULL) os_free(cfg); if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) p2p_group_notif_formation_done(group); wpa_s->p2p_group = group; return group; } void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int registrar) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (!wpa_s->p2p_in_provisioning) { wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P " "provisioning not in progress"); return; } if (ssid && ssid->mode == WPAS_MODE_INFRA) { u8 go_dev_addr[ETH_ALEN]; os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN); wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, ssid->ssid_len); /* Clear any stored provisioning info */ p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr); } eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); wpa_s->p2p_go_group_formation_completed = 1; if (ssid && ssid->mode == WPAS_MODE_INFRA) { /* * Use a separate timeout for initial data connection to * complete to allow the group to be removed automatically if * something goes wrong in this step before the P2P group idle * timeout mechanism is taken into use. */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Re-start group formation timeout (%d seconds) as client for initial connection", P2P_MAX_INITIAL_CONN_WAIT); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); /* Complete group formation on successful data connection. */ wpa_s->p2p_go_group_formation_completed = 0; } else if (ssid) { /* * Use a separate timeout for initial data connection to * complete to allow the group to be removed automatically if * the client does not complete data connection successfully. */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Re-start group formation timeout (%d seconds) as GO for initial connection", P2P_MAX_INITIAL_CONN_WAIT_GO); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); /* * Complete group formation on first successful data connection */ wpa_s->p2p_go_group_formation_completed = 0; } if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, peer_addr); wpas_group_formation_completed(wpa_s, 1, 0); } void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { if (!wpa_s->p2p_in_provisioning) { wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P " "provisioning not in progress"); return; } if (wpa_s->go_params) { p2p_clear_provisioning_info( wpa_s->global->p2p, wpa_s->go_params->peer_device_addr); } wpas_notify_p2p_wps_failed(wpa_s, fail); if (wpa_s == wpa_s->global->p2p_group_formation) { /* * Allow some time for the failed WPS negotiation exchange to * complete, but remove the group since group formation cannot * succeed after provisioning failure. */ wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout"); wpa_s->global->p2p_fail_on_wps_complete = 1; eloop_deplete_timeout(0, 50000, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); } } int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) { if (!wpa_s->global->p2p_fail_on_wps_complete || !wpa_s->p2p_in_provisioning) return 0; wpas_p2p_grpform_fail_after_wps(wpa_s); return 1; } int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *config_method, enum wpas_p2p_prov_disc_use use, struct p2ps_provision *p2ps_prov) { u16 config_methods; wpa_s->global->pending_p2ps_group = 0; wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->pending_pd_use = NORMAL_PD; if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) { p2ps_prov->conncap = p2ps_group_capability( wpa_s, P2PS_SETUP_NONE, p2ps_prov->role, &p2ps_prov->force_freq, &p2ps_prov->pref_freq); wpa_printf(MSG_DEBUG, "P2P: %s conncap: %d - ASP parsed: %x %x %d %s", __func__, p2ps_prov->conncap, p2ps_prov->adv_id, p2ps_prov->conncap, p2ps_prov->status, p2ps_prov->info); config_methods = 0; } else if (os_strncmp(config_method, "display", 7) == 0) config_methods = WPS_CONFIG_DISPLAY; else if (os_strncmp(config_method, "keypad", 6) == 0) config_methods = WPS_CONFIG_KEYPAD; else if (os_strncmp(config_method, "pbc", 3) == 0 || os_strncmp(config_method, "pushbutton", 10) == 0) config_methods = WPS_CONFIG_PUSHBUTTON; else { wpa_printf(MSG_DEBUG, "P2P: Unknown config method"); os_free(p2ps_prov); return -1; } if (use == WPAS_P2P_PD_AUTO) { os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN); wpa_s->pending_pd_config_methods = config_methods; wpa_s->p2p_auto_pd = 1; wpa_s->p2p_auto_join = 0; wpa_s->pending_pd_before_join = 0; wpa_s->auto_pd_scan_retry = 0; wpas_p2p_stop_find(wpa_s); wpa_s->p2p_join_scan_count = 0; os_get_reltime(&wpa_s->p2p_auto_started); wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld", wpa_s->p2p_auto_started.sec, wpa_s->p2p_auto_started.usec); wpas_p2p_join_scan(wpa_s, NULL); return 0; } if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) { os_free(p2ps_prov); return -1; } return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov, config_methods, use == WPAS_P2P_PD_FOR_JOIN, 0, 1); } int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) { return p2p_scan_result_text(ies, ies_len, buf, end); } static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) { if (!offchannel_pending_action_tx(wpa_s)) return; if (wpa_s->p2p_send_action_work) { wpas_p2p_free_send_action_work(wpa_s); eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); offchannel_send_action_done(wpa_s); } wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " "operation request"); offchannel_clear_pending_action_tx(wpa_s); } int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, unsigned int search_delay, - u8 seek_cnt, const char **seek_string, int freq) + u8 seek_cnt, const char **seek_string, int freq, + bool include_6ghz) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->global->p2p_long_listen = 0; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || wpa_s->p2p_in_provisioning) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s", (wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ? " (P2P disabled)" : "", wpa_s->p2p_in_provisioning ? " (p2p_in_provisioning)" : ""); return -1; } wpa_supplicant_cancel_sched_scan(wpa_s); return p2p_find(wpa_s->global->p2p, timeout, type, num_req_dev_types, req_dev_types, dev_id, - search_delay, seek_cnt, seek_string, freq); + search_delay, seek_cnt, seek_string, freq, + include_6ghz); } static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { wpa_printf(MSG_DEBUG, "P2P: Ignore scan results"); if (wpa_s->p2p_scan_work) { struct wpa_radio_work *work = wpa_s->p2p_scan_work; wpa_s->p2p_scan_work = NULL; radio_work_done(work); } if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; /* * Indicate that results have been processed so that the P2P module can * continue pending tasks. */ wpas_p2p_scan_res_handled(wpa_s); } static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->global->p2p_long_listen = 0; eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); if (wpa_s->global->p2p) p2p_stop_find(wpa_s->global->p2p); if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) { wpa_printf(MSG_DEBUG, "P2P: Do not consider the scan results after stop_find"); wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search; } } void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) { wpas_p2p_stop_find_oper(wpa_s); if (!wpa_s->global->pending_group_iface_for_p2ps) wpas_p2p_remove_pending_group_interface(wpa_s); } static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_s->global->p2p_long_listen = 0; } int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout) { int res; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; if (wpa_s->p2p_lo_started) { wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P listen, it is offloaded"); return -1; } wpa_supplicant_cancel_sched_scan(wpa_s); wpas_p2p_clear_pending_action_tx(wpa_s); if (timeout == 0) { /* * This is a request for unlimited Listen state. However, at * least for now, this is mapped to a Listen state for one * hour. */ timeout = 3600; } eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); wpa_s->global->p2p_long_listen = 0; /* * Stop previous find/listen operation to avoid trying to request a new * remain-on-channel operation while the driver is still running the * previous one. */ if (wpa_s->global->p2p) p2p_stop_find(wpa_s->global->p2p); res = wpas_p2p_listen_start(wpa_s, timeout * 1000); if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) { wpa_s->global->p2p_long_listen = timeout * 1000; eloop_register_timeout(timeout, 0, wpas_p2p_long_listen_timeout, wpa_s, NULL); } return res; } int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u8 *buf, size_t len, int p2p_group) { struct wpabuf *p2p_ie; int ret; if (wpa_s->global->p2p_disabled) return -1; /* * Advertize mandatory cross connection capability even on * p2p_disabled=1 interface when associating with a P2P Manager WLAN AP. */ if (wpa_s->conf->p2p_disabled && p2p_group) return -1; if (wpa_s->global->p2p == NULL) return -1; if (bss == NULL) return -1; p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len, p2p_group, p2p_ie); wpabuf_free(p2p_ie); return ret; } int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, unsigned int rx_freq, int ssi_signal) { if (wpa_s->global->p2p_disabled) return 0; if (wpa_s->global->p2p == NULL) return 0; switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) { case P2P_PREQ_NOT_P2P: wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); /* fall through */ case P2P_PREQ_MALFORMED: case P2P_PREQ_NOT_LISTEN: case P2P_PREQ_NOT_PROCESSED: default: /* make gcc happy */ return 0; case P2P_PREQ_PROCESSED: return 1; } } void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq) { if (wpa_s->global->p2p_disabled) return; if (wpa_s->global->p2p == NULL) return; p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len, freq); } void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) { unsigned int bands; if (wpa_s->global->p2p_disabled) return; if (wpa_s->global->p2p == NULL) return; bands = wpas_get_bands(wpa_s, NULL); p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); } static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s) { p2p_group_deinit(wpa_s->p2p_group); wpa_s->p2p_group = NULL; wpa_s->ap_configured_cb = NULL; wpa_s->ap_configured_cb_ctx = NULL; wpa_s->ap_configured_cb_data = NULL; wpa_s->connect_without_scan = NULL; } int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) { wpa_s->global->p2p_long_listen = 0; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; return p2p_reject(wpa_s->global->p2p, addr); } /* Invite to reinvoke a persistent group */ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, int vht_center_freq2, int ht40, int vht, int max_chwidth, - int pref_freq, int he, int edmg) + int pref_freq, int he, int edmg, bool allow_6ghz) { enum p2p_invite_role role; u8 *bssid = NULL; int force_freq = 0; int res; int no_pref_freq_given = pref_freq == 0; unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; + if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq)) + return -1; + wpa_s->global->p2p_invite_group = NULL; if (peer_addr) os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); else os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->p2p_persistent_go_freq = freq; wpa_s->p2p_go_ht40 = !!ht40; wpa_s->p2p_go_vht = !!vht; wpa_s->p2p_go_he = !!he; wpa_s->p2p_go_max_oper_chwidth = max_chwidth; wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; wpa_s->p2p_go_edmg = !!edmg; if (ssid->mode == WPAS_MODE_P2P_GO) { role = P2P_INVITE_ROLE_GO; if (peer_addr == NULL) { wpa_printf(MSG_DEBUG, "P2P: Missing peer " "address in invitation command"); return -1; } if (wpas_p2p_create_iface(wpa_s)) { if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to " "allocate a new interface for the " "group"); return -1; } bssid = wpa_s->pending_interface_addr; } else if (wpa_s->p2p_mgmt) bssid = wpa_s->parent->own_addr; else bssid = wpa_s->own_addr; } else { role = P2P_INVITE_ROLE_CLIENT; peer_addr = ssid->bssid; } wpa_s->pending_invite_ssid_id = ssid->id; size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, role == P2P_INVITE_ROLE_GO, pref_freq_list, &size); if (res) return res; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); if (wpa_s->parent->conf->p2p_ignore_shared_freq && no_pref_freq_given && pref_freq > 0 && wpa_s->num_multichan_concurrent > 1 && wpas_p2p_num_unused_channels(wpa_s) > 0) { wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration", pref_freq); pref_freq = 0; } /* * Stop any find/listen operations before invitation and possibly * connection establishment. */ wpas_p2p_stop_find_oper(wpa_s); return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr, 1, pref_freq, -1); } /* Invite to join an active group */ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, - const u8 *peer_addr, const u8 *go_dev_addr) + const u8 *peer_addr, const u8 *go_dev_addr, + bool allow_6ghz) { struct wpa_global *global = wpa_s->global; enum p2p_invite_role role; u8 *bssid = NULL; struct wpa_ssid *ssid; int persistent; int freq = 0, force_freq = 0, pref_freq = 0; int res; unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_go_ht40 = 0; wpa_s->p2p_go_vht = 0; wpa_s->p2p_go_vht_center_freq2 = 0; wpa_s->p2p_go_max_oper_chwidth = 0; wpa_s->p2p_go_edmg = 0; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) break; } if (wpa_s == NULL) { wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname); return -1; } ssid = wpa_s->current_ssid; if (ssid == NULL) { wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for " "invitation"); return -1; } wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && wpas_p2p_get_persistent(wpa_s->p2pdev, peer_addr, ssid->ssid, ssid->ssid_len); if (ssid->mode == WPAS_MODE_P2P_GO) { role = P2P_INVITE_ROLE_ACTIVE_GO; bssid = wpa_s->own_addr; if (go_dev_addr == NULL) go_dev_addr = wpa_s->global->p2p_dev_addr; freq = ssid->frequency; } else { role = P2P_INVITE_ROLE_CLIENT; if (wpa_s->wpa_state < WPA_ASSOCIATED) { wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot " "invite to current group"); return -1; } bssid = wpa_s->bssid; if (go_dev_addr == NULL && !is_zero_ether_addr(wpa_s->go_dev_addr)) go_dev_addr = wpa_s->go_dev_addr; freq = wpa_s->current_bss ? wpa_s->current_bss->freq : (int) wpa_s->assoc_freq; } wpa_s->p2pdev->pending_invite_ssid_id = -1; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq)) + return -1; size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, role == P2P_INVITE_ROLE_ACTIVE_GO, pref_freq_list, &size); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq); return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr, persistent, pref_freq, -1); } void wpas_p2p_completed(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; u8 go_dev_addr[ETH_ALEN]; int persistent; int freq; u8 ip[3 * 4], *ip_ptr = NULL; char ip_addr[100]; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); } if (!wpa_s->show_group_started || !ssid) return; wpa_s->show_group_started = 0; if (!wpa_s->p2p_go_group_formation_completed && wpa_s->global->p2p_group_formation == wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on client on data connection"); wpa_s->p2p_go_group_formation_completed = 1; wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; wpa_s->p2p_in_invitation = 0; } os_memset(go_dev_addr, 0, ETH_ALEN); if (ssid->bssid_set) os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN); persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, ssid->ssid_len); os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN); if (wpa_s->global->p2p_group_formation == wpa_s) wpa_s->global->p2p_group_formation = NULL; freq = wpa_s->current_bss ? wpa_s->current_bss->freq : (int) wpa_s->assoc_freq; ip_addr[0] = '\0'; if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) { int res; res = os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u " "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11]); if (os_snprintf_error(sizeof(ip_addr), res)) ip_addr[0] = '\0'; ip_ptr = ip; } wpas_p2p_group_started(wpa_s, 0, ssid, freq, ssid->passphrase == NULL && ssid->psk_set ? ssid->psk : NULL, ssid->passphrase, go_dev_addr, persistent, ip_addr); if (persistent) wpas_p2p_store_persistent_group(wpa_s->p2pdev, ssid, go_dev_addr); wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr); } int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, u32 interval1, u32 duration2, u32 interval2) { int ret; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL || wpa_s->current_ssid->mode != WPAS_MODE_INFRA) return -1; ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid, wpa_s->own_addr, wpa_s->assoc_freq, duration1, interval1, duration2, interval2); if (ret == 0) wpa_s->waiting_presence_resp = 1; return ret; } int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period, unsigned int interval) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; return p2p_ext_listen(wpa_s->global->p2p, period, interval); } static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s) { if (wpa_s->current_ssid == NULL) { /* * current_ssid can be cleared when P2P client interface gets * disconnected, so assume this interface was used as P2P * client. */ return 1; } return wpa_s->current_ssid->p2p_group && wpa_s->current_ssid->mode == WPAS_MODE_INFRA; } static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) { wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - " "disabled"); return; } wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate " "group"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT); } static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) { int timeout; if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) return; timeout = wpa_s->conf->p2p_group_idle; if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA && (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE)) timeout = P2P_MAX_CLIENT_IDLE; if (timeout == 0) return; if (timeout < 0) { if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA) timeout = 0; /* special client mode no-timeout */ else return; } if (wpa_s->p2p_in_provisioning) { /* * Use the normal group formation timeout during the * provisioning phase to avoid terminating this process too * early due to group idle timeout. */ wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout " "during provisioning"); return; } if (wpa_s->show_group_started) { /* * Use the normal group formation timeout between the end of * the provisioning phase and completion of 4-way handshake to * avoid terminating this process too early due to group idle * timeout. */ wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout " "while waiting for initial 4-way handshake to " "complete"); return; } wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds", timeout); eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout, wpa_s, NULL); } /* Returns 1 if the interface was removed */ int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len, int locally_generated) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; if (!locally_generated) p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated && wpa_s->current_ssid && wpa_s->current_ssid->p2p_group && wpa_s->current_ssid->mode == WPAS_MODE_INFRA) { wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group " "session is ending"); if (wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_GO_ENDING_SESSION) > 0) return 1; } return 0; } void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len, int locally_generated) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; if (!locally_generated) p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); } void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) { struct p2p_data *p2p = wpa_s->global->p2p; if (p2p == NULL) return; if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) return; if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME) p2p_set_dev_name(p2p, wpa_s->conf->device_name); if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type); if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS)) p2p_set_config_methods(p2p, wpa_s->wps->config_methods); if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)) p2p_set_uuid(p2p, wpa_s->wps->uuid); if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) { p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer); p2p_set_model_name(p2p, wpa_s->conf->model_name); p2p_set_model_number(p2p, wpa_s->conf->model_number); p2p_set_serial_number(p2p, wpa_s->conf->serial_number); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) p2p_set_sec_dev_types(p2p, (void *) wpa_s->conf->sec_device_type, wpa_s->conf->num_sec_device_types); if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) { int i; p2p_remove_wps_vendor_extensions(p2p); for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { if (wpa_s->conf->wps_vendor_ext[i] == NULL) continue; p2p_add_wps_vendor_extension( p2p, wpa_s->conf->wps_vendor_ext[i]); } } if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && wpa_s->conf->country[0] && wpa_s->conf->country[1]) { char country[3]; country[0] = wpa_s->conf->country[0]; country[1] = wpa_s->conf->country[1]; country[2] = 0x04; p2p_set_country(p2p, country); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) { p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix, wpa_s->conf->p2p_ssid_postfix ? os_strlen(wpa_s->conf->p2p_ssid_postfix) : 0); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS) p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss); if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) { u8 reg_class, channel; int ret; unsigned int r; u8 channel_forced; if (wpa_s->conf->p2p_listen_reg_class && wpa_s->conf->p2p_listen_channel) { reg_class = wpa_s->conf->p2p_listen_reg_class; channel = wpa_s->conf->p2p_listen_channel; channel_forced = 1; } else { reg_class = 81; /* * Pick one of the social channels randomly as the * listen channel. */ if (os_get_random((u8 *) &r, sizeof(r)) < 0) channel = 1; else channel = 1 + (r % 3) * 5; channel_forced = 0; } ret = p2p_set_listen_channel(p2p, reg_class, channel, channel_forced); if (ret) wpa_printf(MSG_ERROR, "P2P: Own listen channel update " "failed: %d", ret); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) { u8 op_reg_class, op_channel, cfg_op_channel; int ret = 0; unsigned int r; if (wpa_s->conf->p2p_oper_reg_class && wpa_s->conf->p2p_oper_channel) { op_reg_class = wpa_s->conf->p2p_oper_reg_class; op_channel = wpa_s->conf->p2p_oper_channel; cfg_op_channel = 1; } else { op_reg_class = 81; /* * Use random operation channel from (1, 6, 11) *if no other preference is indicated. */ if (os_get_random((u8 *) &r, sizeof(r)) < 0) op_channel = 1; else op_channel = 1 + (r % 3) * 5; cfg_op_channel = 0; } ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel, cfg_op_channel); if (ret) wpa_printf(MSG_ERROR, "P2P: Own oper channel update " "failed: %d", ret); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) { if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan, wpa_s->conf->p2p_pref_chan) < 0) { wpa_printf(MSG_ERROR, "P2P: Preferred channel list " "update failed"); } if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) { wpa_printf(MSG_ERROR, "P2P: No GO channel list " "update failed"); } } if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN) p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len); } int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, int duration) { if (!wpa_s->ap_iface) return -1; return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start, duration); } int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; wpa_s->global->cross_connection = enabled; p2p_set_cross_connect(wpa_s->global->p2p, enabled); if (!enabled) { struct wpa_supplicant *iface; for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { if (iface->cross_connect_enabled == 0) continue; iface->cross_connect_enabled = 0; iface->cross_connect_in_use = 0; wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", iface->ifname, iface->cross_connect_uplink); } } return 0; } static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) { struct wpa_supplicant *iface; if (!uplink->global->cross_connection) return; for (iface = uplink->global->ifaces; iface; iface = iface->next) { if (!iface->cross_connect_enabled) continue; if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != 0) continue; if (iface->ap_iface == NULL) continue; if (iface->cross_connect_in_use) continue; iface->cross_connect_in_use = 1; wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", iface->ifname, iface->cross_connect_uplink); } } static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) { struct wpa_supplicant *iface; for (iface = uplink->global->ifaces; iface; iface = iface->next) { if (!iface->cross_connect_enabled) continue; if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != 0) continue; if (!iface->cross_connect_in_use) continue; wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", iface->ifname, iface->cross_connect_uplink); iface->cross_connect_in_use = 0; } } void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s) { if (wpa_s->ap_iface || wpa_s->current_ssid == NULL || wpa_s->current_ssid->mode != WPAS_MODE_INFRA || wpa_s->cross_connect_disallowed) wpas_p2p_disable_cross_connect(wpa_s); else wpas_p2p_enable_cross_connect(wpa_s); if (!wpa_s->ap_iface && eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); } void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) { wpas_p2p_disable_cross_connect(wpa_s); if (!wpa_s->ap_iface && !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout, wpa_s, NULL)) wpas_p2p_set_group_idle_timeout(wpa_s); } static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) { struct wpa_supplicant *iface; if (!wpa_s->global->cross_connection) return; for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { if (iface == wpa_s) continue; if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) continue; if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && iface != wpa_s->parent) continue; wpa_s->cross_connect_enabled = 1; os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname, sizeof(wpa_s->cross_connect_uplink)); wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from " "%s to %s whenever uplink is available", wpa_s->ifname, wpa_s->cross_connect_uplink); if (iface->ap_iface || iface->current_ssid == NULL || iface->current_ssid->mode != WPAS_MODE_INFRA || iface->cross_connect_disallowed || iface->wpa_state != WPA_COMPLETED) break; wpa_s->cross_connect_in_use = 1; wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); break; } } int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT && !wpa_s->p2p_in_provisioning) return 0; /* not P2P client operation */ wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC " "session overlap"); if (wpa_s != wpa_s->p2pdev) wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP); wpas_p2p_group_formation_failed(wpa_s, 0); return 1; } void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpas_p2p_notif_pbc_overlap(wpa_s); } void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, enum wpas_p2p_channel_update_trig trig) { struct p2p_channels chan, cli_chan; struct wpa_used_freq_data *freqs = NULL; unsigned int num = wpa_s->num_multichan_concurrent; if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) return; freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); if (!freqs) return; num = get_shared_radio_freqs_data(wpa_s, freqs, num); os_memset(&chan, 0, sizeof(chan)); os_memset(&cli_chan, 0, sizeof(cli_chan)); if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan, is_p2p_6ghz_disabled(wpa_s->global->p2p))) { wpa_printf(MSG_ERROR, "P2P: Failed to update supported " "channel list"); return; } p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan); wpas_p2p_optimize_listen_channel(wpa_s, freqs, num); /* * The used frequencies map changed, so it is possible that a GO is * using a channel that is no longer valid for P2P use. It is also * possible that due to policy consideration, it would be preferable to * move it to a frequency already used by other station interfaces. */ wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig); os_free(freqs); } static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { wpa_printf(MSG_DEBUG, "P2P: Ignore scan results"); } int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) { struct wpa_global *global = wpa_s->global; int found = 0; const u8 *peer; if (global->p2p == NULL) return -1; wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation"); if (wpa_s->pending_interface_name[0] && !is_zero_ether_addr(wpa_s->pending_interface_addr)) found = 1; peer = p2p_get_go_neg_peer(global->p2p); if (peer) { wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer " MACSTR, MAC2STR(peer)); p2p_unauthorize(global->p2p, peer); found = 1; } if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) { wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join"); wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore; found = 1; } if (wpa_s->pending_pd_before_join) { wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join"); wpa_s->pending_pd_before_join = 0; found = 1; } wpas_p2p_stop_find(wpa_s); for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_s == global->p2p_group_formation && (wpa_s->p2p_in_provisioning || wpa_s->parent->pending_interface_type == WPA_IF_P2P_CLIENT)) { wpa_printf(MSG_DEBUG, "P2P: Interface %s in group " "formation found - cancelling", wpa_s->ifname); found = 1; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); if (wpa_s->p2p_in_provisioning) { wpas_group_formation_completed(wpa_s, 0, 0); break; } wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED); break; } else if (wpa_s->p2p_in_invitation) { wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling", wpa_s->ifname); found = 1; wpas_p2p_group_formation_failed(wpa_s, 0); break; } } if (!found) { wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found"); return -1; } return 0; } void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s) { if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) return; wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not " "being available anymore"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE); } void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, int freq_24, int freq_5, int freq_overall) { struct p2p_data *p2p = wpa_s->global->p2p; if (p2p == NULL) return; p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall); } int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr) { u8 peer[ETH_ALEN]; struct p2p_data *p2p = wpa_s->global->p2p; if (p2p == NULL) return -1; if (hwaddr_aton(addr, peer)) return -1; return p2p_unauthorize(p2p, peer); } /** * wpas_p2p_disconnect - Disconnect from a P2P Group * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success, -1 on failure * * This can be used to disconnect from a group in which the local end is a P2P * Client or to end a P2P Group in case the local end is the Group Owner. If a * virtual network interface was created for this group, that interface will be * removed. Otherwise, only the configured P2P group network will be removed * from the interface. */ int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s) { if (wpa_s == NULL) return -1; return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ? -1 : 0; } int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) { int ret; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; ret = p2p_in_progress(wpa_s->global->p2p); if (ret == 0) { /* * Check whether there is an ongoing WPS provisioning step (or * other parts of group formation) on another interface since * p2p_in_progress() does not report this to avoid issues for * scans during such provisioning step. */ if (wpa_s->global->p2p_group_formation && wpa_s->global->p2p_group_formation != wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) " "in group formation", wpa_s->global->p2p_group_formation->ifname); ret = 1; } } if (!ret && wpa_s->global->p2p_go_wait_client.sec) { struct os_reltime now; os_get_reltime(&now); if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client, P2P_MAX_INITIAL_CONN_WAIT_GO)) { /* Wait for the first client has expired */ wpa_s->global->p2p_go_wait_client.sec = 0; } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation"); ret = 1; } } return ret; } void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { if (wpa_s->p2p_in_provisioning && ssid->p2p_group && eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL) > 0) { /** * Remove the network by scheduling the group formation * timeout to happen immediately. The teardown code * needs to be scheduled to run asynch later so that we * don't delete data from under ourselves unexpectedly. * Calling wpas_p2p_group_formation_timeout directly * causes a series of crashes in WPS failure scenarios. */ wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to " "P2P group network getting removed"); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); } } struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *ssid, size_t ssid_len) { struct wpa_ssid *s; size_t i; for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2) continue; if (ssid && (ssid_len != s->ssid_len || os_memcmp(ssid, s->ssid, ssid_len) != 0)) continue; if (addr == NULL) { if (s->mode == WPAS_MODE_P2P_GO) return s; continue; } if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0) return s; /* peer is GO in the persistent group */ if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL) continue; for (i = 0; i < s->num_p2p_clients; i++) { if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr, ETH_ALEN) == 0) return s; /* peer is P2P client in persistent * group */ } } return NULL; } void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL) > 0) { /* * This can happen if WPS provisioning step is not terminated * cleanly (e.g., P2P Client does not send WSC_Done). Since the * peer was able to connect, there is no need to time out group * formation after this, though. In addition, this is used with * the initial connection wait on the GO as a separate formation * timeout and as such, expected to be hit after the initial WPS * provisioning step. */ wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection"); if (!wpa_s->p2p_go_group_formation_completed && !wpa_s->group_formation_reported) { /* * GO has not yet notified group formation success since * the WPS step was not completed cleanly. Do that * notification now since the P2P Client was able to * connect and as such, must have received the * credential from the WPS step. */ if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, addr); wpas_group_formation_completed(wpa_s, 1, 0); } } if (!wpa_s->p2p_go_group_formation_completed) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection"); wpa_s->p2p_go_group_formation_completed = 1; wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; wpa_s->p2p_in_invitation = 0; } wpa_s->global->p2p_go_wait_client.sec = 0; if (addr == NULL) return; wpas_p2p_add_persistent_group_client(wpa_s, addr); } static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, int group_added) { struct wpa_supplicant *group = wpa_s; int ret = 0; if (wpa_s->global->p2p_group_formation) group = wpa_s->global->p2p_group_formation; wpa_s = wpa_s->global->p2p_init_wpa_s; offchannel_send_action_done(wpa_s); if (group_added) ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0); + NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p)); return ret; } int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) { int res; if (!wpa_s->p2p_fallback_to_go_neg || wpa_s->p2p_in_provisioning <= 5) return 0; if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0) return 0; /* peer operating as a GO */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - " "fallback to GO Negotiation"); wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=GO-not-found"); res = wpas_p2p_fallback_to_go_neg(wpa_s, 1); return res == 1 ? 2 : 1; } unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s) { struct wpa_supplicant *ifs; if (wpa_s->wpa_state > WPA_SCANNING) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to " "concurrent operation", wpa_s->conf->p2p_search_delay); return wpa_s->conf->p2p_search_delay; } dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search " "delay due to concurrent operation on " "interface %s", wpa_s->conf->p2p_search_delay, ifs->ifname); return wpa_s->conf->p2p_search_delay; } } return 0; } static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s, struct wpa_ssid *s, const u8 *addr, int iface_addr) { struct psk_list_entry *psk, *tmp; int changed = 0; dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry, list) { if ((iface_addr && !psk->p2p && os_memcmp(addr, psk->addr, ETH_ALEN) == 0) || (!iface_addr && psk->p2p && os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove persistent group PSK list entry for " MACSTR " p2p=%u", MAC2STR(psk->addr), psk->p2p); dl_list_del(&psk->list); os_free(psk); changed++; } } return changed; } void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_ssid *persistent; struct psk_list_entry *p, *last; if (psk_len != sizeof(p->psk)) return; if (p2p_dev_addr) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR " p2p_dev_addr=" MACSTR, MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); if (is_zero_ether_addr(p2p_dev_addr)) p2p_dev_addr = NULL; } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR, MAC2STR(mac_addr)); } if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation"); /* To be added to persistent group once created */ if (wpa_s->global->add_psk == NULL) { wpa_s->global->add_psk = os_zalloc(sizeof(*p)); if (wpa_s->global->add_psk == NULL) return; } p = wpa_s->global->add_psk; if (p2p_dev_addr) { p->p2p = 1; os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); } else { p->p2p = 0; os_memcpy(p->addr, mac_addr, ETH_ALEN); } os_memcpy(p->psk, psk, psk_len); return; } if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO"); return; } persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid, ssid->ssid_len); if (!persistent) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK"); return; } p = os_zalloc(sizeof(*p)); if (p == NULL) return; if (p2p_dev_addr) { p->p2p = 1; os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); } else { p->p2p = 0; os_memcpy(p->addr, mac_addr, ETH_ALEN); } os_memcpy(p->psk, psk, psk_len); if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS && (last = dl_list_last(&persistent->psk_list, struct psk_list_entry, list))) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for " MACSTR " (p2p=%u) to make room for a new one", MAC2STR(last->addr), last->p2p); dl_list_del(&last->list); os_free(last); } wpas_p2p_remove_psk_entry(wpa_s->p2pdev, persistent, p2p_dev_addr ? p2p_dev_addr : mac_addr, p2p_dev_addr == NULL); if (p2p_dev_addr) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr=" MACSTR, MAC2STR(p2p_dev_addr)); } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR, MAC2STR(mac_addr)); } dl_list_add(&persistent->psk_list, &p->list); if (wpa_s->p2pdev->conf->update_config && wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s, struct wpa_ssid *s, const u8 *addr, int iface_addr) { int res; res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr); if (res > 0 && wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to update configuration"); } static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s, const u8 *peer, int iface_addr) { struct hostapd_data *hapd; struct hostapd_wpa_psk *psk, *prev, *rem; struct sta_info *sta; if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL || wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO) return; /* Remove per-station PSK entry */ hapd = wpa_s->ap_iface->bss[0]; prev = NULL; psk = hapd->conf->ssid.wpa_psk; while (psk) { if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) || (!iface_addr && os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for " MACSTR " iface_addr=%d", MAC2STR(peer), iface_addr); if (prev) prev->next = psk->next; else hapd->conf->ssid.wpa_psk = psk->next; rem = psk; psk = psk->next; os_free(rem); } else { prev = psk; psk = psk->next; } } /* Disconnect from group */ if (iface_addr) sta = ap_get_sta(hapd, peer); else sta = ap_get_sta_p2p(hapd, peer); if (sta) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR " (iface_addr=%d) from group", MAC2STR(peer), iface_addr); hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_DEAUTH_LEAVING); ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING); } } void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, int iface_addr) { struct wpa_ssid *s; struct wpa_supplicant *w; struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer)); /* Remove from any persistent group */ for (s = p2p_wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) continue; if (!iface_addr) wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0); wpas_p2p_remove_psk(p2p_wpa_s, s, peer, iface_addr); } /* Remove from any operating group */ for (w = wpa_s->global->ifaces; w; w = w->next) wpas_p2p_remove_client_go(w, peer, iface_addr); } static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE); } static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT); } int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid) { struct wpa_supplicant *iface; for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { if (!iface->current_ssid || iface->current_ssid->frequency == freq || (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && !iface->current_ssid->p2p_group)) continue; /* Remove the connection with least priority */ if (!wpas_is_p2p_prioritized(iface)) { /* STA connection has priority over existing * P2P connection, so remove the interface. */ wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict"); eloop_register_timeout(0, 0, wpas_p2p_group_freq_conflict, iface, NULL); /* If connection in progress is P2P connection, do not * proceed for the connection. */ if (wpa_s == iface) return -1; else return 0; } else { /* P2P connection has priority, disable the STA network */ wpa_supplicant_disable_network(wpa_s->global->ifaces, ssid); wpa_msg(wpa_s->global->ifaces, MSG_INFO, WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id); os_memset(wpa_s->global->ifaces->pending_bssid, 0, ETH_ALEN); /* If P2P connection is in progress, continue * connecting...*/ if (wpa_s == iface) return 0; else return -1; } } return 0; } int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL || !ssid->p2p_group) return 0; if (wpa_s->p2p_last_4way_hs_fail && wpa_s->p2p_last_4way_hs_fail == ssid) { u8 go_dev_addr[ETH_ALEN]; struct wpa_ssid *persistent; if (wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, ssid->ssid_len) <= 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group"); goto disconnect; } wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr=" MACSTR, MAC2STR(go_dev_addr)); persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, go_dev_addr, ssid->ssid, ssid->ssid_len); if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored"); goto disconnect; } wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_PERSISTENT_PSK_FAIL "%d", persistent->id); disconnect: wpa_s->p2p_last_4way_hs_fail = NULL; /* * Remove the group from a timeout to avoid issues with caller * continuing to use the interface if this is on a P2P group * interface. */ eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal, wpa_s, NULL); return 1; } wpa_s->p2p_last_4way_hs_fail = ssid; return 0; } #ifdef CONFIG_WPS_NFC static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc, struct wpabuf *p2p) { struct wpabuf *ret; size_t wsc_len; if (p2p == NULL) { wpabuf_free(wsc); wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover"); return NULL; } wsc_len = wsc ? wpabuf_len(wsc) : 0; ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p)); if (ret == NULL) { wpabuf_free(wsc); wpabuf_free(p2p); return NULL; } wpabuf_put_be16(ret, wsc_len); if (wsc) wpabuf_put_buf(ret, wsc); wpabuf_put_be16(ret, wpabuf_len(p2p)); wpabuf_put_buf(ret, p2p); wpabuf_free(wsc); wpabuf_free(p2p); wpa_hexdump_buf(MSG_DEBUG, "P2P: Generated NFC connection handover message", ret); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_p2p(ret); wpabuf_free(ret); if (tmp == NULL) { wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request"); return NULL; } ret = tmp; } return ret; } static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s, struct wpa_ssid **ssid, u8 *go_dev_addr) { struct wpa_supplicant *iface; if (go_dev_addr) os_memset(go_dev_addr, 0, ETH_ALEN); if (ssid) *ssid = NULL; for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { if (iface->wpa_state < WPA_ASSOCIATING || iface->current_ssid == NULL || iface->assoc_freq == 0 || !iface->current_ssid->p2p_group || iface->current_ssid->mode != WPAS_MODE_INFRA) continue; if (ssid) *ssid = iface->current_ssid; if (go_dev_addr) os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN); return iface->assoc_freq; } return 0; } struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, int ndef) { struct wpabuf *wsc, *p2p; struct wpa_ssid *ssid; u8 go_dev_addr[ETH_ALEN]; int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) { wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request"); return NULL; } if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request"); return NULL; } if (cli_freq == 0) { wsc = wps_build_nfc_handover_req_p2p( wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey); } else wsc = NULL; p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq, go_dev_addr, ssid ? ssid->ssid : NULL, ssid ? ssid->ssid_len : 0); return wpas_p2p_nfc_handover(ndef, wsc, p2p); } struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, int tag) { struct wpabuf *wsc, *p2p; struct wpa_ssid *ssid; u8 go_dev_addr[ETH_ALEN]; int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return NULL; if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL && wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey) < 0) return NULL; if (cli_freq == 0) { wsc = wps_build_nfc_handover_sel_p2p( wpa_s->parent->wps, tag ? wpa_s->conf->wps_nfc_dev_pw_id : DEV_PW_NFC_CONNECTION_HANDOVER, wpa_s->conf->wps_nfc_dh_pubkey, tag ? wpa_s->conf->wps_nfc_dev_pw : NULL); } else wsc = NULL; p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq, go_dev_addr, ssid ? ssid->ssid : NULL, ssid ? ssid->ssid_len : 0); return wpas_p2p_nfc_handover(ndef, wsc, p2p); } static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, struct p2p_nfc_params *params) { wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC " "connection handover (freq=%d)", params->go_freq); if (params->go_freq && params->go_ssid_len) { wpa_s->p2p_wps_method = WPS_NFC; wpa_s->pending_join_wps_method = WPS_NFC; os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN); os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr, ETH_ALEN); return wpas_p2p_join_start(wpa_s, params->go_freq, params->go_ssid, params->go_ssid_len); } return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, params->go_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, params->go_ssid_len ? params->go_ssid : NULL, - params->go_ssid_len); + params->go_ssid_len, false); } static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, struct p2p_nfc_params *params, int tag) { int res, persistent; struct wpa_ssid *ssid; wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC " "connection handover"); for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { ssid = wpa_s->current_ssid; if (ssid == NULL) continue; if (ssid->mode != WPAS_MODE_P2P_GO) continue; if (wpa_s->ap_iface == NULL) continue; break; } if (wpa_s == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface"); return -1; } if (wpa_s->p2pdev->p2p_oob_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && !wpa_s->p2pdev->p2p_oob_dev_pw) { wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); return -1; } res = wpas_ap_wps_add_nfc_pw( wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id, wpa_s->p2pdev->p2p_oob_dev_pw, wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ? wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL); if (res) return res; if (!tag) { wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation"); return 0; } if (!params->peer || !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE)) return 0; wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR " to join", MAC2STR(params->peer->p2p_device_addr)); wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && wpas_p2p_get_persistent(wpa_s->p2pdev, params->peer->p2p_device_addr, ssid->ssid, ssid->ssid_len); wpa_s->p2pdev->pending_invite_ssid_id = -1; return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr, P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr, ssid->ssid, ssid->ssid_len, ssid->frequency, wpa_s->global->p2p_dev_addr, persistent, 0, wpa_s->p2pdev->p2p_oob_dev_pw_id); } static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, struct p2p_nfc_params *params, int forced_freq) { wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC " "connection handover"); return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, forced_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0); + NULL, 0, false); } static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, struct p2p_nfc_params *params, int forced_freq) { int res; wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC " "connection handover"); res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, forced_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0); + NULL, 0, false); if (res) return res; res = wpas_p2p_listen(wpa_s, 60); if (res) { p2p_unauthorize(wpa_s->global->p2p, params->peer->p2p_device_addr); } return res; } static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, const struct wpabuf *data, int sel, int tag, int forced_freq) { const u8 *pos, *end; u16 len, id; struct p2p_nfc_params params; int res; os_memset(¶ms, 0, sizeof(params)); params.sel = sel; wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data); pos = wpabuf_head(data); end = pos + wpabuf_len(data); if (end - pos < 2) { wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC " "attributes"); return -1; } len = WPA_GET_BE16(pos); pos += 2; if (len > end - pos) { wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC " "attributes"); return -1; } params.wsc_attr = pos; params.wsc_len = len; pos += len; if (end - pos < 2) { wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P " "attributes"); return -1; } len = WPA_GET_BE16(pos); pos += 2; if (len > end - pos) { wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P " "attributes"); return -1; } params.p2p_attr = pos; params.p2p_len = len; pos += len; wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes", params.wsc_attr, params.wsc_len); wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes", params.p2p_attr, params.p2p_len); if (pos < end) { wpa_hexdump(MSG_DEBUG, "P2P: Ignored extra data after P2P attributes", pos, end - pos); } res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, ¶ms); if (res) return res; if (params.next_step == NO_ACTION) return 0; if (params.next_step == BOTH_GO) { wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR, MAC2STR(params.peer->p2p_device_addr)); return 0; } if (params.next_step == PEER_CLIENT) { if (!is_zero_ether_addr(params.go_dev_addr)) { wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT "peer=" MACSTR " freq=%d go_dev_addr=" MACSTR " ssid=\"%s\"", MAC2STR(params.peer->p2p_device_addr), params.go_freq, MAC2STR(params.go_dev_addr), wpa_ssid_txt(params.go_ssid, params.go_ssid_len)); } else { wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT "peer=" MACSTR " freq=%d", MAC2STR(params.peer->p2p_device_addr), params.go_freq); } return 0; } if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) { wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer=" MACSTR, MAC2STR(params.peer->p2p_device_addr)); return 0; } wpabuf_free(wpa_s->p2p_oob_dev_pw); wpa_s->p2p_oob_dev_pw = NULL; if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw " "received"); return -1; } id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN); wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id); wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash", params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash, params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); wpa_s->p2p_peer_oob_pk_hash_known = 1; if (tag) { if (id < 0x10) { wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid " "peer OOB Device Password Id %u", id); return -1; } wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB " "Device Password Id %u", id); wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password", params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, params.oob_dev_pw_len - WPS_OOB_PUBKEY_HASH_LEN - 2); wpa_s->p2p_oob_dev_pw_id = id; wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy( params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, params.oob_dev_pw_len - WPS_OOB_PUBKEY_HASH_LEN - 2); if (wpa_s->p2p_oob_dev_pw == NULL) return -1; if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey) < 0) return -1; } else { wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake " "without Device Password"); wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; } switch (params.next_step) { case NO_ACTION: case BOTH_GO: case PEER_CLIENT: /* already covered above */ return 0; case JOIN_GROUP: return wpas_p2p_nfc_join_group(wpa_s, ¶ms); case AUTH_JOIN: return wpas_p2p_nfc_auth_join(wpa_s, ¶ms, tag); case INIT_GO_NEG: return wpas_p2p_nfc_init_go_neg(wpa_s, ¶ms, forced_freq); case RESP_GO_NEG: /* TODO: use own OOB Dev Pw */ return wpas_p2p_nfc_resp_go_neg(wpa_s, ¶ms, forced_freq); } return -1; } int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s, const struct wpabuf *data, int forced_freq) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq); } int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, const struct wpabuf *req, const struct wpabuf *sel, int forced_freq) { struct wpabuf *tmp; int ret; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported"); wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req", wpabuf_head(req), wpabuf_len(req)); wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel", wpabuf_head(sel), wpabuf_len(sel)); if (forced_freq) wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq); tmp = ndef_parse_p2p(init ? sel : req); if (tmp == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF"); return -1; } ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0, forced_freq); wpabuf_free(tmp); return ret; } int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled) { const u8 *if_addr; int go_intent = wpa_s->conf->p2p_go_intent; struct wpa_supplicant *iface; if (wpa_s->global->p2p == NULL) return -1; if (!enabled) { wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag"); for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { if (!iface->ap_iface) continue; hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]); } p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0, 0, NULL); if (wpa_s->p2p_nfc_tag_enabled) wpas_p2p_remove_pending_group_interface(wpa_s); wpa_s->p2p_nfc_tag_enabled = 0; return 0; } if (wpa_s->global->p2p_disabled) return -1; if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || wpa_s->conf->wps_nfc_dh_privkey == NULL || wpa_s->conf->wps_nfc_dev_pw == NULL || wpa_s->conf->wps_nfc_dev_pw_id < 0x10) { wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured " "to allow static handover cases"); return -1; } wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag"); wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; wpabuf_free(wpa_s->p2p_oob_dev_pw); wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw); if (wpa_s->p2p_oob_dev_pw == NULL) return -1; wpa_s->p2p_peer_oob_pk_hash_known = 0; if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) { /* * P2P Group Interface present and the command came on group * interface, so enable the token for the current interface. */ wpa_s->create_p2p_iface = 0; } else { wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); } if (wpa_s->create_p2p_iface) { enum wpa_driver_if_type iftype; /* Prepare to add a new interface for the group */ iftype = WPA_IF_P2P_GROUP; if (go_intent == 15) iftype = WPA_IF_P2P_GO; if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " "interface for the group"); return -1; } if_addr = wpa_s->pending_interface_addr; } else if (wpa_s->p2p_mgmt) if_addr = wpa_s->parent->own_addr; else if_addr = wpa_s->own_addr; wpa_s->p2p_nfc_tag_enabled = enabled; for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { struct hostapd_data *hapd; if (iface->ap_iface == NULL) continue; hapd = iface->ap_iface->bss[0]; wpabuf_free(hapd->conf->wps_nfc_dh_pubkey); hapd->conf->wps_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); wpabuf_free(hapd->conf->wps_nfc_dh_privkey); hapd->conf->wps_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); wpabuf_free(hapd->conf->wps_nfc_dev_pw); hapd->conf->wps_nfc_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw); hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) { wpa_dbg(iface, MSG_DEBUG, "P2P: Failed to enable NFC Tag for GO"); } } p2p_set_authorized_oob_dev_pw_id( wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent, if_addr); return 0; } #endif /* CONFIG_WPS_NFC */ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num) { u8 curr_chan, cand, chan; unsigned int i; /* * If possible, optimize the Listen channel to be a channel that is * already used by one of the other interfaces. */ if (!wpa_s->conf->p2p_optimize_listen_chan) return; if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED) return; curr_chan = p2p_get_listen_channel(wpa_s->global->p2p); for (i = 0, cand = 0; i < num; i++) { ieee80211_freq_to_chan(freqs[i].freq, &chan); if (curr_chan == chan) { cand = 0; break; } if (chan == 1 || chan == 6 || chan == 11) cand = chan; } if (cand) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update Listen channel to %u based on operating channel", cand); p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0); } } static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) { struct hostapd_config *conf; struct p2p_go_neg_results params; struct csa_settings csa_settings; struct wpa_ssid *current_ssid = wpa_s->current_ssid; int old_freq = current_ssid->frequency; int ret; if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled"); return -1; } /* * TODO: This function may not always work correctly. For example, * when we have a running GO and a BSS on a DFS channel. */ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, 0, 0, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: Failed to select new frequency for GO"); return -1; } if (current_ssid->frequency == params.freq) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: Selected same frequency - not moving GO"); return 0; } conf = hostapd_config_defaults(); if (!conf) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: Failed to allocate default config"); return -1; } current_ssid->frequency = params.freq; if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: Failed to create new GO config"); ret = -1; goto out; } if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: CSA to a different band is not supported"); ret = -1; goto out; } os_memset(&csa_settings, 0, sizeof(csa_settings)); csa_settings.cs_count = P2P_GO_CSA_COUNT; csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX; csa_settings.freq_params.freq = params.freq; csa_settings.freq_params.sec_channel_offset = conf->secondary_channel; csa_settings.freq_params.ht_enabled = conf->ieee80211n; csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20; if (conf->ieee80211ac) { int freq1 = 0, freq2 = 0; u8 chan, opclass; if (ieee80211_freq_to_channel_ext(params.freq, conf->secondary_channel, conf->vht_oper_chwidth, &opclass, &chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_ERROR, "P2P CSA: Bad freq"); ret = -1; goto out; } if (conf->vht_oper_centr_freq_seg0_idx) freq1 = ieee80211_chan_to_freq( NULL, opclass, conf->vht_oper_centr_freq_seg0_idx); if (conf->vht_oper_centr_freq_seg1_idx) freq2 = ieee80211_chan_to_freq( NULL, opclass, conf->vht_oper_centr_freq_seg1_idx); if (freq1 < 0 || freq2 < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: Selected invalid VHT center freqs"); ret = -1; goto out; } csa_settings.freq_params.vht_enabled = conf->ieee80211ac; csa_settings.freq_params.center_freq1 = freq1; csa_settings.freq_params.center_freq2 = freq2; switch (conf->vht_oper_chwidth) { case CHANWIDTH_80MHZ: case CHANWIDTH_80P80MHZ: csa_settings.freq_params.bandwidth = 80; break; case CHANWIDTH_160MHZ: csa_settings.freq_params.bandwidth = 160; break; } } ret = ap_switch_channel(wpa_s, &csa_settings); out: current_ssid->frequency = old_freq; hostapd_config_free(conf); return ret; } static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s) { struct p2p_go_neg_results params; struct wpa_ssid *current_ssid = wpa_s->current_ssid; void (*ap_configured_cb)(void *ctx, void *data); void *ap_configured_cb_ctx, *ap_configured_cb_data; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz", current_ssid->frequency); /* Stop the AP functionality */ /* TODO: Should do this in a way that does not indicated to possible * P2P Clients in the group that the group is terminated. */ /* If this action occurs before a group is started, the callback should * be preserved, or GROUP-STARTED event would be lost. If this action * occurs after a group is started, these pointers are all NULL and * harmless. */ ap_configured_cb = wpa_s->ap_configured_cb; ap_configured_cb_ctx = wpa_s->ap_configured_cb_ctx; ap_configured_cb_data = wpa_s->ap_configured_cb_data; wpa_supplicant_ap_deinit(wpa_s); /* Reselect the GO frequency */ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, 0, 0, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); return; } wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)", params.freq); if (params.freq && !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { wpa_printf(MSG_DEBUG, "P2P: Selected freq (%u MHz) is not valid for P2P", params.freq); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); return; } /* Restore preserved callback parameters */ wpa_s->ap_configured_cb = ap_configured_cb; wpa_s->ap_configured_cb_ctx = ap_configured_cb_ctx; wpa_s->ap_configured_cb_data = ap_configured_cb_data; /* Update the frequency */ current_ssid->frequency = params.freq; wpa_s->connect_without_scan = current_ssid; wpa_s->reassociate = 1; wpa_s->disconnected = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); } static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (!wpa_s->ap_iface || !wpa_s->current_ssid) return; wpas_p2p_go_update_common_freqs(wpa_s); /* Do not move GO in the middle of a CSA */ if (hostapd_csa_in_progress(wpa_s->ap_iface)) { wpa_printf(MSG_DEBUG, "P2P: CSA is in progress - not moving GO"); return; } /* * First, try a channel switch flow. If it is not supported or fails, * take down the GO and bring it up again. */ if (wpas_p2p_move_go_csa(wpa_s) < 0) wpas_p2p_move_go_no_csa(wpa_s); } static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_used_freq_data *freqs = NULL; unsigned int num = wpa_s->num_multichan_concurrent; freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); if (!freqs) return; num = get_shared_radio_freqs_data(wpa_s, freqs, num); /* Previous attempt to move a GO was not possible -- try again. */ wpas_p2p_consider_moving_gos(wpa_s, freqs, num, WPAS_P2P_CHANNEL_UPDATE_ANY); os_free(freqs); } /* * Consider moving a GO from its currently used frequency: * 1. It is possible that due to regulatory consideration the frequency * can no longer be used and there is a need to evacuate the GO. * 2. It is possible that due to MCC considerations, it would be preferable * to move the GO to a channel that is currently used by some other * station interface. * * In case a frequency that became invalid is once again valid, cancel a * previously initiated GO frequency change. */ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num) { unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0; unsigned int timeout; int freq; int dfs_offload; wpas_p2p_go_update_common_freqs(wpa_s); freq = wpa_s->current_ssid->frequency; dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes); for (i = 0, invalid_freq = 0; i < num; i++) { if (freqs[i].freq == freq) { flags = freqs[i].flags; /* The channel is invalid, must change it */ if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) && !dfs_offload) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Freq=%d MHz no longer valid for GO", freq); invalid_freq = 1; } } else if (freqs[i].flags == 0) { /* Freq is not used by any other station interface */ continue; } else if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq) && !dfs_offload) { /* Freq is not valid for P2P use cases */ continue; } else if (wpa_s->conf->p2p_go_freq_change_policy == P2P_GO_FREQ_MOVE_SCM) { policy_move = 1; } else if (wpa_s->conf->p2p_go_freq_change_policy == P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS && wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { policy_move = 1; } else if ((wpa_s->conf->p2p_go_freq_change_policy == P2P_GO_FREQ_MOVE_SCM_ECSA) && wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { if (!p2p_get_group_num_members(wpa_s->p2p_group)) { policy_move = 1; } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) && wpas_p2p_go_clients_support_ecsa(wpa_s)) { u8 chan; /* * We do not support CSA between bands, so move * GO only within the same band. */ if (wpa_s->ap_iface->current_mode->mode == ieee80211_freq_to_chan(freqs[i].freq, &chan)) policy_move = 1; } } } wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X", invalid_freq, policy_move, flags); /* * The channel is valid, or we are going to have a policy move, so * cancel timeout. */ if (!invalid_freq || policy_move) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Cancel a GO move from freq=%d MHz", freq); eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); if (wpas_p2p_in_progress(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move"); eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL); eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0, wpas_p2p_reconsider_moving_go, wpa_s, NULL); return; } } if (!invalid_freq && (!policy_move || flags != 0)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Not initiating a GO frequency change"); return; } /* * Do not consider moving GO if it is in the middle of a CSA. When the * CSA is finished this flow should be retriggered. */ if (hostapd_csa_in_progress(wpa_s->ap_iface)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Not initiating a GO frequency change - CSA is in progress"); return; } if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq)) timeout = P2P_GO_FREQ_CHANGE_TIME; else timeout = 0; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs", freq, timeout); eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL); } static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num, enum wpas_p2p_channel_update_trig trig) { struct wpa_supplicant *ifs; eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX, NULL); /* * Travers all the radio interfaces, and for each GO interface, check * if there is a need to move the GO from the frequency it is using, * or in case the frequency is valid again, cancel the evacuation flow. */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { if (ifs->current_ssid == NULL || ifs->current_ssid->mode != WPAS_MODE_P2P_GO) continue; /* * The GO was just started or completed channel switch, no need * to move it. */ if (wpa_s == ifs && (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE || trig == WPAS_P2P_CHANNEL_UPDATE_CS)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO move - schedule re-consideration"); eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0, wpas_p2p_reconsider_moving_go, wpa_s, NULL); continue; } wpas_p2p_consider_moving_one_go(ifs, freqs, num); } } void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE); } void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) { if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " "the management interface is being removed"); wpas_p2p_deinit_global(wpa_s->global); } } void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s) { if (wpa_s->ap_iface->bss) wpa_s->ap_iface->bss[0]->p2p_group = NULL; wpas_p2p_group_deinit(wpa_s); } int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int period, unsigned int interval, unsigned int count) { struct p2p_data *p2p = wpa_s->global->p2p; u8 *device_types; size_t dev_types_len; struct wpabuf *buf; int ret; if (wpa_s->p2p_lo_started) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P Listen offload is already started"); return 0; } if (wpa_s->global->p2p == NULL || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) { wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported"); return -1; } if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u", freq); return -1; } /* Get device type */ dev_types_len = (wpa_s->conf->num_sec_device_types + 1) * WPS_DEV_TYPE_LEN; device_types = os_malloc(dev_types_len); if (!device_types) return -1; os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type, wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN); /* Get Probe Response IE(s) */ buf = p2p_build_probe_resp_template(p2p, freq); if (!buf) { os_free(device_types); return -1; } ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count, device_types, dev_types_len, wpabuf_mhead_u8(buf), wpabuf_len(buf)); if (ret < 0) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to start P2P listen offload"); os_free(device_types); wpabuf_free(buf); if (ret == 0) { wpa_s->p2p_lo_started = 1; /* Stop current P2P listen if any */ wpas_stop_listen(wpa_s); } return ret; } int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s) { int ret; if (!wpa_s->p2p_lo_started) return 0; ret = wpa_drv_p2p_lo_stop(wpa_s); if (ret < 0) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to stop P2P listen offload"); wpa_s->p2p_lo_started = 0; return ret; } diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 941198e3afb0..dee9c1bcc1e1 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -1,350 +1,356 @@ /* * wpa_supplicant - P2P * Copyright (c) 2009-2010, Atheros Communications * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef P2P_SUPPLICANT_H #define P2P_SUPPLICANT_H enum p2p_wps_method; struct p2p_go_neg_results; enum p2p_send_action_result; struct p2p_peer_info; struct p2p_channels; struct wps_event_fail; struct p2ps_provision; enum wpas_p2p_channel_update_trig { WPAS_P2P_CHANNEL_UPDATE_ANY, WPAS_P2P_CHANNEL_UPDATE_DRIVER, WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE, WPAS_P2P_CHANNEL_UPDATE_AVOID, WPAS_P2P_CHANNEL_UPDATE_DISALLOW, WPAS_P2P_CHANNEL_UPDATE_CS, }; int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, const char *conf_p2p_dev); struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, const u8 *peer_dev_addr); int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, int auth, int go_intent, int freq, unsigned int vht_center_freq2, int persistent_id, int pd, int ht40, int vht, unsigned int vht_chwidth, int he, int edmg, - const u8 *group_ssid, size_t group_ssid_len); + const u8 *group_ssid, size_t group_ssid_len, + bool allow_6ghz); int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int vht_center_freq2, int ht40, int vht, - int max_oper_chwidth, int he, int edmg); + int max_oper_chwidth, int he, int edmg, bool allow_6ghz); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, int vht_center_freq2, int ht40, int vht, int max_oper_chwidth, int he, int edmg, const struct p2p_channels *channels, - int connection_timeout, int force_scan); + int connection_timeout, int force_scan, + bool allow_6ghz); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); enum wpas_p2p_prov_disc_use { WPAS_P2P_PD_FOR_GO_NEG, WPAS_P2P_PD_FOR_JOIN, WPAS_P2P_PD_AUTO, WPAS_P2P_PD_FOR_ASP }; int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *config_method, enum wpas_p2p_prov_disc_use use, struct p2ps_provision *p2ps_prov); void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, size_t data_len, enum p2p_send_action_result result); int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); enum p2p_discovery_type; int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, unsigned int search_delay, - u8 seek_cnt, const char **seek_string, int freq); + u8 seek_cnt, const char **seek_string, int freq, + bool include_6ghz); void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s); int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout); int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout); int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u8 *buf, size_t len, int p2p_group); void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs); u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, const char *svc_str, const char *info_substr); u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 version, const char *query); u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, const u8 *dst, const char *role); int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req); void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs); void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s); void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s); int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, struct wpabuf *query, struct wpabuf *resp); int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, const struct wpabuf *query); int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, const char *service); int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, const char *service); int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, const char *svc_info, const u8 *cpt_priority); int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id); void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s); int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id); void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, u16 update_indic, const u8 *tlvs, size_t tlvs_len); void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, const u8 *tlvs, size_t tlvs_len); int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, int vht_center_freq2, int ht40, int vht, int max_chwidth, - int pref_freq, int he, int edmg); + int pref_freq, int he, int edmg, bool allow_6ghz); int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, - const u8 *peer_addr, const u8 *go_dev_addr); + const u8 *peer_addr, const u8 *go_dev_addr, + bool allow_6ghz); int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, u32 interval1, u32 duration2, u32 interval2); int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period, unsigned int interval); int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len, int locally_generated); void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len, int locally_generated); int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, int duration); int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled); int wpas_p2p_cancel(struct wpa_supplicant *wpa_s); int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr); int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s); struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *ssid, size_t ssid_len); void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, u8 channel); + struct hostapd_hw_modes *mode, u8 channel, + u8 op_class); int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, u8 channel); + struct hostapd_hw_modes *mode, u8 channel, + u8 op_class); unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len); void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, int iface_addr); struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, int ndef); struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, int tag); int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s, const struct wpabuf *data, int forced_freq); int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, const struct wpabuf *req, const struct wpabuf *sel, int forced_freq); int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled); void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx); int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params); #ifdef CONFIG_P2P int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s); void wpas_p2p_deinit(struct wpa_supplicant *wpa_s); void wpas_p2p_completed(struct wpa_supplicant *wpa_s); void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, unsigned int rx_freq, int ssi_signal); void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int registrar); void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, enum wpas_p2p_channel_update_trig trig); void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, int freq_24, int freq_5, int freq_overall); void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq); void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int duration); void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq); void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s); void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s); void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s); int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s); int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s); void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s); void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s); void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s); void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s); void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s); int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s); void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int period, unsigned int interval, unsigned int count); int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s); int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s); #else /* CONFIG_P2P */ static inline int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) { return 0; } static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) { } static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, unsigned int rx_freq, int ssi_signal) { return 0; } static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int registrar) { } static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, enum wpas_p2p_channel_update_trig trig) { } static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, int freq_24, int freq_5, int freq_overall) { } static inline void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq) { } static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int duration) { } static inline void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq) { } static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) { } static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) { return 0; } static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) { return 0; } static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s) { } static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { } static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) { return 0; } static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) { return 0; } static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { } static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) { return 0; } #endif /* CONFIG_P2P */ #endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index c194806cd7f2..97a8d9a638d6 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -1,3308 +1,3309 @@ /* * WPA Supplicant - Scanning * Copyright (c) 2003-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" #include "p2p/p2p.h" #include "hs20_supplicant.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "mesh.h" static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; union wpa_event_data data; ssid = wpa_supplicant_get_ssid(wpa_s); if (ssid == NULL) return; if (wpa_s->current_ssid == NULL) { wpa_s->current_ssid = ssid; wpas_notify_network_changed(wpa_s); } wpa_supplicant_initiate_eapol(wpa_s); wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured " "network - generating associated event"); os_memset(&data, 0, sizeof(data)); wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); } #ifdef CONFIG_WPS static int wpas_wps_in_use(struct wpa_supplicant *wpa_s, enum wps_request_type *req_type) { struct wpa_ssid *ssid; int wps = 0; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) continue; wps = 1; *req_type = wpas_wps_get_req_type(ssid); if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1")) return 2; } #ifdef CONFIG_P2P if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p && !wpa_s->conf->p2p_disabled) { wpa_s->wps->dev.p2p = 1; if (!wps) { wps = 1; *req_type = WPS_REQ_ENROLLEE_INFO; } } #endif /* CONFIG_P2P */ return wps; } #endif /* CONFIG_WPS */ static int wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params *params, const u8 *mac_addr) { u8 *tmp; if (params->mac_addr) { params->mac_addr_mask = NULL; os_free(params->mac_addr); params->mac_addr = NULL; } params->mac_addr_rand = 1; if (!mac_addr) return 0; tmp = os_malloc(2 * ETH_ALEN); if (!tmp) return -1; os_memcpy(tmp, mac_addr, 2 * ETH_ALEN); params->mac_addr = tmp; params->mac_addr_mask = tmp + ETH_ALEN; return 0; } /** * wpa_supplicant_enabled_networks - Check whether there are enabled networks * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 if no networks are enabled, >0 if networks are enabled * * This function is used to figure out whether any networks (or Interworking * with enabled credentials and auto_interworking) are present in the current * configuration. */ int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->conf->ssid; int count = 0, disabled = 0; if (wpa_s->p2p_mgmt) return 0; /* no normal network profiles on p2p_mgmt interface */ while (ssid) { if (!wpas_network_disabled(wpa_s, ssid)) count++; else disabled++; ssid = ssid->next; } if (wpa_s->conf->cred && wpa_s->conf->interworking && wpa_s->conf->auto_interworking) count++; if (count == 0 && disabled > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled " "networks)", disabled); } return count; } static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { int min_temp_disabled = 0; while (ssid) { if (!wpas_network_disabled(wpa_s, ssid)) { int temp_disabled = wpas_temp_disabled(wpa_s, ssid); if (temp_disabled <= 0) break; if (!min_temp_disabled || temp_disabled < min_temp_disabled) min_temp_disabled = temp_disabled; } ssid = ssid->next; } /* ap_scan=2 mode - try to associate with each SSID. */ if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached " "end of scan list - go back to beginning"); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0); return; } if (ssid->next) { /* Continue from the next SSID on the next attempt. */ wpa_s->prev_scan_ssid = ssid; } else { /* Start from the beginning of the SSID list. */ wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; } wpa_supplicant_associate(wpa_s, NULL, ssid); } static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; struct wpa_driver_scan_params *params = work->ctx; int ret; if (deinit) { if (!work->started) { wpa_scan_free_params(params); return; } wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); wpa_s->scan_work = NULL; return; } if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) && wpa_s->wpa_state <= WPA_SCANNING) wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan); if (wpas_update_random_addr_disassoc(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to assign random MAC address for a scan"); wpa_scan_free_params(params); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); radio_work_done(work); return; } wpa_supplicant_notify_scanning(wpa_s, 1); if (wpa_s->clear_driver_scan_cache) { wpa_printf(MSG_DEBUG, "Request driver to clear scan cache due to local BSS flush"); params->only_new_results = 1; } ret = wpa_drv_scan(wpa_s, params); /* * Store the obtained vendor scan cookie (if any) in wpa_s context. * The current design is to allow only one scan request on each * interface, hence having this scan cookie stored in wpa_s context is * fine for now. * * Revisit this logic if concurrent scan operations per interface * is supported. */ if (ret == 0) wpa_s->curr_scan_cookie = params->scan_cookie; wpa_scan_free_params(params); work->ctx = NULL; if (ret) { int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ && !wpa_s->beacon_rep_data.token; if (wpa_s->disconnected) retry = 0; /* do not retry if operation is not supported */ if (ret == -EOPNOTSUPP) retry = 0; wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); if (wpa_s->wpa_state == WPA_SCANNING) wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s", ret, retry ? " retry=1" : ""); radio_work_done(work); if (retry) { /* Restore scan_req since we will try to scan again */ wpa_s->scan_req = wpa_s->last_scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); } else if (wpa_s->scan_res_handler) { /* Clear the scan_res_handler */ wpa_s->scan_res_handler = NULL; } if (wpa_s->beacon_rep_data.token) wpas_rrm_refuse_request(wpa_s); return; } os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_runs++; wpa_s->normal_scans++; wpa_s->own_scan_requested = 1; wpa_s->clear_driver_scan_cache = 0; wpa_s->scan_work = work; } /** * wpa_supplicant_trigger_scan - Request driver to start a scan * @wpa_s: Pointer to wpa_supplicant data * @params: Scan parameters * Returns: 0 on success, -1 on failure */ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { struct wpa_driver_scan_params *ctx; if (wpa_s->scan_work) { wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending"); return -1; } ctx = wpa_scan_clone_params(params); if (!ctx || radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) { wpa_scan_free_params(ctx); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); return -1; } return 0; } static void wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan"); if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_scan(wpa_s, 0, 0); } static void wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it"); wpa_s->sched_scan_timed_out = 1; wpa_supplicant_cancel_sched_scan(wpa_s); } static int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { int ret; wpa_supplicant_notify_scanning(wpa_s, 1); ret = wpa_drv_sched_scan(wpa_s, params); if (ret) wpa_supplicant_notify_scanning(wpa_s, 0); else wpa_s->sched_scanning = 1; return ret; } static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) { int ret; ret = wpa_drv_stop_sched_scan(wpa_s); if (ret) { wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!"); /* TODO: what to do if stopping fails? */ return -1; } return ret; } static struct wpa_driver_scan_filter * wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) { struct wpa_driver_scan_filter *ssids; struct wpa_ssid *ssid; size_t count; *num_ssids = 0; if (!conf->filter_ssids) return NULL; for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) { if (ssid->ssid && ssid->ssid_len) count++; } if (count == 0) return NULL; ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter)); if (ssids == NULL) return NULL; for (ssid = conf->ssid; ssid; ssid = ssid->next) { if (!ssid->ssid || !ssid->ssid_len) continue; os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len); ssids[*num_ssids].ssid_len = ssid->ssid_len; (*num_ssids)++; } return ssids; } static void wpa_supplicant_optimize_freqs( struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { #ifdef CONFIG_P2P if (params->freqs == NULL && wpa_s->p2p_in_provisioning && wpa_s->go_params) { /* Optimize provisioning state scan based on GO information */ if (wpa_s->p2p_in_provisioning < 5 && wpa_s->go_params->freq > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO " "preferred frequency %d MHz", wpa_s->go_params->freq); params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->go_params->freq; } else if (wpa_s->p2p_in_provisioning < 8 && wpa_s->go_params->freq_list[0]) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common " "channels"); int_array_concat(¶ms->freqs, wpa_s->go_params->freq_list); if (params->freqs) int_array_sort_unique(params->freqs); } wpa_s->p2p_in_provisioning++; } if (params->freqs == NULL && wpa_s->p2p_in_invitation) { /* * Optimize scan based on GO information during persistent * group reinvocation */ if (wpa_s->p2p_in_invitation < 5 && wpa_s->p2p_invite_go_freq > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation", wpa_s->p2p_invite_go_freq); params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->p2p_invite_go_freq; } wpa_s->p2p_in_invitation++; if (wpa_s->p2p_in_invitation > 20) { /* * This should not really happen since the variable is * cleared on group removal, but if it does happen, make * sure we do not get stuck in special invitation scan * mode. */ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation"); wpa_s->p2p_in_invitation = 0; } } #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { /* * Optimize post-provisioning scan based on channel used * during provisioning. */ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz " "that was used during provisioning", wpa_s->wps_freq); params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->after_wps--; } else if (wpa_s->after_wps) wpa_s->after_wps--; if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq) { /* Optimize provisioning scan based on already known channel */ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz", wpa_s->wps_freq); params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->known_wps_freq = 0; /* only do this once */ } #endif /* CONFIG_WPS */ } #ifdef CONFIG_INTERWORKING static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, struct wpabuf *buf) { wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : 1 + ETH_ALEN); wpabuf_put_u8(buf, wpa_s->conf->access_network_type); /* No Venue Info */ if (!is_zero_ether_addr(wpa_s->conf->hessid)) wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN); } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_MBO static void wpas_fils_req_param_add_max_channel(struct wpa_supplicant *wpa_s, struct wpabuf **ie) { if (wpabuf_resize(ie, 5)) { wpa_printf(MSG_DEBUG, "Failed to allocate space for FILS Request Parameters element"); return; } /* FILS Request Parameters element */ wpabuf_put_u8(*ie, WLAN_EID_EXTENSION); wpabuf_put_u8(*ie, 3); /* FILS Request attribute length */ wpabuf_put_u8(*ie, WLAN_EID_EXT_FILS_REQ_PARAMS); /* Parameter control bitmap */ wpabuf_put_u8(*ie, 0); /* Max Channel Time field - contains the value of MaxChannelTime * parameter of the MLME-SCAN.request primitive represented in units of * TUs, as an unsigned integer. A Max Channel Time field value of 255 * is used to indicate any duration of more than 254 TUs, or an * unspecified or unknown duration. (IEEE Std 802.11ai-2016, 9.4.2.178) */ wpabuf_put_u8(*ie, 255); } #endif /* CONFIG_MBO */ void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *default_ies = NULL; u8 ext_capab[18]; int ext_capab_len, frame_id; enum wpa_driver_if_type type = WPA_IF_STATION; #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) type = WPA_IF_P2P_CLIENT; #endif /* CONFIG_P2P */ wpa_drv_get_ext_capa(wpa_s, type); ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0 && wpabuf_resize(&default_ies, ext_capab_len) == 0) wpabuf_put_data(default_ies, ext_capab, ext_capab_len); #ifdef CONFIG_MBO if (wpa_s->enable_oce & OCE_STA) wpas_fils_req_param_add_max_channel(wpa_s, &default_ies); /* Send MBO and OCE capabilities */ if (wpabuf_resize(&default_ies, 12) == 0) wpas_mbo_scan_ie(wpa_s, default_ies); #endif /* CONFIG_MBO */ if (type == WPA_IF_P2P_CLIENT) frame_id = VENDOR_ELEM_PROBE_REQ_P2P; else frame_id = VENDOR_ELEM_PROBE_REQ; if (wpa_s->vendor_elem[frame_id]) { size_t len; len = wpabuf_len(wpa_s->vendor_elem[frame_id]); if (len > 0 && wpabuf_resize(&default_ies, len) == 0) wpabuf_put_buf(default_ies, wpa_s->vendor_elem[frame_id]); } if (default_ies) wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies), wpabuf_len(default_ies)); wpabuf_free(default_ies); } static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; u8 ext_capab[18]; int ext_capab_len; #ifdef CONFIG_WPS int wps = 0; enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); else #endif /* CONFIG_P2P */ wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0 && wpabuf_resize(&extra_ie, ext_capab_len) == 0) wpabuf_put_data(extra_ie, ext_capab, ext_capab_len); #ifdef CONFIG_INTERWORKING if (wpa_s->conf->interworking && wpabuf_resize(&extra_ie, 100) == 0) wpas_add_interworking_elements(wpa_s, extra_ie); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_MBO if (wpa_s->enable_oce & OCE_STA) wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie); #endif /* CONFIG_MBO */ #ifdef CONFIG_WPS wps = wpas_wps_in_use(wpa_s, &req_type); if (wps) { struct wpabuf *wps_ie; wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT, &wpa_s->wps->dev, wpa_s->wps->uuid, req_type, 0, NULL); if (wps_ie) { if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0) wpabuf_put_buf(extra_ie, wps_ie); wpabuf_free(wps_ie); } } #ifdef CONFIG_P2P if (wps) { size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); if (wpabuf_resize(&extra_ie, ielen) == 0) wpas_p2p_scan_ie(wpa_s, extra_ie); } #endif /* CONFIG_P2P */ wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie); #endif /* CONFIG_WPS */ #ifdef CONFIG_HS20 if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0) wpas_hs20_add_indication(extra_ie, -1, 0); #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST if (wpa_s->fst_ies && wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0) wpabuf_put_buf(extra_ie, wpa_s->fst_ies); #endif /* CONFIG_FST */ #ifdef CONFIG_MBO /* Send MBO and OCE capabilities */ if (wpabuf_resize(&extra_ie, 12) == 0) wpas_mbo_scan_ie(wpa_s, extra_ie); #endif /* CONFIG_MBO */ if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]; if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0) wpabuf_put_buf(extra_ie, buf); } return extra_ie; } #ifdef CONFIG_P2P /* * Check whether there are any enabled networks or credentials that could be * used for a non-P2P connection. */ static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (wpas_network_disabled(wpa_s, ssid)) continue; if (!ssid->p2p_group) return 1; } if (wpa_s->conf->cred && wpa_s->conf->interworking && wpa_s->conf->auto_interworking) return 1; return 0; } #endif /* CONFIG_P2P */ int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s, enum hostapd_hw_mode band, struct wpa_driver_scan_params *params, bool is_6ghz) { /* Include only supported channels for the specified band */ struct hostapd_hw_modes *mode; int num_chans = 0; int *freqs, i; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz); if (!mode) return -1; if (params->freqs) { while (params->freqs[num_chans]) num_chans++; } freqs = os_realloc(params->freqs, (num_chans + mode->num_channels + 1) * sizeof(int)); if (!freqs) return -1; params->freqs = freqs; for (i = 0; i < mode->num_channels; i++) { if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) continue; params->freqs[num_chans++] = mode->channels[i].freq; } params->freqs[num_chans] = 0; return 0; } static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { if (wpa_s->hw.modes == NULL) return; /* unknown what channels the driver supports */ if (params->freqs) return; /* already using a limited channel set */ if (wpa_s->setband_mask & WPA_SETBAND_5G) wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params, 0); if (wpa_s->setband_mask & WPA_SETBAND_2G) wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params, 0); if (wpa_s->setband_mask & WPA_SETBAND_6G) wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params, 1); } static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params, size_t max_ssids, const u8 *ssid, size_t ssid_len) { unsigned int j; for (j = 0; j < params->num_ssids; j++) { if (params->ssids[j].ssid_len == ssid_len && params->ssids[j].ssid && os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0) return; /* already in the list */ } if (params->num_ssids + 1 > max_ssids) { wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request"); return; } wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s", wpa_ssid_txt(ssid, ssid_len)); params->ssids[params->num_ssids].ssid = ssid; params->ssids[params->num_ssids].ssid_len = ssid_len; params->num_ssids++; } static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params, struct wpa_ssid *ssid, size_t max_ssids) { #ifdef CONFIG_OWE struct wpa_bss *bss; if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE)) return; wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { const u8 *owe, *pos, *end; const u8 *owe_ssid; size_t owe_ssid_len; if (bss->ssid_len != ssid->ssid_len || os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0) continue; owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (!owe || owe[1] < 4) continue; pos = owe + 6; end = owe + 2 + owe[1]; /* Must include BSSID and ssid_len */ if (end - pos < ETH_ALEN + 1) return; /* Skip BSSID */ pos += ETH_ALEN; owe_ssid_len = *pos++; owe_ssid = pos; if ((size_t) (end - pos) < owe_ssid_len || owe_ssid_len > SSID_MAX_LEN) return; wpa_printf(MSG_DEBUG, "OWE: scan_ssids: transition mode OWE ssid=%s", wpa_ssid_txt(owe_ssid, owe_ssid_len)); wpa_add_scan_ssid(wpa_s, params, max_ssids, owe_ssid, owe_ssid_len); return; } #endif /* CONFIG_OWE */ } static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params, size_t max_ssids) { unsigned int i; struct wpa_ssid *ssid; /* * For devices with max_ssids greater than 1, leave the last slot empty * for adding the wildcard scan entry. */ max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids; for (i = 0; i < wpa_s->scan_id_count; i++) { ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]); if (!ssid) continue; if (ssid->scan_ssid) wpa_add_scan_ssid(wpa_s, params, max_ssids, ssid->ssid, ssid->ssid_len); /* * Also add the SSID of the OWE BSS, to allow discovery of * transition mode APs more quickly. */ wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids); } wpa_s->scan_id_count = 0; } static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params, size_t max_ssids) { unsigned int i; if (wpa_s->ssids_from_scan_req == NULL || wpa_s->num_ssids_from_scan_req == 0) return 0; if (wpa_s->num_ssids_from_scan_req > max_ssids) { wpa_s->num_ssids_from_scan_req = max_ssids; wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u", (unsigned int) max_ssids); } for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) { params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid; params->ssids[i].ssid_len = wpa_s->ssids_from_scan_req[i].ssid_len; wpa_hexdump_ascii(MSG_DEBUG, "specific SSID", params->ssids[i].ssid, params->ssids[i].ssid_len); } params->num_ssids = wpa_s->num_ssids_from_scan_req; wpa_s->num_ssids_from_scan_req = 0; return 1; } static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; int ret, p2p_in_prog; struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; struct wpa_driver_scan_params *scan_params; size_t max_ssids; int connect_without_scan = 0; wpa_s->ignore_post_flush_scan_res = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); return; } if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) { wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; } if (wpa_s->scanning) { /* * If we are already in scanning state, we shall reschedule the * the incoming scan request. */ wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req"); wpa_supplicant_req_scan(wpa_s, 1, 0); return; } if (!wpa_supplicant_enabled_networks(wpa_s) && wpa_s->scan_req == NORMAL_SCAN_REQ) { wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; } if (wpa_s->conf->ap_scan != 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) { wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - " "overriding ap_scan configuration"); wpa_s->conf->ap_scan = 0; wpas_notify_ap_scan_changed(wpa_s); } if (wpa_s->conf->ap_scan == 0) { wpa_supplicant_gen_assoc_event(wpa_s); return; } ssid = NULL; if (wpa_s->scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) { connect_without_scan = 1; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == wpa_s->connect_without_scan) break; } } p2p_in_prog = wpas_p2p_in_progress(wpa_s); if (p2p_in_prog && p2p_in_prog != 2 && (!ssid || (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) { wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress"); wpa_supplicant_req_scan(wpa_s, 5, 0); return; } /* * Don't cancel the scan based on ongoing PNO; defer it. Some scans are * used for changing modes inside wpa_supplicant (roaming, * auto-reconnect, etc). Discarding the scan might hurt these processes. * The normal use case for PNO is to suspend the host immediately after * starting PNO, so the periodic 100 ms attempts to run the scan do not * normally happen in practice multiple times, i.e., this is simply * restarting scanning once the host is woken up and PNO stopped. */ if (wpa_s->pno || wpa_s->pno_sched_pending) { wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress"); wpa_supplicant_req_scan(wpa_s, 0, 100000); return; } if (wpa_s->conf->ap_scan == 2) max_ssids = 1; else { max_ssids = wpa_s->max_scan_ssids; if (max_ssids > WPAS_MAX_SCAN_SSIDS) max_ssids = WPAS_MAX_SCAN_SSIDS; } wpa_s->last_scan_req = wpa_s->scan_req; wpa_s->scan_req = NORMAL_SCAN_REQ; if (connect_without_scan) { wpa_s->connect_without_scan = NULL; if (ssid) { wpa_printf(MSG_DEBUG, "Start a pre-selected network " "without scan step"); wpa_supplicant_associate(wpa_s, NULL, ssid); return; } } os_memset(¶ms, 0, sizeof(params)); wpa_s->scan_prev_wpa_state = wpa_s->wpa_state; if (wpa_s->wpa_state == WPA_DISCONNECTED || wpa_s->wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); /* * If autoscan has set its own scanning parameters */ if (wpa_s->autoscan_params != NULL) { scan_params = wpa_s->autoscan_params; goto scan; } if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_set_ssids_from_scan_req(wpa_s, ¶ms, max_ssids)) { wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command"); goto ssid_list_set; } #ifdef CONFIG_P2P if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && wpa_s->go_params && !wpa_s->conf->passive_scan) { wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)", wpa_s->p2p_in_provisioning, wpa_s->show_group_started); params.ssids[0].ssid = wpa_s->go_params->ssid; params.ssids[0].ssid_len = wpa_s->go_params->ssid_len; params.num_ssids = 1; goto ssid_list_set; } if (wpa_s->p2p_in_invitation) { if (wpa_s->current_ssid) { wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation"); params.ssids[0].ssid = wpa_s->current_ssid->ssid; params.ssids[0].ssid_len = wpa_s->current_ssid->ssid_len; params.num_ssids = 1; } else { wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation"); } goto ssid_list_set; } #endif /* CONFIG_P2P */ /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { while (ssid) { if (ssid == wpa_s->prev_scan_ssid) { ssid = ssid->next; break; } ssid = ssid->next; } } if (wpa_s->last_scan_req != MANUAL_SCAN_REQ && #ifdef CONFIG_AP !wpa_s->ap_iface && #endif /* CONFIG_AP */ wpa_s->conf->ap_scan == 2) { wpa_s->connect_without_scan = NULL; wpa_s->prev_scan_wildcard = 0; wpa_supplicant_assoc_try(wpa_s, ssid); return; } else if (wpa_s->conf->ap_scan == 2) { /* * User-initiated scan request in ap_scan == 2; scan with * wildcard SSID. */ ssid = NULL; } else if (wpa_s->reattach && wpa_s->current_ssid != NULL) { /* * Perform single-channel single-SSID scan for * reassociate-to-same-BSS operation. */ /* Setup SSID */ ssid = wpa_s->current_ssid; wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", ssid->ssid, ssid->ssid_len); params.ssids[0].ssid = ssid->ssid; params.ssids[0].ssid_len = ssid->ssid_len; params.num_ssids = 1; /* * Allocate memory for frequency array, allocate one extra * slot for the zero-terminator. */ params.freqs = os_malloc(sizeof(int) * 2); if (params.freqs) { params.freqs[0] = wpa_s->assoc_freq; params.freqs[1] = 0; } /* * Reset the reattach flag so that we fall back to full scan if * this scan fails. */ wpa_s->reattach = 0; } else { struct wpa_ssid *start = ssid, *tssid; int freqs_set = 0; if (ssid == NULL && max_ssids > 1) ssid = wpa_s->conf->ssid; while (ssid) { if (!wpas_network_disabled(wpa_s, ssid) && ssid->scan_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", ssid->ssid, ssid->ssid_len); params.ssids[params.num_ssids].ssid = ssid->ssid; params.ssids[params.num_ssids].ssid_len = ssid->ssid_len; params.num_ssids++; if (params.num_ssids + 1 >= max_ssids) break; } if (!wpas_network_disabled(wpa_s, ssid)) { /* * Also add the SSID of the OWE BSS, to allow * discovery of transition mode APs more * quickly. */ wpa_add_owe_scan_ssid(wpa_s, ¶ms, ssid, max_ssids); } ssid = ssid->next; if (ssid == start) break; if (ssid == NULL && max_ssids > 1 && start != wpa_s->conf->ssid) ssid = wpa_s->conf->ssid; } if (wpa_s->scan_id_count && wpa_s->last_scan_req == MANUAL_SCAN_REQ) wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids); for (tssid = wpa_s->conf->ssid; wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid; tssid = tssid->next) { if (wpas_network_disabled(wpa_s, tssid)) continue; if (((params.freqs || !freqs_set) && tssid->scan_freq) && int_array_len(params.freqs) < 100) { int_array_concat(¶ms.freqs, tssid->scan_freq); } else { os_free(params.freqs); params.freqs = NULL; } freqs_set = 1; } int_array_sort_unique(params.freqs); } if (ssid && max_ssids == 1) { /* * If the driver is limited to 1 SSID at a time interleave * wildcard SSID scans with specific SSID scans to avoid * waiting a long time for a wildcard scan. */ if (!wpa_s->prev_scan_wildcard) { params.ssids[0].ssid = NULL; params.ssids[0].ssid_len = 0; wpa_s->prev_scan_wildcard = 1; wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for " "wildcard SSID (Interleave with specific)"); } else { wpa_s->prev_scan_ssid = ssid; wpa_s->prev_scan_wildcard = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific SSID: %s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); } } else if (ssid) { /* max_ssids > 1 */ wpa_s->prev_scan_ssid = ssid; wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " "the scan request"); params.num_ssids++; } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_passive && params.num_ssids == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request"); } else if (wpa_s->conf->passive_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on configuration"); } else { wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; params.num_ssids++; wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " "SSID"); } ssid_list_set: wpa_supplicant_optimize_freqs(wpa_s, ¶ms); extra_ie = wpa_supplicant_extra_ies(wpa_s); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_only_new) { wpa_printf(MSG_DEBUG, "Request driver to clear scan cache due to manual only_new=1 scan"); params.only_new_results = 1; } if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL && wpa_s->manual_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels"); params.freqs = wpa_s->manual_scan_freqs; wpa_s->manual_scan_freqs = NULL; } if (params.freqs == NULL && wpa_s->select_network_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Limit select_network scan to specified channels"); params.freqs = wpa_s->select_network_scan_freqs; wpa_s->select_network_scan_freqs = NULL; } if (params.freqs == NULL && wpa_s->next_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously " "generated frequency list"); params.freqs = wpa_s->next_scan_freqs; } else os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; wpa_setband_scan_freqs(wpa_s, ¶ms); /* See if user specified frequencies. If so, scan only those. */ if (wpa_s->last_scan_req == INITIAL_SCAN_REQ && wpa_s->conf->initial_freq_list && !params.freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on conf->initial_freq_list"); int_array_concat(¶ms.freqs, wpa_s->conf->initial_freq_list); } else if (wpa_s->conf->freq_list && !params.freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on conf->freq_list"); int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); } /* Use current associated channel? */ if (wpa_s->conf->scan_cur_freq && !params.freqs) { unsigned int num = wpa_s->num_multichan_concurrent; params.freqs = os_calloc(num + 1, sizeof(int)); if (params.freqs) { num = get_shared_radio_freqs(wpa_s, params.freqs, num); if (num > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the " "current operating channels since " "scan_cur_freq is enabled"); } else { os_free(params.freqs); params.freqs = NULL; } } } #ifdef CONFIG_MBO if (wpa_s->enable_oce & OCE_STA) params.oce_scan = 1; #endif /* CONFIG_MBO */ params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); if (extra_ie) { params.extra_ies = wpabuf_head(extra_ie); params.extra_ies_len = wpabuf_len(extra_ie); } #ifdef CONFIG_P2P if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation || (wpa_s->show_group_started && wpa_s->go_params)) { /* * The interface may not yet be in P2P mode, so we have to * explicitly request P2P probe to disable CCK rates. */ params.p2p_probe = 1; } #endif /* CONFIG_P2P */ if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) && wpa_s->wpa_state <= WPA_SCANNING) wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_scan); if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) { struct wpa_bss *bss; params.bssid = wpa_s->next_scan_bssid; bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid); if (!wpa_s->next_scan_bssid_wildcard_ssid && bss && bss->ssid_len && params.num_ssids == 1 && params.ssids[0].ssid_len == 0) { params.ssids[0].ssid = bss->ssid; params.ssids[0].ssid_len = bss->ssid_len; wpa_dbg(wpa_s, MSG_DEBUG, "Scan a previously specified BSSID " MACSTR " and SSID %s", MAC2STR(params.bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len)); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Scan a previously specified BSSID " MACSTR, MAC2STR(params.bssid)); } } scan_params = ¶ms; scan: #ifdef CONFIG_P2P /* * If the driver does not support multi-channel concurrency and a * virtual interface that shares the same radio with the wpa_s interface * is operating there may not be need to scan other channels apart from * the current operating channel on the other virtual interface. Filter * out other channels in case we are trying to find a connection for a * station interface when we are not configured to prefer station * connection and a concurrent operation is already in process. */ if (wpa_s->scan_for_connection && wpa_s->last_scan_req == NORMAL_SCAN_REQ && !scan_params->freqs && !params.freqs && wpas_is_p2p_prioritized(wpa_s) && wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && non_p2p_network_enabled(wpa_s)) { unsigned int num = wpa_s->num_multichan_concurrent; params.freqs = os_calloc(num + 1, sizeof(int)); if (params.freqs) { num = get_shared_radio_freqs(wpa_s, params.freqs, num); if (num > 0 && num == wpa_s->num_multichan_concurrent) { wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used"); } else { os_free(params.freqs); params.freqs = NULL; } } } #endif /* CONFIG_P2P */ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params); if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs && !wpa_s->manual_scan_freqs) { /* Restore manual_scan_freqs for the next attempt */ wpa_s->manual_scan_freqs = params.freqs; params.freqs = NULL; } wpabuf_free(extra_ie); os_free(params.freqs); os_free(params.filter_ssids); os_free(params.mac_addr); if (ret) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan"); if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state) wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); /* Restore scan_req since we will try to scan again */ wpa_s->scan_req = wpa_s->last_scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); } else { wpa_s->scan_for_connection = 0; #ifdef CONFIG_INTERWORKING wpa_s->interworking_fast_assoc_tried = 0; #endif /* CONFIG_INTERWORKING */ wpa_s->next_scan_bssid_wildcard_ssid = 0; if (params.bssid) os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN); } } void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec) { struct os_reltime remaining, new_int; int cancelled; cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL, &remaining); new_int.sec = sec; new_int.usec = 0; if (cancelled && os_reltime_before(&remaining, &new_int)) { new_int.sec = remaining.sec; new_int.usec = remaining.usec; } if (cancelled) { eloop_register_timeout(new_int.sec, new_int.usec, wpa_supplicant_scan, wpa_s, NULL); } wpa_s->scan_interval = sec; } /** * wpa_supplicant_req_scan - Schedule a scan for neighboring access points * @wpa_s: Pointer to wpa_supplicant data * @sec: Number of seconds after which to scan * @usec: Number of microseconds after which to scan * * This function is used to schedule a scan for neighboring access points after * the specified time. */ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { int res; if (wpa_s->p2p_mgmt) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan request (%d.%06d sec) on p2p_mgmt interface", sec, usec); return; } res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); if (res == 1) { wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec", sec, usec); } else if (res == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner", sec, usec); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec", sec, usec); eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); } } /** * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan * @wpa_s: Pointer to wpa_supplicant data * @sec: Number of seconds after which to scan * @usec: Number of microseconds after which to scan * Returns: 0 on success or -1 otherwise * * This function is used to schedule periodic scans for neighboring * access points after the specified time. */ int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { if (!wpa_s->sched_scan_supported) return -1; eloop_register_timeout(sec, usec, wpa_supplicant_delayed_sched_scan_timeout, wpa_s, NULL); return 0; } static void wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { if (wpa_s->wpa_state != WPA_COMPLETED || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) || wpa_s->srp.relative_rssi_set == 0) return; params->relative_rssi_set = 1; params->relative_rssi = wpa_s->srp.relative_rssi; if (wpa_s->srp.relative_adjust_rssi == 0) return; params->relative_adjust_band = wpa_s->srp.relative_adjust_band; params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi; } /** * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 is sched_scan was started or -1 otherwise * * This function is used to schedule periodic scans for neighboring * access points repeating the scan continuously. */ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) { struct wpa_driver_scan_params params; struct wpa_driver_scan_params *scan_params; enum wpa_states prev_state; struct wpa_ssid *ssid = NULL; struct wpabuf *extra_ie = NULL; int ret; unsigned int max_sched_scan_ssids; int wildcard = 0; int need_ssids; struct sched_scan_plan scan_plan; if (!wpa_s->sched_scan_supported) return -1; if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; else max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload) return -1; wpa_s->sched_scan_stop_req = 0; if (wpa_s->sched_scanning) { wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning"); return 0; } need_ssids = 0; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) { /* Use wildcard SSID to find this network */ wildcard = 1; } else if (!wpas_network_disabled(wpa_s, ssid) && ssid->ssid_len) need_ssids++; #ifdef CONFIG_WPS if (!wpas_network_disabled(wpa_s, ssid) && ssid->key_mgmt == WPA_KEY_MGMT_WPS) { /* * Normal scan is more reliable and faster for WPS * operations and since these are for short periods of * time, the benefit of trying to use sched_scan would * be limited. */ wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " "sched_scan for WPS"); return -1; } #endif /* CONFIG_WPS */ } if (wildcard) need_ssids++; if (wpa_s->normal_scans < 3 && (need_ssids <= wpa_s->max_scan_ssids || wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) { /* * When normal scan can speed up operations, use that for the * first operations before starting the sched_scan to allow * user space sleep more. We do this only if the normal scan * has functionality that is suitable for this or if the * sched_scan does not have better support for multiple SSIDs. */ wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " "sched_scan for initial scans (normal_scans=%d)", wpa_s->normal_scans); return -1; } os_memset(¶ms, 0, sizeof(params)); /* If we can't allocate space for the filters, we just don't filter */ params.filter_ssids = os_calloc(wpa_s->max_match_sets, sizeof(struct wpa_driver_scan_filter)); prev_state = wpa_s->wpa_state; if (wpa_s->wpa_state == WPA_DISCONNECTED || wpa_s->wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); if (wpa_s->autoscan_params != NULL) { scan_params = wpa_s->autoscan_params; goto scan; } /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_sched_ssid) { while (ssid) { if (ssid == wpa_s->prev_sched_ssid) { ssid = ssid->next; break; } ssid = ssid->next; } } if (!ssid || !wpa_s->prev_sched_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; wpa_s->first_sched_scan = 1; ssid = wpa_s->conf->ssid; wpa_s->prev_sched_ssid = ssid; } if (wildcard) { wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan"); params.num_ssids++; } while (ssid) { if (wpas_network_disabled(wpa_s, ssid)) goto next; if (params.num_filter_ssids < wpa_s->max_match_sets && params.filter_ssids && ssid->ssid && ssid->ssid_len) { wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid, ssid->ssid, ssid->ssid_len); params.filter_ssids[params.num_filter_ssids].ssid_len = ssid->ssid_len; params.num_filter_ssids++; } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len) { wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID " "filter for sched_scan - drop filter"); os_free(params.filter_ssids); params.filter_ssids = NULL; params.num_filter_ssids = 0; } if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) { if (params.num_ssids == max_sched_scan_ssids) break; /* only room for broadcast SSID */ wpa_dbg(wpa_s, MSG_DEBUG, "add to active scan ssid: %s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); params.ssids[params.num_ssids].ssid = ssid->ssid; params.ssids[params.num_ssids].ssid_len = ssid->ssid_len; params.num_ssids++; if (params.num_ssids >= max_sched_scan_ssids) { wpa_s->prev_sched_ssid = ssid; do { ssid = ssid->next; } while (ssid && (wpas_network_disabled(wpa_s, ssid) || !ssid->scan_ssid)); break; } } next: wpa_s->prev_sched_ssid = ssid; ssid = ssid->next; } if (params.num_filter_ssids == 0) { os_free(params.filter_ssids); params.filter_ssids = NULL; } extra_ie = wpa_supplicant_extra_ies(wpa_s); if (extra_ie) { params.extra_ies = wpabuf_head(extra_ie); params.extra_ies_len = wpabuf_len(extra_ie); } if (wpa_s->conf->filter_rssi) params.filter_rssi = wpa_s->conf->filter_rssi; /* See if user specified frequencies. If so, scan only those. */ if (wpa_s->conf->freq_list && !params.freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on conf->freq_list"); int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); } #ifdef CONFIG_MBO if (wpa_s->enable_oce & OCE_STA) params.oce_scan = 1; #endif /* CONFIG_MBO */ scan_params = ¶ms; scan: wpa_s->sched_scan_timed_out = 0; /* * We cannot support multiple scan plans if the scan request includes * too many SSID's, so in this case use only the last scan plan and make * it run infinitely. It will be stopped by the timeout. */ if (wpa_s->sched_scan_plans_num == 1 || (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) { params.sched_scan_plans = wpa_s->sched_scan_plans; params.sched_scan_plans_num = wpa_s->sched_scan_plans_num; } else if (wpa_s->sched_scan_plans_num > 1) { wpa_dbg(wpa_s, MSG_DEBUG, "Too many SSIDs. Default to using single scheduled_scan plan"); params.sched_scan_plans = &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num - 1]; params.sched_scan_plans_num = 1; } else { if (wpa_s->conf->sched_scan_interval) scan_plan.interval = wpa_s->conf->sched_scan_interval; else scan_plan.interval = 10; if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) { wpa_printf(MSG_WARNING, "Scan interval too long(%u), use the maximum allowed(%u)", scan_plan.interval, wpa_s->max_sched_scan_plan_interval); scan_plan.interval = wpa_s->max_sched_scan_plan_interval; } scan_plan.iterations = 0; params.sched_scan_plans = &scan_plan; params.sched_scan_plans_num = 1; } params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay; if (ssid || !wpa_s->first_sched_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan after %u seconds: interval %u timeout %d", params.sched_scan_start_delay, params.sched_scan_plans[0].interval, wpa_s->sched_scan_timeout); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan after %u seconds (no timeout)", params.sched_scan_start_delay); } wpa_setband_scan_freqs(wpa_s, scan_params); if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) && wpa_s->wpa_state <= WPA_SCANNING) wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_sched_scan); wpa_scan_set_relative_rssi_params(wpa_s, scan_params); ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params); wpabuf_free(extra_ie); os_free(params.filter_ssids); os_free(params.mac_addr); if (ret) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan"); if (prev_state != wpa_s->wpa_state) wpa_supplicant_set_state(wpa_s, prev_state); return ret; } /* If we have more SSIDs to scan, add a timeout so we scan them too */ if (ssid || !wpa_s->first_sched_scan) { wpa_s->sched_scan_timed_out = 0; eloop_register_timeout(wpa_s->sched_scan_timeout, 0, wpa_supplicant_sched_scan_timeout, wpa_s, NULL); wpa_s->first_sched_scan = 0; wpa_s->sched_scan_timeout /= 2; params.sched_scan_plans[0].interval *= 2; if ((unsigned int) wpa_s->sched_scan_timeout < params.sched_scan_plans[0].interval || params.sched_scan_plans[0].interval > wpa_s->max_sched_scan_plan_interval) { params.sched_scan_plans[0].interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; } } /* If there is no more ssids, start next time from the beginning */ if (!ssid) wpa_s->prev_sched_ssid = NULL; return 0; } /** * wpa_supplicant_cancel_scan - Cancel a scheduled scan request * @wpa_s: Pointer to wpa_supplicant data * * This function is used to cancel a scan request scheduled with * wpa_supplicant_req_scan(). */ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request"); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); } /** * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan * @wpa_s: Pointer to wpa_supplicant data * * This function is used to stop a delayed scheduled scan. */ void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s) { if (!wpa_s->sched_scan_supported) return; wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan"); eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout, wpa_s, NULL); } /** * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans * @wpa_s: Pointer to wpa_supplicant data * * This function is used to stop a periodic scheduled scan. */ void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s) { if (!wpa_s->sched_scanning) return; if (wpa_s->sched_scanning) wpa_s->sched_scan_stop_req = 1; wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan"); eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL); wpa_supplicant_stop_sched_scan(wpa_s); } /** * wpa_supplicant_notify_scanning - Indicate possible scan state change * @wpa_s: Pointer to wpa_supplicant data * @scanning: Whether scanning is currently in progress * * This function is to generate scanning notifycations. It is called whenever * there may have been a change in scanning (scan started, completed, stopped). * wpas_notify_scanning() is called whenever the scanning state changed from the * previously notified state. */ void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning) { if (wpa_s->scanning != scanning) { wpa_s->scanning = scanning; wpas_notify_scanning(wpa_s); } } static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) { int rate = 0; const u8 *ie; int i; ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES); for (i = 0; ie && i < ie[1]; i++) { if ((ie[i + 2] & 0x7f) > rate) rate = ie[i + 2] & 0x7f; } ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES); for (i = 0; ie && i < ie[1]; i++) { if ((ie[i + 2] & 0x7f) > rate) rate = ie[i + 2] & 0x7f; } return rate; } /** * wpa_scan_get_ie - Fetch a specified information element from a scan result * @res: Scan result entry * @ie: Information element identitifier (WLAN_EID_*) * Returns: Pointer to the information element (id field) or %NULL if not found * * This function returns the first matching information element in the scan * result. */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { size_t ie_len = res->ie_len; /* Use the Beacon frame IEs if res->ie_len is not available */ if (!ie_len) ie_len = res->beacon_ie_len; return get_ie((const u8 *) (res + 1), ie_len, ie); } /** * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result * @res: Scan result entry * @vendor_type: Vendor type (four octets starting the IE payload) * Returns: Pointer to the information element (id field) or %NULL if not found * * This function returns the first matching information element in the scan * result. */ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type) { const u8 *ies; const struct element *elem; ies = (const u8 *) (res + 1); for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, res->ie_len) { if (elem->datalen >= 4 && vendor_type == WPA_GET_BE32(elem->data)) return &elem->id; } return NULL; } /** * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result * @res: Scan result entry * @vendor_type: Vendor type (four octets starting the IE payload) * Returns: Pointer to the information element (id field) or %NULL if not found * * This function returns the first matching information element in the scan * result. * * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only * from Beacon frames instead of either Beacon or Probe Response frames. */ const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res, u32 vendor_type) { const u8 *ies; const struct element *elem; if (res->beacon_ie_len == 0) return NULL; ies = (const u8 *) (res + 1); ies += res->ie_len; for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, res->beacon_ie_len) { if (elem->datalen >= 4 && vendor_type == WPA_GET_BE32(elem->data)) return &elem->id; } return NULL; } /** * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result * @res: Scan result entry * @vendor_type: Vendor type (four octets starting the IE payload) * Returns: Pointer to the information element payload or %NULL if not found * * This function returns concatenated payload of possibly fragmented vendor * specific information elements in the scan result. The caller is responsible * for freeing the returned buffer. */ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, u32 vendor_type) { struct wpabuf *buf; const u8 *end, *pos; buf = wpabuf_alloc(res->ie_len); if (buf == NULL) return NULL; pos = (const u8 *) (res + 1); end = pos + res->ie_len; while (end - pos > 1) { u8 ie, len; ie = pos[0]; len = pos[1]; if (len > end - pos - 2) break; pos += 2; if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 && vendor_type == WPA_GET_BE32(pos)) wpabuf_put_data(buf, pos + 4, len - 4); pos += len; } if (wpabuf_len(buf) == 0) { wpabuf_free(buf); buf = NULL; } return buf; } /* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { #define MIN(a,b) a < b ? a : b struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int wpa_a, wpa_b; int snr_a, snr_b, snr_a_full, snr_b_full; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; if (wpa_b && !wpa_a) return 1; if (!wpa_b && wpa_a) return -1; /* privacy support preferred */ if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && (wb->caps & IEEE80211_CAP_PRIVACY)) return 1; if ((wa->caps & IEEE80211_CAP_PRIVACY) && (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) { snr_a_full = wa->snr; snr_a = MIN(wa->snr, GREAT_SNR); snr_b_full = wb->snr; snr_b = MIN(wb->snr, GREAT_SNR); } else { /* Level is not in dBm, so we can't calculate * SNR. Just use raw level (units unknown). */ snr_a = snr_a_full = wa->level; snr_b = snr_b_full = wb->level; } /* If SNR is close, decide by max rate or frequency band. For cases * involving the 6 GHz band, use the throughput estimate irrespective * of the SNR difference since the LPI/VLP rules may result in * significant differences in SNR for cases where the estimated * throughput can be considerably higher with the lower SNR. */ if (snr_a && snr_b && (abs(snr_b - snr_a) < 7 || is_6ghz_freq(wa->freq) || is_6ghz_freq(wb->freq))) { if (wa->est_throughput != wb->est_throughput) return (int) wb->est_throughput - (int) wa->est_throughput; } if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { if (is_6ghz_freq(wa->freq) ^ is_6ghz_freq(wb->freq)) return is_6ghz_freq(wa->freq) ? -1 : 1; if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq)) return IS_5GHZ(wa->freq) ? -1 : 1; } /* all things being equal, use SNR; if SNRs are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ if (snr_b_full == snr_a_full) return wb->qual - wa->qual; return snr_b_full - snr_a_full; #undef MIN } #ifdef CONFIG_WPS /* Compare function for sorting scan results when searching a WPS AP for * provisioning. Return >0 if @b is considered better. */ static int wpa_scan_result_wps_compar(const void *a, const void *b) { struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int uses_wps_a, uses_wps_b; struct wpabuf *wps_a, *wps_b; int res; /* Optimization - check WPS IE existence before allocated memory and * doing full reassembly. */ uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL; uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL; if (uses_wps_a && !uses_wps_b) return -1; if (!uses_wps_a && uses_wps_b) return 1; if (uses_wps_a && uses_wps_b) { wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE); wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE); res = wps_ap_priority_compar(wps_a, wps_b); wpabuf_free(wps_a); wpabuf_free(wps_b); if (res) return res; } /* * Do not use current AP security policy as a sorting criteria during * WPS provisioning step since the AP may get reconfigured at the * completion of provisioning. */ /* all things being equal, use signal level; if signal levels are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ if (wb->level == wa->level) return wb->qual - wa->qual; return wb->level - wa->level; } #endif /* CONFIG_WPS */ static void dump_scan_res(struct wpa_scan_results *scan_res) { #ifndef CONFIG_NO_STDOUT_DEBUG size_t i; if (scan_res->res == NULL || scan_res->num == 0) return; wpa_printf(MSG_EXCESSIVE, "Sorted scan results"); for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *r = scan_res->res[i]; u8 *pos; if (r->flags & WPA_SCAN_LEVEL_DBM) { int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID); wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u", MAC2STR(r->bssid), r->freq, r->qual, r->noise, noise_valid ? "" : "~", r->level, r->snr, r->snr >= GREAT_SNR ? "*" : "", r->flags, r->age, r->est_throughput); } else { wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " "noise=%d level=%d flags=0x%x age=%u est=%u", MAC2STR(r->bssid), r->freq, r->qual, r->noise, r->level, r->flags, r->age, r->est_throughput); } pos = (u8 *) (r + 1); if (r->ie_len) wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len); pos += r->ie_len; if (r->beacon_ie_len) wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs", pos, r->beacon_ie_len); } #endif /* CONFIG_NO_STDOUT_DEBUG */ } /** * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed * @wpa_s: Pointer to wpa_supplicant data * @bssid: BSSID to check * Returns: 0 if the BSSID is filtered or 1 if not * * This function is used to filter out specific BSSIDs from scan reslts mainly * for testing purposes (SET bssid_filter ctrl_iface command). */ int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid) { size_t i; if (wpa_s->bssid_filter == NULL) return 1; for (i = 0; i < wpa_s->bssid_filter_count; i++) { if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid, ETH_ALEN) == 0) return 1; } return 0; } void filter_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_results *res) { size_t i, j; if (wpa_s->bssid_filter == NULL) return; for (i = 0, j = 0; i < res->num; i++) { if (wpa_supplicant_filter_bssid_match(wpa_s, res->res[i]->bssid)) { res->res[j++] = res->res[i]; } else { os_free(res->res[i]); res->res[i] = NULL; } } if (res->num != j) { wpa_printf(MSG_DEBUG, "Filtered out %d scan results", (int) (res->num - j)); res->num = j; } } void scan_snr(struct wpa_scan_res *res) { if (res->flags & WPA_SCAN_NOISE_INVALID) { res->noise = is_6ghz_freq(res->freq) ? DEFAULT_NOISE_FLOOR_6GHZ : (IS_5GHZ(res->freq) ? DEFAULT_NOISE_FLOOR_5GHZ : DEFAULT_NOISE_FLOOR_2GHZ); } if (res->flags & WPA_SCAN_LEVEL_DBM) { res->snr = res->level - res->noise; } else { /* Level is not in dBm, so we can't calculate * SNR. Just use raw level (units unknown). */ res->snr = res->level; } } /* Minimum SNR required to achieve a certain bitrate. */ struct minsnr_bitrate_entry { int minsnr; unsigned int bitrate; /* in Mbps */ }; /* VHT needs to be enabled in order to achieve MCS8 and MCS9 rates. */ static const int vht_mcs = 8; static const struct minsnr_bitrate_entry vht20_table[] = { { 0, 0 }, { 2, 6500 }, /* HT20 MCS0 */ { 5, 13000 }, /* HT20 MCS1 */ { 9, 19500 }, /* HT20 MCS2 */ { 11, 26000 }, /* HT20 MCS3 */ { 15, 39000 }, /* HT20 MCS4 */ { 18, 52000 }, /* HT20 MCS5 */ { 20, 58500 }, /* HT20 MCS6 */ { 25, 65000 }, /* HT20 MCS7 */ { 29, 78000 }, /* VHT20 MCS8 */ { -1, 78000 } /* SNR > 29 */ }; static const struct minsnr_bitrate_entry vht40_table[] = { { 0, 0 }, { 5, 13500 }, /* HT40 MCS0 */ { 8, 27000 }, /* HT40 MCS1 */ { 12, 40500 }, /* HT40 MCS2 */ { 14, 54000 }, /* HT40 MCS3 */ { 18, 81000 }, /* HT40 MCS4 */ { 21, 108000 }, /* HT40 MCS5 */ { 23, 121500 }, /* HT40 MCS6 */ { 28, 135000 }, /* HT40 MCS7 */ { 32, 162000 }, /* VHT40 MCS8 */ { 34, 180000 }, /* VHT40 MCS9 */ { -1, 180000 } /* SNR > 34 */ }; static const struct minsnr_bitrate_entry vht80_table[] = { { 0, 0 }, { 8, 29300 }, /* VHT80 MCS0 */ { 11, 58500 }, /* VHT80 MCS1 */ { 15, 87800 }, /* VHT80 MCS2 */ { 17, 117000 }, /* VHT80 MCS3 */ { 21, 175500 }, /* VHT80 MCS4 */ { 24, 234000 }, /* VHT80 MCS5 */ { 26, 263300 }, /* VHT80 MCS6 */ { 31, 292500 }, /* VHT80 MCS7 */ { 35, 351000 }, /* VHT80 MCS8 */ { 37, 390000 }, /* VHT80 MCS9 */ { -1, 390000 } /* SNR > 37 */ }; static const struct minsnr_bitrate_entry vht160_table[] = { { 0, 0 }, { 11, 58500 }, /* VHT160 MCS0 */ { 14, 117000 }, /* VHT160 MCS1 */ { 18, 175500 }, /* VHT160 MCS2 */ { 20, 234000 }, /* VHT160 MCS3 */ { 24, 351000 }, /* VHT160 MCS4 */ { 27, 468000 }, /* VHT160 MCS5 */ { 29, 526500 }, /* VHT160 MCS6 */ { 34, 585000 }, /* VHT160 MCS7 */ { 38, 702000 }, /* VHT160 MCS8 */ { 40, 780000 }, /* VHT160 MCS9 */ { -1, 780000 } /* SNR > 37 */ }; static const struct minsnr_bitrate_entry he20_table[] = { { 0, 0 }, { 2, 8600 }, /* HE20 MCS0 */ { 5, 17200 }, /* HE20 MCS1 */ { 9, 25800 }, /* HE20 MCS2 */ { 11, 34400 }, /* HE20 MCS3 */ { 15, 51600 }, /* HE20 MCS4 */ { 18, 68800 }, /* HE20 MCS5 */ { 20, 77400 }, /* HE20 MCS6 */ { 25, 86000 }, /* HE20 MCS7 */ { 29, 103200 }, /* HE20 MCS8 */ { 31, 114700 }, /* HE20 MCS9 */ { 34, 129000 }, /* HE20 MCS10 */ { 36, 143400 }, /* HE20 MCS11 */ { -1, 143400 } /* SNR > 29 */ }; static const struct minsnr_bitrate_entry he40_table[] = { { 0, 0 }, { 5, 17200 }, /* HE40 MCS0 */ { 8, 34400 }, /* HE40 MCS1 */ { 12, 51600 }, /* HE40 MCS2 */ { 14, 68800 }, /* HE40 MCS3 */ { 18, 103200 }, /* HE40 MCS4 */ { 21, 137600 }, /* HE40 MCS5 */ { 23, 154900 }, /* HE40 MCS6 */ { 28, 172100 }, /* HE40 MCS7 */ { 32, 206500 }, /* HE40 MCS8 */ { 34, 229400 }, /* HE40 MCS9 */ { 37, 258100 }, /* HE40 MCS10 */ { 39, 286800 }, /* HE40 MCS11 */ { -1, 286800 } /* SNR > 34 */ }; static const struct minsnr_bitrate_entry he80_table[] = { { 0, 0 }, { 8, 36000 }, /* HE80 MCS0 */ { 11, 72100 }, /* HE80 MCS1 */ { 15, 108100 }, /* HE80 MCS2 */ { 17, 144100 }, /* HE80 MCS3 */ { 21, 216200 }, /* HE80 MCS4 */ { 24, 288200 }, /* HE80 MCS5 */ { 26, 324300 }, /* HE80 MCS6 */ { 31, 360300 }, /* HE80 MCS7 */ { 35, 432400 }, /* HE80 MCS8 */ { 37, 480400 }, /* HE80 MCS9 */ { 40, 540400 }, /* HE80 MCS10 */ { 42, 600500 }, /* HE80 MCS11 */ { -1, 600500 } /* SNR > 37 */ }; static const struct minsnr_bitrate_entry he160_table[] = { { 0, 0 }, { 11, 72100 }, /* HE160 MCS0 */ { 14, 144100 }, /* HE160 MCS1 */ { 18, 216200 }, /* HE160 MCS2 */ { 20, 288200 }, /* HE160 MCS3 */ { 24, 432400 }, /* HE160 MCS4 */ { 27, 576500 }, /* HE160 MCS5 */ { 29, 648500 }, /* HE160 MCS6 */ { 34, 720600 }, /* HE160 MCS7 */ { 38, 864700 }, /* HE160 MCS8 */ { 40, 960800 }, /* HE160 MCS9 */ { 43, 1080900 }, /* HE160 MCS10 */ { 45, 1201000 }, /* HE160 MCS11 */ { -1, 1201000 } /* SNR > 37 */ }; static unsigned int interpolate_rate(int snr, int snr0, int snr1, int rate0, int rate1) { return rate0 + (snr - snr0) * (rate1 - rate0) / (snr1 - snr0); } static unsigned int max_rate(const struct minsnr_bitrate_entry table[], int snr, bool vht) { const struct minsnr_bitrate_entry *prev, *entry = table; while ((entry->minsnr != -1) && (snr >= entry->minsnr) && (vht || entry - table <= vht_mcs)) entry++; if (entry == table) return entry->bitrate; prev = entry - 1; if (entry->minsnr == -1 || (!vht && entry - table > vht_mcs)) return prev->bitrate; return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate, entry->bitrate); } static unsigned int max_ht20_rate(int snr, bool vht) { return max_rate(vht20_table, snr, vht); } static unsigned int max_ht40_rate(int snr, bool vht) { return max_rate(vht40_table, snr, vht); } static unsigned int max_vht80_rate(int snr) { return max_rate(vht80_table, snr, 1); } static unsigned int max_vht160_rate(int snr) { return max_rate(vht160_table, snr, 1); } static unsigned int max_he_rate(const struct minsnr_bitrate_entry table[], int snr) { const struct minsnr_bitrate_entry *prev, *entry = table; while (entry->minsnr != -1 && snr >= entry->minsnr) entry++; if (entry == table) return 0; prev = entry - 1; if (entry->minsnr == -1) return prev->bitrate; return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate, entry->bitrate); } unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len, int rate, int snr, int freq) { struct hostapd_hw_modes *hw_mode; unsigned int est, tmp; const u8 *ie; /* Limit based on estimated SNR */ if (rate > 1 * 2 && snr < 1) rate = 1 * 2; else if (rate > 2 * 2 && snr < 4) rate = 2 * 2; else if (rate > 6 * 2 && snr < 5) rate = 6 * 2; else if (rate > 9 * 2 && snr < 6) rate = 9 * 2; else if (rate > 12 * 2 && snr < 7) rate = 12 * 2; else if (rate > 12 * 2 && snr < 8) rate = 14 * 2; else if (rate > 12 * 2 && snr < 9) rate = 16 * 2; else if (rate > 18 * 2 && snr < 10) rate = 18 * 2; else if (rate > 24 * 2 && snr < 11) rate = 24 * 2; else if (rate > 24 * 2 && snr < 12) rate = 27 * 2; else if (rate > 24 * 2 && snr < 13) rate = 30 * 2; else if (rate > 24 * 2 && snr < 14) rate = 33 * 2; else if (rate > 36 * 2 && snr < 15) rate = 36 * 2; else if (rate > 36 * 2 && snr < 16) rate = 39 * 2; else if (rate > 36 * 2 && snr < 17) rate = 42 * 2; else if (rate > 36 * 2 && snr < 18) rate = 45 * 2; else if (rate > 48 * 2 && snr < 19) rate = 48 * 2; else if (rate > 48 * 2 && snr < 20) rate = 51 * 2; else if (rate > 54 * 2 && snr < 21) rate = 54 * 2; est = rate * 500; hw_mode = get_mode_with_freq(wpa_s->hw.modes, wpa_s->hw.num_modes, freq); if (hw_mode && hw_mode->ht_capab) { ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP); if (ie) { tmp = max_ht20_rate(snr, false); if (tmp > est) est = tmp; } } if (hw_mode && (hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); if (ie && ie[1] >= 2 && (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) { tmp = max_ht40_rate(snr, false); if (tmp > est) est = tmp; } } if (hw_mode && hw_mode->vht_capab) { /* Use +1 to assume VHT is always faster than HT */ ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP); if (ie) { bool vht80 = false, vht160 = false; tmp = max_ht20_rate(snr, true) + 1; if (tmp > est) est = tmp; ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); if (ie && ie[1] >= 2 && (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) { tmp = max_ht40_rate(snr, true) + 1; if (tmp > est) est = tmp; } /* Determine VHT BSS bandwidth based on IEEE Std * 802.11-2020, Table 11-23 (VHT BSs bandwidth) */ ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION); if (ie && ie[1] >= 3) { u8 cw = ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK; u8 seg0 = ie[3]; u8 seg1 = ie[4]; if (cw) vht80 = true; if (cw == 2 || (cw == 3 && (seg1 > 0 && abs(seg1 - seg0) == 16))) vht160 = true; if (cw == 1 && ((seg1 > 0 && abs(seg1 - seg0) == 8) || (seg1 > 0 && abs(seg1 - seg0) == 16))) vht160 = true; } if (vht80) { tmp = max_vht80_rate(snr) + 1; if (tmp > est) est = tmp; } if (vht160 && (hw_mode->vht_capab & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { tmp = max_vht160_rate(snr) + 1; if (tmp > est) est = tmp; } } } if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) { /* Use +2 to assume HE is always faster than HT/VHT */ struct ieee80211_he_capabilities *he; struct he_capabilities *own_he; u8 cw; ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES); if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN)) return est; he = (struct ieee80211_he_capabilities *) &ie[3]; own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA]; tmp = max_he_rate(he20_table, snr) + 2; if (tmp > est) est = tmp; cw = he->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & own_he->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX]; if (cw & (IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G : HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) { tmp = max_he_rate(he40_table, snr) + 2; if (tmp > est) est = tmp; } if (!IS_2P4GHZ(freq) && (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) { tmp = max_he_rate(he80_table, snr) + 2; if (tmp > est) est = tmp; } if (!IS_2P4GHZ(freq) && (cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G | HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) { tmp = max_he_rate(he160_table, snr) + 2; if (tmp > est) est = tmp; } } return est; } void scan_est_throughput(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res) { int rate; /* max legacy rate in 500 kb/s units */ int snr = res->snr; const u8 *ies = (const void *) (res + 1); size_t ie_len = res->ie_len; if (res->est_throughput) return; /* Get maximum legacy rate */ rate = wpa_scan_get_max_rate(res); if (!ie_len) ie_len = res->beacon_ie_len; res->est_throughput = wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, res->freq); /* TODO: channel utilization and AP load (e.g., from AP Beacon) */ } /** * wpa_supplicant_get_scan_results - Get scan results * @wpa_s: Pointer to wpa_supplicant data * @info: Information about what was scanned or %NULL if not available * @new_scan: Whether a new scan was performed * Returns: Scan results, %NULL on failure * * This function request the current scan results from the driver and updates * the local BSS list wpa_s->bss. The caller is responsible for freeing the * results with wpa_scan_results_free(). */ struct wpa_scan_results * wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan) { struct wpa_scan_results *scan_res; size_t i; int (*compar)(const void *, const void *) = wpa_scan_result_compar; scan_res = wpa_drv_get_scan_results2(wpa_s); if (scan_res == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results"); return NULL; } if (scan_res->fetch_time.sec == 0) { /* * Make sure we have a valid timestamp if the driver wrapper * does not set this. */ os_get_reltime(&scan_res->fetch_time); } filter_scan_res(wpa_s, scan_res); for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *scan_res_item = scan_res->res[i]; scan_snr(scan_res_item); scan_est_throughput(wpa_s, scan_res_item); } #ifdef CONFIG_WPS if (wpas_wps_searching(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " "provisioning rules"); compar = wpa_scan_result_wps_compar; } #endif /* CONFIG_WPS */ if (scan_res->res) { qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), compar); } dump_scan_res(scan_res); if (wpa_s->ignore_post_flush_scan_res) { /* FLUSH command aborted an ongoing scan and these are the * results from the aborted scan. Do not process the results to * maintain flushed state. */ wpa_dbg(wpa_s, MSG_DEBUG, "Do not update BSS table based on pending post-FLUSH scan results"); wpa_s->ignore_post_flush_scan_res = 0; return scan_res; } wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) wpa_bss_update_scan_res(wpa_s, scan_res->res[i], &scan_res->fetch_time); wpa_bss_update_end(wpa_s, info, new_scan); return scan_res; } /** * wpa_supplicant_update_scan_results - Update scan results from the driver * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success, -1 on failure * * This function updates the BSS table within wpa_supplicant based on the * currently available scan results from the driver without requesting a new * scan. This is used in cases where the driver indicates an association * (including roaming within ESS) and wpa_supplicant does not yet have the * needed information to complete the connection (e.g., to perform validation * steps in 4-way handshake). */ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_scan_results *scan_res; scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); if (scan_res == NULL) return -1; wpa_scan_results_free(scan_res); return 0; } /** * scan_only_handler - Reports scan results */ void scan_only_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received"); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; } else { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); if (wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); } if (wpa_s->wpa_state == WPA_SCANNING) wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); } int wpas_scan_scheduled(struct wpa_supplicant *wpa_s) { return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL); } struct wpa_driver_scan_params * wpa_scan_clone_params(const struct wpa_driver_scan_params *src) { struct wpa_driver_scan_params *params; size_t i; u8 *n; params = os_zalloc(sizeof(*params)); if (params == NULL) return NULL; for (i = 0; i < src->num_ssids; i++) { if (src->ssids[i].ssid) { n = os_memdup(src->ssids[i].ssid, src->ssids[i].ssid_len); if (n == NULL) goto failed; params->ssids[i].ssid = n; params->ssids[i].ssid_len = src->ssids[i].ssid_len; } } params->num_ssids = src->num_ssids; if (src->extra_ies) { n = os_memdup(src->extra_ies, src->extra_ies_len); if (n == NULL) goto failed; params->extra_ies = n; params->extra_ies_len = src->extra_ies_len; } if (src->freqs) { int len = int_array_len(src->freqs); params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int)); if (params->freqs == NULL) goto failed; } if (src->filter_ssids) { params->filter_ssids = os_memdup(src->filter_ssids, sizeof(*params->filter_ssids) * src->num_filter_ssids); if (params->filter_ssids == NULL) goto failed; params->num_filter_ssids = src->num_filter_ssids; } params->filter_rssi = src->filter_rssi; params->p2p_probe = src->p2p_probe; params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; params->duration = src->duration; params->duration_mandatory = src->duration_mandatory; params->oce_scan = src->oce_scan; if (src->sched_scan_plans_num > 0) { params->sched_scan_plans = os_memdup(src->sched_scan_plans, sizeof(*src->sched_scan_plans) * src->sched_scan_plans_num); if (!params->sched_scan_plans) goto failed; params->sched_scan_plans_num = src->sched_scan_plans_num; } if (src->mac_addr_rand && wpa_setup_mac_addr_rand_params(params, src->mac_addr)) goto failed; if (src->bssid) { u8 *bssid; bssid = os_memdup(src->bssid, ETH_ALEN); if (!bssid) goto failed; params->bssid = bssid; } params->relative_rssi_set = src->relative_rssi_set; params->relative_rssi = src->relative_rssi; params->relative_adjust_band = src->relative_adjust_band; params->relative_adjust_rssi = src->relative_adjust_rssi; + params->p2p_include_6ghz = src->p2p_include_6ghz; return params; failed: wpa_scan_free_params(params); return NULL; } void wpa_scan_free_params(struct wpa_driver_scan_params *params) { size_t i; if (params == NULL) return; for (i = 0; i < params->num_ssids; i++) os_free((u8 *) params->ssids[i].ssid); os_free((u8 *) params->extra_ies); os_free(params->freqs); os_free(params->filter_ssids); os_free(params->sched_scan_plans); /* * Note: params->mac_addr_mask points to same memory allocation and * must not be freed separately. */ os_free((u8 *) params->mac_addr); os_free((u8 *) params->bssid); os_free(params); } int wpas_start_pno(struct wpa_supplicant *wpa_s) { int ret; size_t prio, i, num_ssid, num_match_ssid; struct wpa_ssid *ssid; struct wpa_driver_scan_params params; struct sched_scan_plan scan_plan; unsigned int max_sched_scan_ssids; if (!wpa_s->sched_scan_supported) return -1; if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; else max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; if (max_sched_scan_ssids < 1) return -1; if (wpa_s->pno || wpa_s->pno_sched_pending) return 0; if ((wpa_s->wpa_state > WPA_SCANNING) && (wpa_s->wpa_state < WPA_COMPLETED)) { wpa_printf(MSG_ERROR, "PNO: In assoc process"); return -EAGAIN; } if (wpa_s->wpa_state == WPA_SCANNING) { wpa_supplicant_cancel_scan(wpa_s); if (wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Schedule PNO on completion of " "ongoing sched scan"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_s->pno_sched_pending = 1; return 0; } } if (wpa_s->sched_scan_stop_req) { wpa_printf(MSG_DEBUG, "Schedule PNO after previous sched scan has stopped"); wpa_s->pno_sched_pending = 1; return 0; } os_memset(¶ms, 0, sizeof(params)); num_ssid = num_match_ssid = 0; ssid = wpa_s->conf->ssid; while (ssid) { if (!wpas_network_disabled(wpa_s, ssid)) { num_match_ssid++; if (ssid->scan_ssid) num_ssid++; } ssid = ssid->next; } if (num_match_ssid == 0) { wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs"); return -1; } if (num_match_ssid > num_ssid) { params.num_ssids++; /* wildcard */ num_ssid++; } if (num_ssid > max_sched_scan_ssids) { wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from " "%u", max_sched_scan_ssids, (unsigned int) num_ssid); num_ssid = max_sched_scan_ssids; } if (num_match_ssid > wpa_s->max_match_sets) { num_match_ssid = wpa_s->max_match_sets; wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match"); } params.filter_ssids = os_calloc(num_match_ssid, sizeof(struct wpa_driver_scan_filter)); if (params.filter_ssids == NULL) return -1; i = 0; prio = 0; ssid = wpa_s->conf->pssid[prio]; while (ssid) { if (!wpas_network_disabled(wpa_s, ssid)) { if (ssid->scan_ssid && params.num_ssids < num_ssid) { params.ssids[params.num_ssids].ssid = ssid->ssid; params.ssids[params.num_ssids].ssid_len = ssid->ssid_len; params.num_ssids++; } os_memcpy(params.filter_ssids[i].ssid, ssid->ssid, ssid->ssid_len); params.filter_ssids[i].ssid_len = ssid->ssid_len; params.num_filter_ssids++; i++; if (i == num_match_ssid) break; } if (ssid->pnext) ssid = ssid->pnext; else if (prio + 1 == wpa_s->conf->num_prio) break; else ssid = wpa_s->conf->pssid[++prio]; } if (wpa_s->conf->filter_rssi) params.filter_rssi = wpa_s->conf->filter_rssi; if (wpa_s->sched_scan_plans_num) { params.sched_scan_plans = wpa_s->sched_scan_plans; params.sched_scan_plans_num = wpa_s->sched_scan_plans_num; } else { /* Set one scan plan that will run infinitely */ if (wpa_s->conf->sched_scan_interval) scan_plan.interval = wpa_s->conf->sched_scan_interval; else scan_plan.interval = 10; scan_plan.iterations = 0; params.sched_scan_plans = &scan_plan; params.sched_scan_plans_num = 1; } params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay; if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels"); params.freqs = wpa_s->manual_sched_scan_freqs; } if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) && wpa_s->wpa_state <= WPA_SCANNING) wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_pno); wpa_scan_set_relative_rssi_params(wpa_s, ¶ms); ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms); os_free(params.filter_ssids); os_free(params.mac_addr); if (ret == 0) wpa_s->pno = 1; else wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO"); return ret; } int wpas_stop_pno(struct wpa_supplicant *wpa_s) { int ret = 0; if (!wpa_s->pno) return 0; ret = wpa_supplicant_stop_sched_scan(wpa_s); wpa_s->sched_scan_stop_req = 1; wpa_s->pno = 0; wpa_s->pno_sched_pending = 0; if (wpa_s->wpa_state == WPA_SCANNING) wpa_supplicant_req_scan(wpa_s, 0, 0); return ret; } void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, unsigned int type) { type &= MAC_ADDR_RAND_ALL; wpa_s->mac_addr_rand_enable &= ~type; if (type & MAC_ADDR_RAND_SCAN) { os_free(wpa_s->mac_addr_scan); wpa_s->mac_addr_scan = NULL; } if (type & MAC_ADDR_RAND_SCHED_SCAN) { os_free(wpa_s->mac_addr_sched_scan); wpa_s->mac_addr_sched_scan = NULL; } if (type & MAC_ADDR_RAND_PNO) { os_free(wpa_s->mac_addr_pno); wpa_s->mac_addr_pno = NULL; } } int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, unsigned int type, const u8 *addr, const u8 *mask) { u8 *tmp = NULL; if ((wpa_s->mac_addr_rand_supported & type) != type ) { wpa_printf(MSG_INFO, "scan: MAC randomization type %u != supported=%u", type, wpa_s->mac_addr_rand_supported); return -1; } wpas_mac_addr_rand_scan_clear(wpa_s, type); if (addr) { tmp = os_malloc(2 * ETH_ALEN); if (!tmp) return -1; os_memcpy(tmp, addr, ETH_ALEN); os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN); } if (type == MAC_ADDR_RAND_SCAN) { wpa_s->mac_addr_scan = tmp; } else if (type == MAC_ADDR_RAND_SCHED_SCAN) { wpa_s->mac_addr_sched_scan = tmp; } else if (type == MAC_ADDR_RAND_PNO) { wpa_s->mac_addr_pno = tmp; } else { wpa_printf(MSG_INFO, "scan: Invalid MAC randomization type=0x%x", type); os_free(tmp); return -1; } wpa_s->mac_addr_rand_enable |= type; return 0; } int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s, unsigned int type, u8 *mask) { const u8 *to_copy; if ((wpa_s->mac_addr_rand_enable & type) != type) return -1; if (type == MAC_ADDR_RAND_SCAN) { to_copy = wpa_s->mac_addr_scan; } else if (type == MAC_ADDR_RAND_SCHED_SCAN) { to_copy = wpa_s->mac_addr_sched_scan; } else if (type == MAC_ADDR_RAND_PNO) { to_copy = wpa_s->mac_addr_pno; } else { wpa_printf(MSG_DEBUG, "scan: Invalid MAC randomization type=0x%x", type); return -1; } os_memcpy(mask, to_copy + ETH_ALEN, ETH_ALEN); return 0; } int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s) { struct wpa_radio_work *work; struct wpa_radio *radio = wpa_s->radio; dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { if (work->wpa_s != wpa_s || !work->started || (os_strcmp(work->type, "scan") != 0 && os_strcmp(work->type, "p2p-scan") != 0)) continue; wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan"); return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie); } wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort"); return -1; } int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd) { struct sched_scan_plan *scan_plans = NULL; const char *token, *context = NULL; unsigned int num = 0; if (!cmd) return -1; if (!cmd[0]) { wpa_printf(MSG_DEBUG, "Clear sched scan plans"); os_free(wpa_s->sched_scan_plans); wpa_s->sched_scan_plans = NULL; wpa_s->sched_scan_plans_num = 0; return 0; } while ((token = cstr_token(cmd, " ", &context))) { int ret; struct sched_scan_plan *scan_plan, *n; n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans)); if (!n) goto fail; scan_plans = n; scan_plan = &scan_plans[num]; num++; ret = sscanf(token, "%u:%u", &scan_plan->interval, &scan_plan->iterations); if (ret <= 0 || ret > 2 || !scan_plan->interval) { wpa_printf(MSG_ERROR, "Invalid sched scan plan input: %s", token); goto fail; } if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) { wpa_printf(MSG_WARNING, "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)", num, scan_plan->interval, wpa_s->max_sched_scan_plan_interval); scan_plan->interval = wpa_s->max_sched_scan_plan_interval; } if (ret == 1) { scan_plan->iterations = 0; break; } if (!scan_plan->iterations) { wpa_printf(MSG_ERROR, "scan plan %u: Number of iterations cannot be zero", num); goto fail; } if (scan_plan->iterations > wpa_s->max_sched_scan_plan_iterations) { wpa_printf(MSG_WARNING, "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)", num, scan_plan->iterations, wpa_s->max_sched_scan_plan_iterations); scan_plan->iterations = wpa_s->max_sched_scan_plan_iterations; } wpa_printf(MSG_DEBUG, "scan plan %u: interval=%u iterations=%u", num, scan_plan->interval, scan_plan->iterations); } if (!scan_plans) { wpa_printf(MSG_ERROR, "Invalid scan plans entry"); goto fail; } if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) { wpa_printf(MSG_ERROR, "All scan plans but the last must specify a number of iterations"); goto fail; } wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u", num, scan_plans[num - 1].interval); if (num > wpa_s->max_sched_scan_plans) { wpa_printf(MSG_WARNING, "Too many scheduled scan plans (only %u supported)", wpa_s->max_sched_scan_plans); wpa_printf(MSG_WARNING, "Use only the first %u scan plans, and the last one (in infinite loop)", wpa_s->max_sched_scan_plans - 1); os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1], &scan_plans[num - 1], sizeof(*scan_plans)); num = wpa_s->max_sched_scan_plans; } os_free(wpa_s->sched_scan_plans); wpa_s->sched_scan_plans = scan_plans; wpa_s->sched_scan_plans_num = num; return 0; fail: os_free(scan_plans); wpa_printf(MSG_ERROR, "invalid scan plans list"); return -1; } /** * wpas_scan_reset_sched_scan - Reset sched_scan state * @wpa_s: Pointer to wpa_supplicant data * * This function is used to cancel a running scheduled scan and to reset an * internal scan state to continue with a regular scan on the following * wpa_supplicant_req_scan() calls. */ void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s) { wpa_s->normal_scans = 0; if (wpa_s->sched_scanning) { wpa_s->sched_scan_timed_out = 0; wpa_s->prev_sched_ssid = NULL; wpa_supplicant_cancel_sched_scan(wpa_s); } } void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s) { /* simulate timeout to restart the sched scan */ wpa_s->sched_scan_timed_out = 1; wpa_s->prev_sched_ssid = NULL; wpa_supplicant_cancel_sched_scan(wpa_s); } diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 72aa9b5b8f18..f2c42ff354e7 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1,2942 +1,2949 @@ /* * wpa_supplicant - SME * Copyright (c) 2009-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/ocv.h" #include "common/hw_features_common.h" #include "eapol_supp/eapol_supp_sm.h" #include "common/wpa_common.h" #include "common/sae.h" #include "common/dpp.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "sme.h" #include "hs20_supplicant.h" #define SME_AUTH_TIMEOUT 5 #define SME_ASSOC_TIMEOUT 5 static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx); static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); #ifdef CONFIG_SAE static int index_within_array(const int *array, int idx) { int i; for (i = 0; i < idx; i++) { if (array[i] <= 0) return 0; } return 1; } static int sme_set_sae_group(struct wpa_supplicant *wpa_s) { int *groups = wpa_s->conf->sae_groups; int default_groups[] = { 19, 20, 21, 0 }; if (!groups || groups[0] <= 0) groups = default_groups; /* Configuration may have changed, so validate current index */ if (!index_within_array(groups, wpa_s->sme.sae_group_index)) return -1; for (;;) { int group = groups[wpa_s->sme.sae_group_index]; if (group <= 0) break; if (sae_set_group(&wpa_s->sme.sae, group) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", wpa_s->sme.sae.group); return 0; } wpa_s->sme.sae_group_index++; } return -1; } static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *bssid, int external, int reuse, int *ret_use_pt, bool *ret_use_pk) { struct wpabuf *buf; size_t len; const char *password; struct wpa_bss *bss; int use_pt = 0; bool use_pk = false; u8 rsnxe_capa = 0; if (ret_use_pt) *ret_use_pt = 0; if (ret_use_pk) *ret_use_pk = false; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->sae_commit_override) { wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override)); if (!buf) return NULL; if (!external) { wpabuf_put_le16(buf, 1); /* Transaction seq# */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); } wpabuf_put_buf(buf, wpa_s->sae_commit_override); return buf; } #endif /* CONFIG_TESTING_OPTIONS */ password = ssid->sae_password; if (!password) password = ssid->passphrase; if (!password) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } if (reuse && wpa_s->sme.sae.tmp && os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "SAE: Reuse previously generated PWE on a retry with the same AP"); use_pt = wpa_s->sme.sae.h2e; use_pk = wpa_s->sme.sae.pk; goto reuse_data; } if (sme_set_sae_group(wpa_s) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to select group"); return NULL; } bss = wpa_bss_get_bssid_latest(wpa_s, bssid); if (bss) { const u8 *rsnxe; rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (rsnxe && rsnxe[1] >= 1) rsnxe_capa = rsnxe[2]; } if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3) use_pt = 1; #ifdef CONFIG_SAE_PK if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) && ssid->sae_pk != SAE_PK_MODE_DISABLED && ((ssid->sae_password && sae_pk_valid_password(ssid->sae_password)) || (!ssid->sae_password && ssid->passphrase && sae_pk_valid_password(ssid->passphrase)))) { use_pt = 1; use_pk = true; } if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) { wpa_printf(MSG_DEBUG, "SAE: Cannot use PK with the selected AP"); return NULL; } #endif /* CONFIG_SAE_PK */ if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) { use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E)); if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) && wpa_s->conf->sae_pwe != 3 && !use_pt) { wpa_printf(MSG_DEBUG, "SAE: Cannot use H2E with the selected AP"); return NULL; } } if (use_pt && sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt, wpa_s->own_addr, bssid, wpa_s->sme.sae_rejected_groups, NULL) < 0) return NULL; if (!use_pt && sae_prepare_commit(wpa_s->own_addr, bssid, (u8 *) password, os_strlen(password), &wpa_s->sme.sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } if (wpa_s->sme.sae.tmp) { os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN); if (use_pt && use_pk) wpa_s->sme.sae.pk = 1; #ifdef CONFIG_SAE_PK os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN); sae_pk_set_password(&wpa_s->sme.sae, password); #endif /* CONFIG_SAE_PK */ } reuse_data: len = wpa_s->sme.sae_token ? 3 + wpabuf_len(wpa_s->sme.sae_token) : 0; if (ssid->sae_password_id) len += 4 + os_strlen(ssid->sae_password_id); buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len); if (buf == NULL) return NULL; if (!external) { wpabuf_put_le16(buf, 1); /* Transaction seq# */ if (use_pk) wpabuf_put_le16(buf, WLAN_STATUS_SAE_PK); else if (use_pt) wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); else wpabuf_put_le16(buf,WLAN_STATUS_SUCCESS); } if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token, ssid->sae_password_id) < 0) { wpabuf_free(buf); return NULL; } if (ret_use_pt) *ret_use_pt = use_pt; if (ret_use_pk) *ret_use_pk = use_pk; return buf; } static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s, int external) { struct wpabuf *buf; buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; if (!external) { wpabuf_put_le16(buf, 2); /* Transaction seq# */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); } sae_write_confirm(&wpa_s->sme.sae, buf); return buf; } #endif /* CONFIG_SAE */ /** * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt * @wpa_s: Pointer to wpa_supplicant data * @bss: Pointer to the bss which is the target of authentication attempt */ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { const u8 rrm_ie_len = 5; u8 *pos; const u8 *rrm_ie; wpa_s->rrm.rrm_used = 0; wpa_printf(MSG_DEBUG, "RRM: Determining whether RRM can be used - device support: 0x%x", wpa_s->drv_rrm_flags); rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES); if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) { wpa_printf(MSG_DEBUG, "RRM: No RRM in network"); return; } if (!((wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) && (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) && !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) { wpa_printf(MSG_DEBUG, "RRM: Insufficient RRM support in driver - do not use RRM"); return; } if (sizeof(wpa_s->sme.assoc_req_ie) < wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) { wpa_printf(MSG_INFO, "RRM: Unable to use RRM, no room for RRM IE"); return; } wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request"); pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; os_memset(pos, 0, 2 + rrm_ie_len); *pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; *pos++ = rrm_ie_len; /* Set supported capabilities flags */ if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | WLAN_RRM_CAPS_BEACON_REPORT_TABLE; if (wpa_s->lci) pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT; wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2; wpa_s->rrm.rrm_used = 1; } static void sme_send_authentication(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, int start) { struct wpa_driver_auth_params params; struct wpa_ssid *old_ssid; #ifdef CONFIG_IEEE80211R const u8 *ie; #endif /* CONFIG_IEEE80211R */ #if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) const u8 *md = NULL; #endif /* CONFIG_IEEE80211R || CONFIG_FILS */ int bssid_changed; struct wpabuf *resp = NULL; u8 ext_capab[18]; int ext_capab_len; int skip_auth; u8 *wpa_ie; size_t wpa_ie_len; #ifdef CONFIG_MBO const u8 *mbo_ie; #endif /* CONFIG_MBO */ int omit_rsnxe = 0; if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " "the network"); wpas_connect_work_done(wpa_s); return; } skip_auth = wpa_s->conf->reassoc_same_bss_optim && wpa_s->reassoc_same_bss; wpa_s->current_bss = bss; os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; params.freq = bss->freq; params.bssid = bss->bssid; params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; params.p2p = ssid->p2p_group; if (wpa_s->sme.ssid_len != params.ssid_len || os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0) wpa_s->sme.prev_bssid_set = 0; wpa_s->sme.freq = params.freq; os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len); wpa_s->sme.ssid_len = params.ssid_len; params.auth_alg = WPA_AUTH_ALG_OPEN; #ifdef IEEE8021X_EAPOL if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (ssid->leap) { if (ssid->non_leap == 0) params.auth_alg = WPA_AUTH_ALG_LEAP; else params.auth_alg |= WPA_AUTH_ALG_LEAP; } } #endif /* IEEE8021X_EAPOL */ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", params.auth_alg); if (ssid->auth_alg) { params.auth_alg = ssid->auth_alg; wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " "0x%x", params.auth_alg); } #ifdef CONFIG_SAE wpa_s->sme.sae_pmksa_caching = 0; if (wpa_key_mgmt_sae(ssid->key_mgmt)) { const u8 *rsn; struct wpa_ie_data ied; rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (!rsn) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but target BSS does not advertise RSN"); #ifdef CONFIG_DPP } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && (ssid->key_mgmt & WPA_KEY_MGMT_DPP) && (ied.key_mgmt & WPA_KEY_MGMT_DPP)) { wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled"); #endif /* CONFIG_DPP */ } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && wpa_key_mgmt_sae(ied.key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); params.auth_alg = WPA_AUTH_ALG_SAE; } else { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but target BSS does not advertise SAE AKM for RSN"); } } #endif /* CONFIG_SAE */ #ifdef CONFIG_WEP { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) params.wep_key[i] = ssid->wep_key[i]; params.wep_key_len[i] = ssid->wep_key_len[i]; } params.wep_tx_keyidx = ssid->wep_tx_keyidx; } #endif /* CONFIG_WEP */ if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; const u8 *cache_id = NULL; try_opportunistic = (ssid->proactive_key_caching < 0 ? wpa_s->conf->okc : ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); #ifdef CONFIG_FILS if (wpa_key_mgmt_fils(ssid->key_mgmt)) cache_id = wpa_bss_get_fils_cache_id(bss); #endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, try_opportunistic, cache_id, 0) == 0) eapol_sm_notify_pmkid_attempt(wpa_s->eapol); wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites"); wpas_connect_work_done(wpa_s); return; } #ifdef CONFIG_HS20 } else if (wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { /* No PMKSA caching, but otherwise similar to RSN/WPA */ wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites"); wpas_connect_work_done(wpa_s); return; } #endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* * Both WPA and non-WPA IEEE 802.1X enabled in configuration - * use non-WPA since the scan results did not indicate that the * AP is using WPA or WPA2. */ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_s->sme.assoc_req_ie_len = 0; } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites (no " "scan results)"); wpas_connect_work_done(wpa_s); return; } #ifdef CONFIG_WPS } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { struct wpabuf *wps_ie; wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_s->sme.assoc_req_ie)) { wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie); os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie), wpa_s->sme.assoc_req_ie_len); } else wpa_s->sme.assoc_req_ie_len = 0; wpabuf_free(wps_ie); wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); #endif /* CONFIG_WPS */ } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_s->sme.assoc_req_ie_len = 0; } /* In case the WPA vendor IE is used, it should be placed after all the * non-vendor IEs, as the lower layer expects the IEs to be ordered as * defined in the standard. Store the WPA IE so it can later be * inserted at the correct location. */ wpa_ie = NULL; wpa_ie_len = 0; if (wpa_s->wpa_proto == WPA_PROTO_WPA) { wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); if (wpa_ie) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE"); wpa_ie_len = wpa_s->sme.assoc_req_ie_len; wpa_s->sme.assoc_req_ie_len = 0; } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed copy WPA IE"); wpas_connect_work_done(wpa_s); return; } } #ifdef CONFIG_IEEE80211R ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) md = ie + 2; wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); if (md && (!wpa_key_mgmt_ft(ssid->key_mgmt) || !wpa_key_mgmt_ft(wpa_s->key_mgmt))) md = NULL; if (md) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } if (md) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x", md[0], md[1]); omit_rsnxe = !wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (wpa_s->sme.assoc_req_ie_len + 5 < sizeof(wpa_s->sme.assoc_req_ie)) { struct rsn_mdie *mdie; u8 *pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; *pos++ = WLAN_EID_MOBILITY_DOMAIN; *pos++ = sizeof(*mdie); mdie = (struct rsn_mdie *) pos; os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); mdie->ft_capab = md[MOBILITY_DOMAIN_ID_LEN]; wpa_s->sme.assoc_req_ie_len += 5; } if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used && os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 && wpa_sm_has_ptk(wpa_s->wpa)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " "over-the-air"); params.auth_alg = WPA_AUTH_ALG_FT; params.ie = wpa_s->sme.ft_ies; params.ie_len = wpa_s->sme.ft_ies_len; } } #endif /* CONFIG_IEEE80211R */ wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid); if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data _ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && _ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports " "MFP: require MFP"); wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; } } #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; size_t len; int res; pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; len = sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len; res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, ssid->p2p_group); if (res >= 0) wpa_s->sme.assoc_req_ie_len += res; } #endif /* CONFIG_P2P */ #ifdef CONFIG_FST if (wpa_s->fst_ies) { int fst_ies_len = wpabuf_len(wpa_s->fst_ies); if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <= sizeof(wpa_s->sme.assoc_req_ie)) { os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(wpa_s->fst_ies), fst_ies_len); wpa_s->sme.assoc_req_ie_len += fst_ies_len; } } #endif /* CONFIG_FST */ sme_auth_handle_rrm(wpa_s, bss); wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie( wpa_s, ssid, bss, wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len); if (params.p2p) wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); else wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0) { u8 *pos = wpa_s->sme.assoc_req_ie; if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) pos += 2 + pos[1]; os_memmove(pos + ext_capab_len, pos, wpa_s->sme.assoc_req_ie_len - (pos - wpa_s->sme.assoc_req_ie)); wpa_s->sme.assoc_req_ie_len += ext_capab_len; os_memcpy(pos, ext_capab, ext_capab_len); } #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->rsnxe_override_assoc && wpabuf_len(wpa_s->rsnxe_override_assoc) <= sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) { wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override"); os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(wpa_s->rsnxe_override_assoc), wpabuf_len(wpa_s->rsnxe_override_assoc)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(wpa_s->rsnxe_override_assoc); } else #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->rsnxe_len > 0 && wpa_s->rsnxe_len <= sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len && !omit_rsnxe) { os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len); wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len; } #ifdef CONFIG_HS20 if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN); if (hs20) { int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); size_t len; wpas_hs20_add_indication(hs20, pps_mo_id, get_hs20_version(bss)); wpas_hs20_add_roam_cons_sel(hs20, ssid); len = sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len; if (wpabuf_len(hs20) <= len) { os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(hs20), wpabuf_len(hs20)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); } wpabuf_free(hs20); } } #endif /* CONFIG_HS20 */ if (wpa_ie) { size_t len; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Reinsert WPA IE"); len = sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len; if (len > wpa_ie_len) { os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpa_ie, wpa_ie_len); wpa_s->sme.assoc_req_ie_len += wpa_ie_len; } else { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Failed to add WPA IE"); } os_free(wpa_ie); } if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; len = sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len; if (wpabuf_len(buf) <= len) { os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(buf), wpabuf_len(buf)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf); } } #ifdef CONFIG_MBO mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); if (!wpa_s->disable_mbo_oce && mbo_ie) { int len; len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len, !!mbo_attr_from_mbo_ie(mbo_ie, OCE_ATTR_ID_CAPA_IND)); if (len >= 0) wpa_s->sme.assoc_req_ie_len += len; } #endif /* CONFIG_MBO */ #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0, NULL, wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ? WPA_KEY_MGMT_FT_SAE : WPA_KEY_MGMT_SAE) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication"); wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); params.auth_alg = WPA_AUTH_ALG_OPEN; wpa_s->sme.sae_pmksa_caching = 1; } if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) { if (start) resp = sme_auth_build_sae_commit(wpa_s, ssid, bss->bssid, 0, start == 2, NULL, NULL); else resp = sme_auth_build_sae_confirm(wpa_s, 0); if (resp == NULL) { wpas_connection_failed(wpa_s, bss->bssid); return; } params.auth_data = wpabuf_head(resp); params.auth_data_len = wpabuf_len(resp); wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED; } #endif /* CONFIG_SAE */ bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); if (bssid_changed) wpas_notify_bssid_changed(wpa_s); old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); #ifdef CONFIG_FILS /* TODO: FILS operations can in some cases be done between different * network_ctx (i.e., same credentials can be used with multiple * networks). */ if (params.auth_alg == WPA_AUTH_ALG_OPEN && wpa_key_mgmt_fils(ssid->key_mgmt)) { const u8 *indic; u16 fils_info; const u8 *realm, *username, *rrk; size_t realm_len, username_len, rrk_len; u16 next_seq_num; /* * Check FILS Indication element (FILS Information field) bits * indicating supported authentication algorithms against local * configuration (ssid->fils_dh_group). Try to use FILS * authentication only if the AP supports the combination in the * network profile. */ indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION); if (!indic || indic[1] < 2) { wpa_printf(MSG_DEBUG, "SME: " MACSTR " does not include FILS Indication element - cannot use FILS authentication with it", MAC2STR(bss->bssid)); goto no_fils; } fils_info = WPA_GET_LE16(indic + 2); if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) { wpa_printf(MSG_DEBUG, "SME: " MACSTR " does not support FILS SK without PFS - cannot use FILS authentication with it", MAC2STR(bss->bssid)); goto no_fils; } if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) { wpa_printf(MSG_DEBUG, "SME: " MACSTR " does not support FILS SK with PFS - cannot use FILS authentication with it", MAC2STR(bss->bssid)); goto no_fils; } if (wpa_s->last_con_fail_realm && eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username, &username_len, &realm, &realm_len, &next_seq_num, &rrk, &rrk_len) == 0 && realm && realm_len == wpa_s->last_con_fail_realm_len && os_memcmp(realm, wpa_s->last_con_fail_realm, realm_len) == 0) { wpa_printf(MSG_DEBUG, "SME: FILS authentication for this realm failed last time - try to regenerate ERP key hierarchy"); goto no_fils; } if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0, wpa_bss_get_fils_cache_id(bss), 0) == 0) wpa_printf(MSG_DEBUG, "SME: Try to use FILS with PMKSA caching"); resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md); if (resp) { int auth_alg; if (ssid->fils_dh_group) wpa_printf(MSG_DEBUG, "SME: Try to use FILS SK authentication with PFS (DH Group %u)", ssid->fils_dh_group); else wpa_printf(MSG_DEBUG, "SME: Try to use FILS SK authentication without PFS"); auth_alg = ssid->fils_dh_group ? WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS; params.auth_alg = auth_alg; params.auth_data = wpabuf_head(resp); params.auth_data_len = wpabuf_len(resp); wpa_s->sme.auth_alg = auth_alg; } } no_fils: #endif /* CONFIG_FILS */ wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); eapol_sm_notify_portValid(wpa_s->eapol, false); wpa_clear_keys(wpa_s, bss->bssid); wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); #ifdef CONFIG_HS20 hs20_configure_frame_filters(wpa_s); #endif /* CONFIG_HS20 */ #ifdef CONFIG_P2P /* * If multi-channel concurrency is not supported, check for any * frequency conflict. In case of any frequency conflict, remove the * least prioritized connection. */ if (wpa_s->num_multichan_concurrent < 2) { int freq, num; num = get_shared_radio_freqs(wpa_s, &freq, 1); if (num > 0 && freq > 0 && freq != params.freq) { wpa_printf(MSG_DEBUG, "Conflicting frequency found (%d != %d)", freq, params.freq); if (wpas_p2p_handle_frequency_conflicts(wpa_s, params.freq, ssid) < 0) { wpas_connection_failed(wpa_s, bss->bssid); wpa_supplicant_mark_disassoc(wpa_s); wpabuf_free(resp); wpas_connect_work_done(wpa_s); return; } } } #endif /* CONFIG_P2P */ if (skip_auth) { wpa_msg(wpa_s, MSG_DEBUG, "SME: Skip authentication step on reassoc-to-same-BSS"); wpabuf_free(resp); sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN); return; } wpa_s->sme.auth_alg = params.auth_alg; if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the " "driver failed"); wpas_connection_failed(wpa_s, bss->bssid); wpa_supplicant_mark_disassoc(wpa_s); wpabuf_free(resp); wpas_connect_work_done(wpa_s); return; } eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, NULL); /* * Association will be started based on the authentication event from * the driver. */ wpabuf_free(resp); } static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) { struct wpa_connect_work *cwork = work->ctx; struct wpa_supplicant *wpa_s = work->wpa_s; wpa_s->roam_in_progress = false; if (deinit) { if (work->started) wpa_s->connect_work = NULL; wpas_connect_work_free(cwork); return; } wpa_s->connect_work = work; if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) || wpas_network_disabled(wpa_s, cwork->ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt"); wpas_connect_work_done(wpa_s); return; } /* Starting new connection, so clear the possibly used WPA IE from the * previous association. */ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); wpa_s->rsnxe_len = 0; sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1); } void sme_authenticate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { struct wpa_connect_work *cwork; if (bss == NULL || ssid == NULL) return; if (wpa_s->connect_work) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist"); return; } if (wpa_s->roam_in_progress) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() in favor of explicit roam request"); return; } if (radio_work_pending(wpa_s, "sme-connect")) { /* * The previous sme-connect work might no longer be valid due to * the fact that the BSS list was updated. In addition, it makes * sense to adhere to the 'newer' decision. */ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove previous pending sme-connect"); radio_remove_works(wpa_s, "sme-connect", 0); } wpas_abort_ongoing_scan(wpa_s); cwork = os_zalloc(sizeof(*cwork)); if (cwork == NULL) return; cwork->bss = bss; cwork->ssid = ssid; cwork->sme = 1; #ifdef CONFIG_SAE wpa_s->sme.sae.state = SAE_NOTHING; wpa_s->sme.sae.send_confirm = 0; wpa_s->sme.sae_group_index = 0; #endif /* CONFIG_SAE */ if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1, sme_auth_start_cb, cwork) < 0) wpas_connect_work_free(cwork); } #ifdef CONFIG_SAE static int sme_external_auth_build_buf(struct wpabuf *buf, struct wpabuf *params, const u8 *sa, const u8 *da, u16 auth_transaction, u16 seq_num, u16 status_code) { struct ieee80211_mgmt *resp; resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, u.auth.variable)); resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4)); os_memcpy(resp->da, da, ETH_ALEN); os_memcpy(resp->sa, sa, ETH_ALEN); os_memcpy(resp->bssid, da, ETH_ALEN); resp->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE); resp->seq_ctrl = host_to_le16(seq_num << 4); resp->u.auth.auth_transaction = host_to_le16(auth_transaction); resp->u.auth.status_code = host_to_le16(status_code); if (params) wpabuf_put_buf(buf, params); return 0; } static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, const u8 *bssid, struct wpa_ssid *ssid) { struct wpabuf *resp, *buf; int use_pt; bool use_pk; u16 status; resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt, &use_pk); if (!resp) { wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit"); return -1; } wpa_s->sme.sae.state = SAE_COMMITTED; buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp)); if (!buf) { wpabuf_free(resp); return -1; } wpa_s->sme.seq_num++; if (use_pk) status = WLAN_STATUS_SAE_PK; else if (use_pt) status = WLAN_STATUS_SAE_HASH_TO_ELEMENT; else status = WLAN_STATUS_SUCCESS; sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, bssid, 1, wpa_s->sme.seq_num, status); wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0); wpabuf_free(resp); wpabuf_free(buf); return 0; } static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, u16 status) { struct external_auth params; os_memset(¶ms, 0, sizeof(params)); params.status = status; params.ssid = wpa_s->sme.ext_auth_ssid; params.ssid_len = wpa_s->sme.ext_auth_ssid_len; params.bssid = wpa_s->sme.ext_auth_bssid; if (wpa_s->conf->sae_pmkid_in_assoc && status == WLAN_STATUS_SUCCESS) params.pmkid = wpa_s->sme.sae.pmkid; wpa_drv_send_external_auth_status(wpa_s, ¶ms); } static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid; size_t ssid_str_len = data->external_auth.ssid_len; const u8 *ssid_str = data->external_auth.ssid; /* Get the SSID conf from the ssid string obtained */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!wpas_network_disabled(wpa_s, ssid) && ssid_str_len == ssid->ssid_len && os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 && (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))) break; } if (!ssid || sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid, ssid) < 0) return -1; return 0; } static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s, const u8 *da) { struct wpabuf *resp, *buf; resp = sme_auth_build_sae_confirm(wpa_s, 1); if (!resp) { wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure"); return; } wpa_s->sme.sae.state = SAE_CONFIRMED; buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp)); if (!buf) { wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure"); wpabuf_free(resp); return; } wpa_s->sme.seq_num++; sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, da, 2, wpa_s->sme.seq_num, WLAN_STATUS_SUCCESS); wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0); wpabuf_free(resp); wpabuf_free(buf); } void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) != RSN_AUTH_KEY_MGMT_SAE) return; if (data->external_auth.action == EXT_AUTH_START) { if (!data->external_auth.bssid || !data->external_auth.ssid) return; os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid, ETH_ALEN); os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid, data->external_auth.ssid_len); wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len; wpa_s->sme.seq_num = 0; wpa_s->sme.sae.state = SAE_NOTHING; wpa_s->sme.sae.send_confirm = 0; wpa_s->sme.sae_group_index = 0; if (sme_handle_external_auth_start(wpa_s, data) < 0) sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); } else if (data->external_auth.action == EXT_AUTH_ABORT) { /* Report failure to driver for the wrong trigger */ sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); } } static int sme_sae_is_group_enabled(struct wpa_supplicant *wpa_s, int group) { int *groups = wpa_s->conf->sae_groups; int default_groups[] = { 19, 20, 21, 0 }; int i; if (!groups) groups = default_groups; for (i = 0; groups[i] > 0; i++) { if (groups[i] == group) return 1; } return 0; } static int sme_check_sae_rejected_groups(struct wpa_supplicant *wpa_s, const struct wpabuf *groups) { size_t i, count; const u8 *pos; if (!groups) return 0; pos = wpabuf_head(groups); count = wpabuf_len(groups) / 2; for (i = 0; i < count; i++) { int enabled; u16 group; group = WPA_GET_LE16(pos); pos += 2; enabled = sme_sae_is_group_enabled(wpa_s, group); wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s", group, enabled ? "enabled" : "disabled"); if (enabled) return 1; } return 0; } static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, u16 status_code, const u8 *data, size_t len, int external, const u8 *sa) { int *groups; wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " "status code %u", auth_transaction, status_code); if (auth_transaction == 1 && status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && wpa_s->sme.sae.state == SAE_COMMITTED && (external || wpa_s->current_bss) && wpa_s->current_ssid) { int default_groups[] = { 19, 20, 21, 0 }; u16 group; const u8 *token_pos; size_t token_len; int h2e = 0; groups = wpa_s->conf->sae_groups; if (!groups || groups[0] <= 0) groups = default_groups; wpa_hexdump(MSG_DEBUG, "SME: SAE anti-clogging token request", data, len); if (len < sizeof(le16)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Too short SAE anti-clogging token request"); return -1; } group = WPA_GET_LE16(data); wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token requested (group %u)", group); if (sae_group_allowed(&wpa_s->sme.sae, groups, group) != WLAN_STATUS_SUCCESS) { wpa_dbg(wpa_s, MSG_ERROR, "SME: SAE group %u of anti-clogging request is invalid", group); return -1; } wpabuf_free(wpa_s->sme.sae_token); token_pos = data + sizeof(le16); token_len = len - sizeof(le16); h2e = wpa_s->sme.sae.h2e; if (h2e) { if (token_len < 3) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Too short SAE anti-clogging token container"); return -1; } if (token_pos[0] != WLAN_EID_EXTENSION || token_pos[1] == 0 || token_pos[1] > token_len - 2 || token_pos[2] != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Invalid SAE anti-clogging token container header"); return -1; } token_len = token_pos[1] - 1; token_pos += 3; } wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len); wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token", wpa_s->sme.sae_token); if (!external) sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 2); else sme_external_auth_send_sae_commit( wpa_s, wpa_s->sme.ext_auth_bssid, wpa_s->current_ssid); return 0; } if (auth_transaction == 1 && status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && wpa_s->sme.sae.state == SAE_COMMITTED && (external || wpa_s->current_bss) && wpa_s->current_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported"); int_array_add_unique(&wpa_s->sme.sae_rejected_groups, wpa_s->sme.sae.group); wpa_s->sme.sae_group_index++; if (sme_set_sae_group(wpa_s) < 0) return -1; /* no other groups enabled */ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group"); if (!external) sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 1); else sme_external_auth_send_sae_commit( wpa_s, wpa_s->sme.ext_auth_bssid, wpa_s->current_ssid); return 0; } if (auth_transaction == 1 && status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { const u8 *bssid = sa ? sa : wpa_s->pending_bssid; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR, MAC2STR(bssid)); return -1; } if (status_code != WLAN_STATUS_SUCCESS && status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT && - status_code != WLAN_STATUS_SAE_PK) + status_code != WLAN_STATUS_SAE_PK) { + const u8 *bssid = sa ? sa : wpa_s->pending_bssid; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR + " auth_type=%u auth_transaction=%u status_code=%u", + MAC2STR(bssid), WLAN_AUTH_SAE, + auth_transaction, status_code); return -1; + } if (auth_transaction == 1) { u16 res; groups = wpa_s->conf->sae_groups; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); if ((!external && wpa_s->current_bss == NULL) || wpa_s->current_ssid == NULL) return -1; if (wpa_s->sme.sae.state != SAE_COMMITTED) { wpa_printf(MSG_DEBUG, "SAE: Ignore commit message while waiting for confirm"); return 0; } if (wpa_s->sme.sae.h2e && status_code == WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "SAE: Unexpected use of status code 0 in SAE commit when H2E was expected"); return -1; } if ((!wpa_s->sme.sae.h2e || wpa_s->sme.sae.pk) && status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) { wpa_printf(MSG_DEBUG, "SAE: Unexpected use of status code for H2E in SAE commit when H2E was not expected"); return -1; } if (!wpa_s->sme.sae.pk && status_code == WLAN_STATUS_SAE_PK) { wpa_printf(MSG_DEBUG, "SAE: Unexpected use of status code for PK in SAE commit when PK was not expected"); return -1; } if (groups && groups[0] <= 0) groups = NULL; res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, groups, status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || status_code == WLAN_STATUS_SAE_PK); if (res == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message due to reflection attack"); return 0; } if (res != WLAN_STATUS_SUCCESS) return -1; if (wpa_s->sme.sae.tmp && sme_check_sae_rejected_groups( wpa_s, wpa_s->sme.sae.tmp->peer_rejected_groups)) return -1; if (sae_process_commit(&wpa_s->sme.sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to process peer " "commit"); return -1; } wpabuf_free(wpa_s->sme.sae_token); wpa_s->sme.sae_token = NULL; if (!external) sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 0); else sme_external_auth_send_sae_confirm(wpa_s, sa); return 0; } else if (auth_transaction == 2) { if (status_code != WLAN_STATUS_SUCCESS) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); if (wpa_s->sme.sae.state != SAE_CONFIRMED) return -1; if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0) return -1; wpa_s->sme.sae.state = SAE_ACCEPTED; sae_clear_temp_data(&wpa_s->sme.sae); if (external) { /* Report success to driver */ sme_send_external_auth_status(wpa_s, WLAN_STATUS_SUCCESS); } return 1; } return -1; } static int sme_sae_set_pmk(struct wpa_supplicant *wpa_s, const u8 *bssid) { wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for 4-way handshake"); wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, wpa_s->sme.sae.pmkid, bssid); if (wpa_s->conf->sae_pmkid_in_assoc) { /* Update the own RSNE contents now that we have set the PMK * and added a PMKSA cache entry based on the successfully * completed SAE exchange. In practice, this will add the PMKID * into RSNE. */ if (wpa_s->sme.assoc_req_ie_len + 2 + PMKID_LEN > sizeof(wpa_s->sme.assoc_req_ie)) { wpa_msg(wpa_s, MSG_WARNING, "RSN: Not enough room for inserting own PMKID into RSNE"); return -1; } if (wpa_insert_pmkid(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, wpa_s->sme.sae.pmkid) < 0) return -1; wpa_hexdump(MSG_DEBUG, "SME: Updated Association Request IEs", wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); } return 0; } void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, const u8 *auth_frame, size_t len) { const struct ieee80211_mgmt *header; size_t auth_length; header = (const struct ieee80211_mgmt *) auth_frame; auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth); if (len < auth_length) { /* Notify failure to the driver */ sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); return; } if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) { int res; res = sme_sae_auth( wpa_s, le_to_host16(header->u.auth.auth_transaction), le_to_host16(header->u.auth.status_code), header->u.auth.variable, len - auth_length, 1, header->sa); if (res < 0) { /* Notify failure to the driver */ sme_send_external_auth_status( wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); return; } if (res != 1) return; if (sme_sae_set_pmk(wpa_s, wpa_s->sme.ext_auth_bssid) < 0) return; } } #endif /* CONFIG_SAE */ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " "when network is not selected"); return; } if (wpa_s->wpa_state != WPA_AUTHENTICATING) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " "when not in authenticating state"); return; } if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with " "unexpected peer " MACSTR, MAC2STR(data->auth.peer)); return; } wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR " auth_type=%d auth_transaction=%d status_code=%d", MAC2STR(data->auth.peer), data->auth.auth_type, data->auth.auth_transaction, data->auth.status_code); wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", data->auth.ies, data->auth.ies_len); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); #ifdef CONFIG_SAE if (data->auth.auth_type == WLAN_AUTH_SAE) { int res; res = sme_sae_auth(wpa_s, data->auth.auth_transaction, data->auth.status_code, data->auth.ies, - data->auth.ies_len, 0, NULL); + data->auth.ies_len, 0, data->auth.peer); if (res < 0) { wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); } if (res != 1) return; if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0) return; } #endif /* CONFIG_SAE */ if (data->auth.status_code != WLAN_STATUS_SUCCESS) { char *ie_txt = NULL; if (data->auth.ies && data->auth.ies_len) { size_t buflen = 2 * data->auth.ies_len + 1; ie_txt = os_malloc(buflen); if (ie_txt) { wpa_snprintf_hex(ie_txt, buflen, data->auth.ies, data->auth.ies_len); } } wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR " auth_type=%u auth_transaction=%u status_code=%u%s%s", MAC2STR(data->auth.peer), data->auth.auth_type, data->auth.auth_transaction, data->auth.status_code, ie_txt ? " ie=" : "", ie_txt ? ie_txt : ""); os_free(ie_txt); #ifdef CONFIG_FILS if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS || wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) fils_connection_failure(wpa_s); #endif /* CONFIG_FILS */ if (data->auth.status_code != WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || wpa_s->sme.auth_alg == data->auth.auth_type || wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) { wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; } wpas_connect_work_done(wpa_s); switch (data->auth.auth_type) { case WLAN_AUTH_OPEN: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth"); wpa_supplicant_associate(wpa_s, wpa_s->current_bss, wpa_s->current_ssid); return; case WLAN_AUTH_SHARED_KEY: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth"); wpa_supplicant_associate(wpa_s, wpa_s->current_bss, wpa_s->current_ssid); return; default: return; } } #ifdef CONFIG_IEEE80211R if (data->auth.auth_type == WLAN_AUTH_FT) { const u8 *ric_ies = NULL; size_t ric_ies_len = 0; if (wpa_s->ric_ies) { ric_ies = wpabuf_head(wpa_s->ric_ies); ric_ies_len = wpabuf_len(wpa_s->ric_ies); } if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies, data->auth.ies_len, 0, data->auth.peer, ric_ies, ric_ies_len) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT Authentication response processing failed"); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d locally_generated=1", MAC2STR(wpa_s->pending_bssid), WLAN_REASON_DEAUTH_LEAVING); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); return; } } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_FILS if (data->auth.auth_type == WLAN_AUTH_FILS_SK || data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) { u16 expect_auth_type; expect_auth_type = wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; if (data->auth.auth_type != expect_auth_type) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FILS Authentication response used different auth alg (%u; expected %u)", data->auth.auth_type, expect_auth_type); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d locally_generated=1", MAC2STR(wpa_s->pending_bssid), WLAN_REASON_DEAUTH_LEAVING); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); return; } if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid, data->auth.ies, data->auth.ies_len) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FILS Authentication response processing failed"); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d locally_generated=1", MAC2STR(wpa_s->pending_bssid), WLAN_REASON_DEAUTH_LEAVING); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); return; } } #endif /* CONFIG_FILS */ sme_associate(wpa_s, ssid->mode, data->auth.peer, data->auth.auth_type); } #ifdef CONFIG_IEEE80211R static void remove_ie(u8 *buf, size_t *len, u8 eid) { u8 *pos, *next, *end; pos = (u8 *) get_ie(buf, *len, eid); if (pos) { next = pos + 2 + pos[1]; end = buf + *len; *len -= 2 + pos[1]; os_memmove(pos, next, end - next); } } #endif /* CONFIG_IEEE80211R */ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, const u8 *bssid, u16 auth_type) { struct wpa_driver_associate_params params; struct ieee802_11_elems elems; struct wpa_ssid *ssid = wpa_s->current_ssid; #ifdef CONFIG_FILS u8 nonces[2 * FILS_NONCE_LEN]; #endif /* CONFIG_FILS */ #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES struct ieee80211_vht_capabilities vhtcaps; struct ieee80211_vht_capabilities vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ os_memset(¶ms, 0, sizeof(params)); #ifdef CONFIG_FILS if (auth_type == WLAN_AUTH_FILS_SK || auth_type == WLAN_AUTH_FILS_SK_PFS) { struct wpabuf *buf; const u8 *snonce, *anonce; const unsigned int max_hlp = 20; struct wpabuf *hlp[max_hlp]; unsigned int i, num_hlp = 0; struct fils_hlp_req *req; dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, list) { hlp[num_hlp] = wpabuf_alloc(2 * ETH_ALEN + 6 + wpabuf_len(req->pkt)); if (!hlp[num_hlp]) break; wpabuf_put_data(hlp[num_hlp], req->dst, ETH_ALEN); wpabuf_put_data(hlp[num_hlp], wpa_s->own_addr, ETH_ALEN); wpabuf_put_data(hlp[num_hlp], "\xaa\xaa\x03\x00\x00\x00", 6); wpabuf_put_buf(hlp[num_hlp], req->pkt); num_hlp++; if (num_hlp >= max_hlp) break; } buf = fils_build_assoc_req(wpa_s->wpa, ¶ms.fils_kek, ¶ms.fils_kek_len, &snonce, &anonce, (const struct wpabuf **) hlp, num_hlp); for (i = 0; i < num_hlp; i++) wpabuf_free(hlp[i]); if (!buf) return; wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements", wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) { /* Remove RSNE and MDE to allow them to be overridden * with FILS+FT specific values from * fils_build_assoc_req(). */ remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_RSN); wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after RSNE removal", wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_MOBILITY_DOMAIN); wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after MDE removal", wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); } #endif /* CONFIG_IEEE80211R */ /* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */ if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) > sizeof(wpa_s->sme.assoc_req_ie)) { wpa_printf(MSG_ERROR, "FILS: Not enough buffer room for own AssocReq elements"); wpabuf_free(buf); return; } os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(buf), wpabuf_len(buf)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf); wpabuf_free(buf); wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements", wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); os_memcpy(nonces, snonce, FILS_NONCE_LEN); os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN); params.fils_nonces = nonces; params.fils_nonces_len = sizeof(nonces); } #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE #ifdef CONFIG_TESTING_OPTIONS if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len, WLAN_EID_EXT_OWE_DH_PARAM)) { wpa_printf(MSG_INFO, "TESTING: Override OWE DH element"); } else #endif /* CONFIG_TESTING_OPTIONS */ if (auth_type == WLAN_AUTH_OPEN && wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) { struct wpabuf *owe_ie; u16 group; if (ssid && ssid->owe_group) { group = ssid->owe_group; } else if (wpa_s->assoc_status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { if (wpa_s->last_owe_group == 19) group = 20; else if (wpa_s->last_owe_group == 20) group = 21; else group = OWE_DH_GROUP; } else { group = OWE_DH_GROUP; } wpa_s->last_owe_group = group; wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group); owe_ie = owe_build_assoc_req(wpa_s->wpa, group); if (!owe_ie) { wpa_printf(MSG_ERROR, "OWE: Failed to build IE for Association Request frame"); return; } if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) > sizeof(wpa_s->sme.assoc_req_ie)) { wpa_printf(MSG_ERROR, "OWE: Not enough buffer room for own Association Request frame elements"); wpabuf_free(owe_ie); return; } os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(owe_ie), wpabuf_len(owe_ie)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie); wpabuf_free(owe_ie); } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && ssid && ssid->dpp_netaccesskey && ssid->dpp_pfs != 2 && !ssid->dpp_pfs_fallback) { struct rsn_pmksa_cache_entry *pmksa; pmksa = pmksa_cache_get_current(wpa_s->wpa); if (!pmksa || !pmksa->dpp_pfs) goto pfs_fail; dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len); if (!wpa_s->dpp_pfs) { wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); /* Try to continue without PFS */ goto pfs_fail; } if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(wpa_s->dpp_pfs->ie) > sizeof(wpa_s->sme.assoc_req_ie)) { wpa_printf(MSG_ERROR, "DPP: Not enough buffer room for own Association Request frame elements"); dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = NULL; goto pfs_fail; } os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, wpabuf_head(wpa_s->dpp_pfs->ie), wpabuf_len(wpa_s->dpp_pfs->ie)); wpa_s->sme.assoc_req_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie); } pfs_fail: #endif /* CONFIG_DPP2 */ wpa_s->mscs_setup_done = false; if (wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS) && wpa_s->robust_av.valid_config) { struct wpabuf *mscs_ie; size_t mscs_ie_len, buf_len, *wpa_ie_len, max_ie_len; buf_len = 3 + /* MSCS descriptor IE header */ 1 + /* Request type */ 2 + /* User priority control */ 4 + /* Stream timeout */ 3 + /* TCLAS Mask IE header */ wpa_s->robust_av.frame_classifier_len; mscs_ie = wpabuf_alloc(buf_len); if (!mscs_ie) { wpa_printf(MSG_INFO, "MSCS: Failed to allocate MSCS IE"); goto mscs_fail; } wpa_ie_len = &wpa_s->sme.assoc_req_ie_len; max_ie_len = sizeof(wpa_s->sme.assoc_req_ie); wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie); if ((*wpa_ie_len + wpabuf_len(mscs_ie)) <= max_ie_len) { wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie); mscs_ie_len = wpabuf_len(mscs_ie); os_memcpy(wpa_s->sme.assoc_req_ie + *wpa_ie_len, wpabuf_head(mscs_ie), mscs_ie_len); *wpa_ie_len += mscs_ie_len; } wpabuf_free(mscs_ie); } mscs_fail: if (ssid && ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; multi_ap_ie_len = add_multi_ap_ie( wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len, MULTI_AP_BACKHAUL_STA); if (multi_ap_ie_len == 0) { wpa_printf(MSG_ERROR, "Multi-AP: Failed to build Multi-AP IE"); return; } wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len; } params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; params.freq.freq = wpa_s->sme.freq; params.bg_scan_period = ssid ? ssid->bg_scan_period : -1; params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? wpa_s->sme.assoc_req_ie : NULL; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs", params.wpa_ie, params.wpa_ie_len); params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = wpa_s->group_cipher; params.mgmt_group_suite = wpa_s->mgmt_group_cipher; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; #ifdef CONFIG_HT_OVERRIDES os_memset(&htcaps, 0, sizeof(htcaps)); os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); params.htcaps = (u8 *) &htcaps; params.htcaps_mask = (u8 *) &htcaps_mask; wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES os_memset(&vhtcaps, 0, sizeof(vhtcaps)); os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask)); params.vhtcaps = &vhtcaps; params.vhtcaps_mask = &vhtcaps_mask; wpa_supplicant_apply_vht_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_VHT_OVERRIDES */ #ifdef CONFIG_HE_OVERRIDES wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_HE_OVERRIDES */ #ifdef CONFIG_IEEE80211R if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies && get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len, WLAN_EID_RIC_DATA)) { /* There seems to be a pretty inconvenient bug in the Linux * kernel IE splitting functionality when RIC is used. For now, * skip correct behavior in IE construction here (i.e., drop the * additional non-FT-specific IEs) to avoid kernel issues. This * is fine since RIC is used only for testing purposes in the * current implementation. */ wpa_printf(MSG_INFO, "SME: Linux kernel workaround - do not try to include additional IEs with RIC"); params.wpa_ie = wpa_s->sme.ft_ies; params.wpa_ie_len = wpa_s->sme.ft_ies_len; } else if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { const u8 *rm_en, *pos, *end; size_t rm_en_len = 0; u8 *rm_en_dup = NULL, *wpos; /* Remove RSNE, MDE, FTE to allow them to be overridden with * FT specific values */ remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_RSN); remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_MOBILITY_DOMAIN); remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_FAST_BSS_TRANSITION); rm_en = get_ie(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len, WLAN_EID_RRM_ENABLED_CAPABILITIES); if (rm_en) { /* Need to remove RM Enabled Capabilities element as * well temporarily, so that it can be placed between * RSNE and MDE. */ rm_en_len = 2 + rm_en[1]; rm_en_dup = os_memdup(rm_en, rm_en_len); remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_RRM_ENABLED_CAPABILITIES); } wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs after FT IE removal", wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); if (wpa_s->sme.assoc_req_ie_len + wpa_s->sme.ft_ies_len + rm_en_len > sizeof(wpa_s->sme.assoc_req_ie)) { wpa_printf(MSG_ERROR, "SME: Not enough buffer room for FT IEs in Association Request frame"); os_free(rm_en_dup); return; } os_memmove(wpa_s->sme.assoc_req_ie + wpa_s->sme.ft_ies_len + rm_en_len, wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); pos = wpa_s->sme.ft_ies; end = pos + wpa_s->sme.ft_ies_len; wpos = wpa_s->sme.assoc_req_ie; if (*pos == WLAN_EID_RSN) { os_memcpy(wpos, pos, 2 + pos[1]); wpos += 2 + pos[1]; pos += 2 + pos[1]; } if (rm_en_dup) { os_memcpy(wpos, rm_en_dup, rm_en_len); wpos += rm_en_len; os_free(rm_en_dup); } os_memcpy(wpos, pos, end - pos); wpa_s->sme.assoc_req_ie_len += wpa_s->sme.ft_ies_len + rm_en_len; params.wpa_ie = wpa_s->sme.assoc_req_ie; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs after FT override", params.wpa_ie, params.wpa_ie_len); } #endif /* CONFIG_IEEE80211R */ params.mode = mode; params.mgmt_frame_protection = wpa_s->sme.mfp; params.rrm_used = wpa_s->rrm.rrm_used; if (wpa_s->sme.prev_bssid_set) params.prev_bssid = wpa_s->sme.prev_bssid; wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "", params.freq.freq); wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); if (params.wpa_ie == NULL || ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!"); os_memset(&elems, 0, sizeof(elems)); } if (elems.rsn_ie) { params.wpa_proto = WPA_PROTO_RSN; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2, elems.rsn_ie_len + 2); } else if (elems.wpa_ie) { params.wpa_proto = WPA_PROTO_WPA; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2, elems.wpa_ie_len + 2); } else if (elems.osen) { params.wpa_proto = WPA_PROTO_OSEN; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2, elems.osen_len + 2); } else wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); if (elems.rsnxe) wpa_sm_set_assoc_rsnxe(wpa_s->wpa, elems.rsnxe - 2, elems.rsnxe_len + 2); else wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); if (ssid && ssid->p2p_group) params.p2p = 1; if (wpa_s->p2pdev->set_sta_uapsd) params.uapsd = wpa_s->p2pdev->sta_uapsd; else params.uapsd = -1; if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the " "driver failed"); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); return; } eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s, NULL); #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(wpa_s->last_assoc_req_wpa_ie); wpa_s->last_assoc_req_wpa_ie = NULL; if (params.wpa_ie) wpa_s->last_assoc_req_wpa_ie = wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len); #endif /* CONFIG_TESTING_OPTIONS */ } int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, const u8 *ies, size_t ies_len) { if (md == NULL || ies == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain"); os_free(wpa_s->sme.ft_ies); wpa_s->sme.ft_ies = NULL; wpa_s->sme.ft_ies_len = 0; wpa_s->sme.ft_used = 0; return 0; } os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len); os_free(wpa_s->sme.ft_ies); wpa_s->sme.ft_ies = os_memdup(ies, ies_len); if (wpa_s->sme.ft_ies == NULL) return -1; wpa_s->sme.ft_ies_len = ies_len; return 0; } static void sme_deauth(struct wpa_supplicant *wpa_s) { int bssid_changed; bssid_changed = !is_zero_ether_addr(wpa_s->bssid); if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, WLAN_REASON_DEAUTH_LEAVING) < 0) { wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver " "failed"); } wpa_s->sme.prev_bssid_set = 0; wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); if (bssid_changed) wpas_notify_bssid_changed(wpa_s); } void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: " "status code %d", MAC2STR(wpa_s->pending_bssid), data->assoc_reject.status_code); eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); #ifdef CONFIG_SAE if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid && wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication"); wpa_sm_aborted_cached(wpa_s->wpa); wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); if (wpa_s->current_bss) { struct wpa_bss *bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, WLAN_REASON_DEAUTH_LEAVING); wpas_connect_work_done(wpa_s); wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_connect(wpa_s, bss, ssid); return; } } #endif /* CONFIG_SAE */ /* * For now, unconditionally terminate the previous authentication. In * theory, this should not be needed, but mac80211 gets quite confused * if the authentication is left pending.. Some roaming cases might * benefit from using the previous authentication, so this could be * optimized in the future. */ sme_deauth(wpa_s); } void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out"); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); } void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out"); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); } void sme_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received"); if (wpa_s->sme.prev_bssid_set) { /* * cfg80211/mac80211 can get into somewhat confused state if * the AP only disassociates us and leaves us in authenticated * state. For now, force the state to be cleared to avoid * confusing errors if we try to associate with the AP again. */ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear " "driver state"); wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid, WLAN_REASON_DEAUTH_LEAVING); } } static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->wpa_state == WPA_AUTHENTICATING) { wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout"); sme_deauth(wpa_s); } } static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->wpa_state == WPA_ASSOCIATING) { wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout"); sme_deauth(wpa_s); } } void sme_state_changed(struct wpa_supplicant *wpa_s) { /* Make sure timers are cleaned up appropriately. */ if (wpa_s->wpa_state != WPA_ASSOCIATING) eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); if (wpa_s->wpa_state != WPA_AUTHENTICATING) eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); } void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, const u8 *prev_pending_bssid) { /* * mac80211-workaround to force deauth on failed auth cmd, * requires us to remain in authenticating state to allow the * second authentication attempt to be continued properly. */ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication " "to proceed after disconnection event"); wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN); /* * Re-arm authentication timer in case auth fails for whatever reason. */ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, NULL); } void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) { wpa_s->sme.prev_bssid_set = 0; #ifdef CONFIG_SAE wpabuf_free(wpa_s->sme.sae_token); wpa_s->sme.sae_token = NULL; sae_clear_data(&wpa_s->sme.sae); #endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211R if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used) sme_update_ft_ies(wpa_s, NULL, NULL, 0); #endif /* CONFIG_IEEE80211R */ sme_stop_sa_query(wpa_s); } void sme_deinit(struct wpa_supplicant *wpa_s) { sme_clear_on_disassoc(wpa_s); #ifdef CONFIG_SAE os_free(wpa_s->sme.sae_rejected_groups); wpa_s->sme.sae_rejected_groups = NULL; #endif /* CONFIG_SAE */ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); } static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s, const u8 *chan_list, u8 num_channels, u8 num_intol) { struct ieee80211_2040_bss_coex_ie *bc_ie; struct ieee80211_2040_intol_chan_report *ic_report; struct wpabuf *buf; wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR " (num_channels=%u num_intol=%u)", MAC2STR(wpa_s->bssid), num_channels, num_intol); wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels", chan_list, num_channels); buf = wpabuf_alloc(2 + /* action.category + action_code */ sizeof(struct ieee80211_2040_bss_coex_ie) + sizeof(struct ieee80211_2040_intol_chan_report) + num_channels); if (buf == NULL) return; wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX); bc_ie = wpabuf_put(buf, sizeof(*bc_ie)); bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE; bc_ie->length = 1; if (num_intol) bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ; if (num_channels > 0) { ic_report = wpabuf_put(buf, sizeof(*ic_report)); ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT; ic_report->length = num_channels + 1; ic_report->op_class = 0; os_memcpy(wpabuf_put(buf, num_channels), chan_list, num_channels); } if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send 20/40 BSS Coexistence frame"); } wpabuf_free(buf); } int sme_proc_obss_scan(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { const u8 *ie; u8 chan_list[P2P_MAX_CHANNELS], channel; u8 num_channels = 0, num_intol = 0, i; size_t j; int pri_freq, sec_freq; if (!wpa_s->sme.sched_obss_scan) return 0; wpa_s->sme.sched_obss_scan = 0; if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED) return 1; /* * Check whether AP uses regulatory triplet or channel triplet in * country info. Right now the operating class of the BSS channel * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12), * based on the assumption that operating class triplet is not used in * beacon frame. If the First Channel Number/Operating Extension * Identifier octet has a positive integer value of 201 or greater, * then its operating class triplet. * * TODO: If Supported Operating Classes element is present in beacon * frame, have to lookup operating class in Annex E and fill them in * 2040 coex frame. */ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY); if (ie && (ie[1] >= 6) && (ie[5] >= 201)) return 1; os_memset(chan_list, 0, sizeof(chan_list)); pri_freq = wpa_s->assoc_freq; switch (wpa_s->sme.ht_sec_chan) { case HT_SEC_CHAN_ABOVE: sec_freq = pri_freq + 20; break; case HT_SEC_CHAN_BELOW: sec_freq = pri_freq - 20; break; case HT_SEC_CHAN_UNKNOWN: default: wpa_msg(wpa_s, MSG_WARNING, "Undefined secondary channel: drop OBSS scan results"); return 1; } for (j = 0; j < scan_res->num; j++) { struct wpa_scan_res *bss = scan_res->res[j]; enum hostapd_hw_mode mode; int res; /* Skip other band bss */ mode = ieee80211_freq_to_chan(bss->freq, &channel); if (mode != HOSTAPD_MODE_IEEE80211G && mode != HOSTAPD_MODE_IEEE80211B) continue; res = check_bss_coex_40mhz(bss, pri_freq, sec_freq); if (res) { if (res == 2) num_intol++; /* Check whether the channel is already considered */ for (i = 0; i < num_channels; i++) { if (channel == chan_list[i]) break; } if (i != num_channels) continue; chan_list[num_channels++] = channel; } } sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol); return 1; } static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { /* Include only affected channels */ struct hostapd_hw_modes *mode; int count, i; int start, end; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, HOSTAPD_MODE_IEEE80211G, false); if (mode == NULL) { /* No channels supported in this band - use empty list */ params->freqs = os_zalloc(sizeof(int)); return; } if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN && wpa_s->current_bss) { const u8 *ie; ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION); if (ie && ie[1] >= 2) { u8 o; o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE; else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW; } } start = wpa_s->assoc_freq - 10; end = wpa_s->assoc_freq + 10; switch (wpa_s->sme.ht_sec_chan) { case HT_SEC_CHAN_UNKNOWN: /* HT40+ possible on channels 1..9 */ if (wpa_s->assoc_freq <= 2452) start -= 20; /* HT40- possible on channels 5-13 */ if (wpa_s->assoc_freq >= 2432) end += 20; break; case HT_SEC_CHAN_ABOVE: end += 20; break; case HT_SEC_CHAN_BELOW: start -= 20; break; } wpa_printf(MSG_DEBUG, "OBSS: assoc_freq %d possible affected range %d-%d", wpa_s->assoc_freq, start, end); params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; for (count = 0, i = 0; i < mode->num_channels; i++) { int freq; if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) continue; freq = mode->channels[i].freq; if (freq - 10 >= end || freq + 10 <= start) continue; /* not affected */ params->freqs[count++] = freq; } } static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_driver_scan_params params; if (!wpa_s->current_bss) { wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request"); return; } os_memset(¶ms, 0, sizeof(params)); wpa_obss_scan_freqs_list(wpa_s, ¶ms); params.low_priority = 1; wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan"); if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan"); else wpa_s->sme.sched_obss_scan = 1; os_free(params.freqs); eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, sme_obss_scan_timeout, wpa_s, NULL); } void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) { const u8 *ie; struct wpa_bss *bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; struct hostapd_hw_modes *hw_mode = NULL; int i; eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); wpa_s->sme.sched_obss_scan = 0; wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN; if (!enable) return; /* * Schedule OBSS scan if driver is using station SME in wpa_supplicant * or it expects OBSS scan to be performed by wpa_supplicant. */ if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) || ssid == NULL || ssid->mode != WPAS_MODE_INFRA) return; if (!wpa_s->hw.modes) return; /* only HT caps in 11g mode are relevant */ for (i = 0; i < wpa_s->hw.num_modes; i++) { hw_mode = &wpa_s->hw.modes[i]; if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G) break; } /* Driver does not support HT40 for 11g or doesn't have 11g. */ if (i == wpa_s->hw.num_modes || !hw_mode || !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) return; if (bss == NULL || bss->freq < 2400 || bss->freq > 2500) return; /* Not associated on 2.4 GHz band */ /* Check whether AP supports HT40 */ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP); if (!ie || ie[1] < 2 || !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) return; /* AP does not support HT40 */ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS); if (!ie || ie[1] < 14) return; /* AP does not request OBSS scans */ wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6); if (wpa_s->sme.obss_scan_int < 10) { wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u " "replaced with the minimum 10 sec", wpa_s->sme.obss_scan_int); wpa_s->sme.obss_scan_int = 10; } wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec", wpa_s->sme.obss_scan_int); eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, sme_obss_scan_timeout, wpa_s, NULL); } static const unsigned int sa_query_max_timeout = 1000; static const unsigned int sa_query_retry_timeout = 201; static const unsigned int sa_query_ch_switch_max_delay = 5000; /* in usec */ static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) { u32 tu; struct os_reltime now, passed; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->sme.sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; if (sa_query_max_timeout < tu) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out"); sme_stop_sa_query(wpa_s); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID); return 1; } return 0; } static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, const u8 *trans_id) { u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN]; u8 req_len = 2 + WLAN_SA_QUERY_TR_ID_LEN; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to " MACSTR, MAC2STR(wpa_s->bssid)); wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); req[0] = WLAN_ACTION_SA_QUERY; req[1] = WLAN_SA_QUERY_REQUEST; os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN); #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(wpa_s->wpa)) { struct wpa_channel_info ci; if (wpa_drv_channel_info(wpa_s, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info for OCI element in SA Query Request frame"); return; } #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->oci_freq_override_saquery_req) { wpa_printf(MSG_INFO, "TEST: Override SA Query Request OCI frequency %d -> %d MHz", ci.frequency, wpa_s->oci_freq_override_saquery_req); ci.frequency = wpa_s->oci_freq_override_saquery_req; } #endif /* CONFIG_TESTING_OPTIONS */ if (ocv_insert_extended_oci(&ci, req + req_len) < 0) return; req_len += OCV_OCI_EXTENDED_LEN; } #endif /* CONFIG_OCV */ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, req, req_len, 0) < 0) wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query " "Request"); } static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; unsigned int timeout, sec, usec; u8 *trans_id, *nbuf; if (wpa_s->sme.sa_query_count > 0 && sme_check_sa_query_timeout(wpa_s)) return; nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id, wpa_s->sme.sa_query_count + 1, WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) { sme_stop_sa_query(wpa_s); return; } if (wpa_s->sme.sa_query_count == 0) { /* Starting a new SA Query procedure */ os_get_reltime(&wpa_s->sme.sa_query_start); } trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; wpa_s->sme.sa_query_trans_id = nbuf; wpa_s->sme.sa_query_count++; if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { wpa_printf(MSG_DEBUG, "Could not generate SA Query ID"); sme_stop_sa_query(wpa_s); return; } timeout = sa_query_retry_timeout; sec = ((timeout / 1000) * 1024) / 1000; usec = (timeout % 1000) * 1024; eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL); wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d", wpa_s->sme.sa_query_count); sme_send_sa_query_req(wpa_s, trans_id); } static void sme_start_sa_query(struct wpa_supplicant *wpa_s) { sme_sa_query_timer(wpa_s, NULL); } static void sme_stop_sa_query(struct wpa_supplicant *wpa_s) { if (wpa_s->sme.sa_query_trans_id) wpa_dbg(wpa_s, MSG_DEBUG, "SME: Stop SA Query"); eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); os_free(wpa_s->sme.sa_query_trans_id); wpa_s->sme.sa_query_trans_id = NULL; wpa_s->sme.sa_query_count = 0; } void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *da, u16 reason_code) { struct wpa_ssid *ssid; struct os_reltime now; if (wpa_s->wpa_state != WPA_COMPLETED) return; ssid = wpa_s->current_ssid; if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) return; if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) return; if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA && reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA) return; if (wpa_s->sme.sa_query_count > 0) return; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->disable_sa_query) return; #endif /* CONFIG_TESTING_OPTIONS */ os_get_reltime(&now); if (wpa_s->sme.last_unprot_disconnect.sec && !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10)) return; /* limit SA Query procedure frequency */ wpa_s->sme.last_unprot_disconnect = now; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - " "possible AP/STA state mismatch - trigger SA Query"); sme_start_sa_query(wpa_s); } void sme_event_ch_switch(struct wpa_supplicant *wpa_s) { unsigned int usec; u32 _rand; if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_sm_ocv_enabled(wpa_s->wpa)) return; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Channel switch completed - trigger new SA Query to verify new operating channel"); sme_stop_sa_query(wpa_s); if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) _rand = os_random(); usec = _rand % (sa_query_ch_switch_max_delay + 1); eloop_register_timeout(0, usec, sme_sa_query_timer, wpa_s, NULL); } static void sme_process_sa_query_request(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *data, size_t len) { u8 resp[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN]; u8 resp_len = 2 + WLAN_SA_QUERY_TR_ID_LEN; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Response to " MACSTR, MAC2STR(wpa_s->bssid)); resp[0] = WLAN_ACTION_SA_QUERY; resp[1] = WLAN_SA_QUERY_RESPONSE; os_memcpy(resp + 2, data + 1, WLAN_SA_QUERY_TR_ID_LEN); #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(wpa_s->wpa)) { struct wpa_channel_info ci; if (wpa_drv_channel_info(wpa_s, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info for OCI element in SA Query Response frame"); return; } #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->oci_freq_override_saquery_resp) { wpa_printf(MSG_INFO, "TEST: Override SA Query Response OCI frequency %d -> %d MHz", ci.frequency, wpa_s->oci_freq_override_saquery_resp); ci.frequency = wpa_s->oci_freq_override_saquery_resp; } #endif /* CONFIG_TESTING_OPTIONS */ if (ocv_insert_extended_oci(&ci, resp + resp_len) < 0) return; resp_len += OCV_OCI_EXTENDED_LEN; } #endif /* CONFIG_OCV */ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, resp, resp_len, 0) < 0) wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query Response"); } static void sme_process_sa_query_response(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *data, size_t len) { int i; if (!wpa_s->sme.sa_query_trans_id) return; wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from " MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) return; for (i = 0; i < wpa_s->sme.sa_query_count; i++) { if (os_memcmp(wpa_s->sme.sa_query_trans_id + i * WLAN_SA_QUERY_TR_ID_LEN, data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0) break; } if (i >= wpa_s->sme.sa_query_count) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query " "transaction identifier found"); return; } wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received " "from " MACSTR, MAC2STR(sa)); sme_stop_sa_query(wpa_s); } void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa, const u8 *data, size_t len) { if (len < 1 + WLAN_SA_QUERY_TR_ID_LEN) return; if (is_multicast_ether_addr(da)) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore group-addressed SA Query frame (A1=" MACSTR " A2=" MACSTR ")", MAC2STR(da), MAC2STR(sa)); return; } wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query frame from " MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); #ifdef CONFIG_OCV if (wpa_sm_ocv_enabled(wpa_s->wpa)) { struct ieee802_11_elems elems; struct wpa_channel_info ci; if (ieee802_11_parse_elems(data + 1 + WLAN_SA_QUERY_TR_ID_LEN, len - 1 - WLAN_SA_QUERY_TR_ID_LEN, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "SA Query: Failed to parse elements"); return; } if (wpa_drv_channel_info(wpa_s, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info to validate received OCI in SA Query Action frame"); return; } if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, channel_width_to_int(ci.chanwidth), ci.seg1_idx) != OCI_SUCCESS) { wpa_msg(wpa_s, MSG_INFO, OCV_FAILURE "addr=" MACSTR " frame=saquery%s error=%s", MAC2STR(sa), data[0] == WLAN_SA_QUERY_REQUEST ? "req" : "resp", ocv_errorstr); return; } } #endif /* CONFIG_OCV */ if (data[0] == WLAN_SA_QUERY_REQUEST) sme_process_sa_query_request(wpa_s, sa, data, len); else if (data[0] == WLAN_SA_QUERY_RESPONSE) sme_process_sa_query_response(wpa_s, sa, data, len); } diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index c4c6651d42e6..8a1a44690ba5 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1,1970 +1,1970 @@ /* * wpa_supplicant - WNM * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/ocv.h" #include "rsn_supp/wpa.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" #include "ctrl_iface.h" #include "bss.h" #include "wnm_sta.h" #include "notify.h" #include "hs20_supplicant.h" #define MAX_TFS_IE_LEN 1024 #define WNM_MAX_NEIGHBOR_REPORT 10 #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */ /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, u16 *buf_len, enum wnm_oper oper) { wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); } /* set the TFS IE to driver */ static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *buf, u16 buf_len, enum wnm_oper oper) { u16 len = buf_len; wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len); } /* MLME-SLEEPMODE.request */ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, u8 action, u16 intval, struct wpabuf *tfs_req) { struct ieee80211_mgmt *mgmt; int res; size_t len; struct wnm_sleep_element *wnmsleep_ie; u8 *wnmtfs_ie, *oci_ie; u8 wnmsleep_ie_len, oci_ie_len; u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : WNM_SLEEP_TFS_REQ_IE_NONE; wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " "action=%s to " MACSTR, action == 0 ? "enter" : "exit", MAC2STR(wpa_s->bssid)); /* WNM-Sleep Mode IE */ wnmsleep_ie_len = sizeof(struct wnm_sleep_element); wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); if (wnmsleep_ie == NULL) return -1; wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; wnmsleep_ie->len = wnmsleep_ie_len - 2; wnmsleep_ie->action_type = action; wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; wnmsleep_ie->intval = host_to_le16(intval); wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", (u8 *) wnmsleep_ie, wnmsleep_ie_len); /* TFS IE(s) */ if (tfs_req) { wnmtfs_ie_len = wpabuf_len(tfs_req); wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len); if (wnmtfs_ie == NULL) { os_free(wnmsleep_ie); return -1; } } else { wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); if (wnmtfs_ie == NULL) { os_free(wnmsleep_ie); return -1; } if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, tfs_oper)) { wnmtfs_ie_len = 0; os_free(wnmtfs_ie); wnmtfs_ie = NULL; } } wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", (u8 *) wnmtfs_ie, wnmtfs_ie_len); oci_ie = NULL; oci_ie_len = 0; #ifdef CONFIG_OCV if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) { struct wpa_channel_info ci; if (wpa_drv_channel_info(wpa_s, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); os_free(wnmsleep_ie); os_free(wnmtfs_ie); return -1; } #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->oci_freq_override_wnm_sleep) { wpa_printf(MSG_INFO, "TEST: Override OCI KDE frequency %d -> %d MHz", ci.frequency, wpa_s->oci_freq_override_wnm_sleep); ci.frequency = wpa_s->oci_freq_override_wnm_sleep; } #endif /* CONFIG_TESTING_OPTIONS */ oci_ie_len = OCV_OCI_EXTENDED_LEN; oci_ie = os_zalloc(oci_ie_len); if (!oci_ie) { wpa_printf(MSG_WARNING, "Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame"); os_free(wnmsleep_ie); os_free(wnmtfs_ie); return -1; } if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { os_free(wnmsleep_ie); os_free(wnmtfs_ie); os_free(oci_ie); return -1; } } #endif /* CONFIG_OCV */ mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Request action frame"); os_free(wnmsleep_ie); os_free(wnmtfs_ie); return -1; } os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ if (wnmtfs_ie_len > 0) { os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); } #ifdef CONFIG_OCV /* copy OCV OCI here */ if (oci_ie_len > 0) { os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len); } #endif /* CONFIG_OCV */ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, &mgmt->u.action.category, len, 0); if (res < 0) wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " "(action=%d, intval=%d)", action, intval); else wpa_s->wnmsleep_used = 1; os_free(wnmsleep_ie); os_free(wnmtfs_ie); os_free(oci_ie); os_free(mgmt); return res; } static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, const u8 *tfsresp_ie_start, const u8 *tfsresp_ie_end) { wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, wpa_s->bssid, NULL, NULL); /* remove GTK/IGTK ?? */ /* set the TFS Resp IE(s) */ if (tfsresp_ie_start && tfsresp_ie_end && tfsresp_ie_end - tfsresp_ie_start >= 0) { u16 tfsresp_ie_len; tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - tfsresp_ie_start; wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); /* pass the TFS Resp IE(s) to driver for processing */ if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, tfsresp_ie_start, tfsresp_ie_len, WNM_SLEEP_TFS_RESP_IE_SET)) wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); } } static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, const u8 *frm, u16 key_len_total) { u8 *ptr, *end; u8 gtk_len; wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, NULL, NULL); /* Install GTK/IGTK */ /* point to key data field */ ptr = (u8 *) frm + 1 + 2; end = ptr + key_len_total; wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { wpa_msg(wpa_s, MSG_INFO, "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); return; } while (end - ptr > 1) { if (2 + ptr[1] > end - ptr) { wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " "length"); if (end > ptr) { wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", ptr, end - ptr); } break; } if (*ptr == WNM_SLEEP_SUBELEM_GTK) { if (ptr[1] < 11 + 5) { wpa_printf(MSG_DEBUG, "WNM: Too short GTK " "subelem"); break; } gtk_len = *(ptr + 4); if (ptr[1] < 11 + gtk_len || gtk_len < 5 || gtk_len > 32) { wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " "subelem"); break; } wpa_wnmsleep_install_key( wpa_s->wpa, WNM_SLEEP_SUBELEM_GTK, ptr); ptr += 13 + gtk_len; } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " "subelem"); break; } wpa_wnmsleep_install_key(wpa_s->wpa, WNM_SLEEP_SUBELEM_IGTK, ptr); ptr += 10 + WPA_IGTK_LEN; } else if (*ptr == WNM_SLEEP_SUBELEM_BIGTK) { if (ptr[1] < 2 + 6 + WPA_BIGTK_LEN) { wpa_printf(MSG_DEBUG, "WNM: Too short BIGTK subelem"); break; } wpa_wnmsleep_install_key(wpa_s->wpa, WNM_SLEEP_SUBELEM_BIGTK, ptr); ptr += 10 + WPA_BIGTK_LEN; } else break; /* skip the loop */ } } static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, const u8 *frm, int len) { /* * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ const u8 *pos = frm; /* point to payload after the action field */ u16 key_len_total; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ const u8 *tfsresp_ie_start = NULL; const u8 *tfsresp_ie_end = NULL; #ifdef CONFIG_OCV const u8 *oci_ie = NULL; u8 oci_ie_len = 0; #endif /* CONFIG_OCV */ size_t left; if (!wpa_s->wnmsleep_used) { wpa_printf(MSG_DEBUG, "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); return; } if (len < 3) return; key_len_total = WPA_GET_LE16(frm + 1); wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", frm[0], key_len_total); left = len - 3; if (key_len_total > left) { wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); return; } pos += 3 + key_len_total; while (pos - frm + 1 < len) { u8 ie_len = *(pos + 1); if (2 + ie_len > frm + len - pos) { wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); break; } wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_RESP) { if (!tfsresp_ie_start) tfsresp_ie_start = pos; tfsresp_ie_end = pos; #ifdef CONFIG_OCV } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && pos[2] == WLAN_EID_EXT_OCV_OCI) { oci_ie = pos + 3; oci_ie_len = ie_len - 1; #endif /* CONFIG_OCV */ } else wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); pos += ie_len + 2; } if (!wnmsleep_ie) { wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); return; } #ifdef CONFIG_OCV if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) { struct wpa_channel_info ci; if (wpa_drv_channel_info(wpa_s, &ci) != 0) { wpa_msg(wpa_s, MSG_WARNING, "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); return; } if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, channel_width_to_int(ci.chanwidth), ci.seg1_idx) != OCI_SUCCESS) { wpa_msg(wpa_s, MSG_WARNING, "WNM: OCV failed: %s", ocv_errorstr); return; } } #endif /* CONFIG_OCV */ wpa_s->wnmsleep_used = 0; if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " "frame (action=%d, intval=%d)", wnmsleep_ie->action_type, wnmsleep_ie->intval); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, tfsresp_ie_end); } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); } } else { wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " "(action=%d, intval=%d)", wnmsleep_ie->action_type, wnmsleep_ie->intval); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, wpa_s->bssid, NULL, NULL); else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, wpa_s->bssid, NULL, NULL); } } void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) { int i; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); } wpa_s->wnm_num_neighbor_report = 0; os_free(wpa_s->wnm_neighbor_report_elements); wpa_s->wnm_neighbor_report_elements = NULL; wpabuf_free(wpa_s->coloc_intf_elems); wpa_s->coloc_intf_elems = NULL; } static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, u8 id, u8 elen, const u8 *pos) { switch (id) { case WNM_NEIGHBOR_TSF: if (elen < 2 + 2) { wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); break; } rep->tsf_offset = WPA_GET_LE16(pos); rep->beacon_int = WPA_GET_LE16(pos + 2); rep->tsf_present = 1; break; case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: if (elen < 2) { wpa_printf(MSG_DEBUG, "WNM: Too short condensed " "country string"); break; } os_memcpy(rep->country, pos, 2); rep->country_present = 1; break; case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: if (elen < 1) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " "candidate"); break; } rep->preference = pos[0]; rep->preference_present = 1; break; case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: if (elen < 10) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination duration"); break; } rep->bss_term_tsf = WPA_GET_LE64(pos); rep->bss_term_dur = WPA_GET_LE16(pos + 8); rep->bss_term_present = 1; break; case WNM_NEIGHBOR_BEARING: if (elen < 8) { wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " "bearing"); break; } rep->bearing = WPA_GET_LE16(pos); rep->distance = WPA_GET_LE32(pos + 2); rep->rel_height = WPA_GET_LE16(pos + 2 + 4); rep->bearing_present = 1; break; case WNM_NEIGHBOR_MEASUREMENT_PILOT: if (elen < 1) { wpa_printf(MSG_DEBUG, "WNM: Too short measurement " "pilot"); break; } os_free(rep->meas_pilot); rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); if (rep->meas_pilot == NULL) break; rep->meas_pilot->measurement_pilot = pos[0]; rep->meas_pilot->subelem_len = elen - 1; os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); break; case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: if (elen < 5) { wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " "capabilities"); break; } os_memcpy(rep->rm_capab, pos, 5); rep->rm_capab_present = 1; break; case WNM_NEIGHBOR_MULTIPLE_BSSID: if (elen < 1) { wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); break; } os_free(rep->mul_bssid); rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); if (rep->mul_bssid == NULL) break; rep->mul_bssid->max_bssid_indicator = pos[0]; rep->mul_bssid->subelem_len = elen - 1; os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); break; } } static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) { struct wpa_bss *bss = wpa_s->current_bss; const char *country = NULL; int freq; if (bss) { const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); if (elem && elem[1] >= 2) country = (const char *) (elem + 2); } freq = ieee80211_chan_to_freq(country, op_class, chan); if (freq <= 0 && op_class == 0) { /* * Some APs do not advertise correct operating class * information. Try to determine the most likely operating * frequency based on the channel number. */ if (chan >= 1 && chan <= 13) freq = 2407 + chan * 5; else if (chan == 14) freq = 2484; else if (chan >= 36 && chan <= 177) freq = 5000 + chan * 5; } return freq; } static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, const u8 *pos, u8 len, struct neighbor_report *rep) { u8 left = len; if (left < 13) { wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); return; } os_memcpy(rep->bssid, pos, ETH_ALEN); rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); rep->regulatory_class = *(pos + 10); rep->channel_number = *(pos + 11); rep->phy_type = *(pos + 12); pos += 13; left -= 13; while (left >= 2) { u8 id, elen; id = *pos++; elen = *pos++; wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); left -= 2; if (elen > left) { wpa_printf(MSG_DEBUG, "WNM: Truncated neighbor report subelement"); break; } wnm_parse_neighbor_report_elem(rep, id, elen, pos); left -= elen; pos += elen; } rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, rep->channel_number); } static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) { unsigned int i; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; } static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s) { unsigned int i; struct neighbor_report *nei; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { nei = &wpa_s->wnm_neighbor_report_elements[i]; if (nei->acceptable) return wpa_bss_get_bssid(wpa_s, nei->bssid); } return NULL; } #ifdef CONFIG_MBO static struct wpa_bss * get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, enum mbo_transition_reject_reason *reason) { struct wpa_bss *target = NULL; struct wpa_bss_trans_info params; struct wpa_bss_candidate_info *info = NULL; struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; u8 *first_candidate_bssid = NULL, *pos; unsigned int i; params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; params.n_candidates = 0; params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); if (!params.bssid) return NULL; pos = params.bssid; for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { if (nei->is_first) first_candidate_bssid = nei->bssid; if (!nei->acceptable) continue; os_memcpy(pos, nei->bssid, ETH_ALEN); pos += ETH_ALEN; params.n_candidates++; } if (!params.n_candidates) goto end; info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); if (!info) { /* If failed to get candidate BSS transition status from driver, * get the first acceptable candidate from wpa_supplicant. */ target = wpa_bss_get_bssid(wpa_s, params.bssid); goto end; } /* Get the first acceptable candidate from driver */ for (i = 0; i < info->num; i++) { if (info->candidates[i].is_accept) { target = wpa_bss_get_bssid(wpa_s, info->candidates[i].bssid); goto end; } } /* If Disassociation Imminent is set and driver rejects all the * candidate select first acceptable candidate which has * rssi > disassoc_imminent_rssi_threshold */ if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { for (i = 0; i < info->num; i++) { target = wpa_bss_get_bssid(wpa_s, info->candidates[i].bssid); if (target && (target->level < wpa_s->conf->disassoc_imminent_rssi_threshold)) continue; goto end; } } /* While sending BTM reject use reason code of the first candidate * received in BTM request frame */ if (reason) { for (i = 0; i < info->num; i++) { if (first_candidate_bssid && os_memcmp(first_candidate_bssid, info->candidates[i].bssid, ETH_ALEN) == 0) { *reason = info->candidates[i].reject_reason; break; } } } target = NULL; end: os_free(params.bssid); if (info) { os_free(info->candidates); os_free(info); } return target; } #endif /* CONFIG_MBO */ static struct wpa_bss * compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, enum mbo_transition_reject_reason *reason) { u8 i; struct wpa_bss *bss = wpa_s->current_bss; struct wpa_bss *target; if (!bss) return NULL; wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); wnm_clear_acceptable(wpa_s); for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; if (nei->preference_present && nei->preference == 0) { wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, MAC2STR(nei->bssid)); continue; } target = wpa_bss_get_bssid(wpa_s, nei->bssid); if (!target) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) not found in scan results", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1); continue; } if (age_secs) { struct os_reltime now; if (os_get_reltime(&now) == 0 && os_reltime_expired(&now, &target->last_update, age_secs)) { wpa_printf(MSG_DEBUG, "Candidate BSS is more than %ld seconds old", age_secs); continue; } } if (bss->ssid_len != target->ssid_len || os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { /* * TODO: Could consider allowing transition to another * ESS if PMF was enabled for the association. */ wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) in different ESS", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1); continue; } if (wpa_s->current_ssid && !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, 1, 0)) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not match the current network profile", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1); continue; } if (wpa_is_bss_tmp_disallowed(wpa_s, target)) { wpa_printf(MSG_DEBUG, "MBO: Candidate BSS " MACSTR " retry delay is not over yet", MAC2STR(nei->bssid)); continue; } if (target->level < bss->level && target->level < -80) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not have sufficient signal level (%d)", MAC2STR(nei->bssid), nei->preference_present ? nei->preference : -1, target->level); continue; } nei->acceptable = 1; } #ifdef CONFIG_MBO if (wpa_s->wnm_mbo_trans_reason_present) target = get_mbo_transition_candidate(wpa_s, reason); else target = get_first_acceptable(wpa_s); #else /* CONFIG_MBO */ target = get_first_acceptable(wpa_s); #endif /* CONFIG_MBO */ if (target) { wpa_printf(MSG_DEBUG, "WNM: Found an acceptable preferred transition candidate BSS " MACSTR " (RSSI %d)", MAC2STR(target->bssid), target->level); } return target; } static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid) { const u8 *ie_a, *ie_b; if (!a || !b) return 0; ie_a = wpa_bss_get_ie(a, eid); ie_b = wpa_bss_get_ie(b, eid); if (!ie_a || !ie_b || ie_a[1] != ie_b[1]) return 0; return os_memcmp(ie_a, ie_b, ie_a[1]) == 0; } static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { u32 info = 0; info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH; /* * Leave the security and key scope bits unset to indicate that the * security information is not available. */ if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT) info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; if (bss->caps & WLAN_CAPABILITY_QOS) info |= NEI_REP_BSSID_INFO_QOS; if (bss->caps & WLAN_CAPABILITY_APSD) info |= NEI_REP_BSSID_INFO_APSD; if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT) info |= NEI_REP_BSSID_INFO_RM; if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK) info |= NEI_REP_BSSID_INFO_DELAYED_BA; if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK) info |= NEI_REP_BSSID_INFO_IMM_BA; if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN)) info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN; if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP)) info |= NEI_REP_BSSID_INFO_HT; return info; } static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid, u32 bss_info, u8 op_class, u8 chan, u8 phy_type, u8 pref) { if (wpabuf_len(*buf) + 18 > IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) { wpa_printf(MSG_DEBUG, "WNM: No room in frame for Neighbor Report element"); return -1; } if (wpabuf_resize(buf, 18) < 0) { wpa_printf(MSG_DEBUG, "WNM: Failed to allocate memory for Neighbor Report element"); return -1; } wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT); /* length: 13 for basic neighbor report + 3 for preference subelement */ wpabuf_put_u8(*buf, 16); wpabuf_put_data(*buf, bssid, ETH_ALEN); wpabuf_put_le32(*buf, bss_info); wpabuf_put_u8(*buf, op_class); wpabuf_put_u8(*buf, chan); wpabuf_put_u8(*buf, phy_type); wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE); wpabuf_put_u8(*buf, 1); wpabuf_put_u8(*buf, pref); return 0; } static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpabuf **buf, u8 pref) { const u8 *ie; u8 op_class, chan; int sec_chan = 0, vht = 0; enum phy_type phy_type; u32 info; struct ieee80211_ht_operation *ht_oper = NULL; struct ieee80211_vht_operation *vht_oper = NULL; ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); if (ie && ie[1] >= 2) { ht_oper = (struct ieee80211_ht_operation *) (ie + 2); if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) sec_chan = 1; else if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) sec_chan = -1; } ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); if (ie && ie[1] >= 1) { vht_oper = (struct ieee80211_vht_operation *) (ie + 2); if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ || vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ || vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ) vht = vht_oper->vht_op_info_chwidth; } if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, &chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine operating class and channel"); return -2; } phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), (vht_oper != NULL)); if (phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine BSS phy type for Neighbor Report"); return -2; } info = wnm_get_bss_info(wpa_s, bss); return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type, pref); } static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf) { unsigned int i, pref = 255; struct os_reltime now; struct wpa_ssid *ssid = wpa_s->current_ssid; if (!ssid) return; /* * TODO: Define when scan results are no longer valid for the candidate * list. */ os_get_reltime(&now); if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) return; wpa_printf(MSG_DEBUG, "WNM: Add candidate list to BSS Transition Management Response frame"); for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) { struct wpa_bss *bss = wpa_s->last_scan_res[i]; int res; if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--); if (res == -2) continue; /* could not build entry for BSS */ if (res < 0) break; /* no more room for candidates */ if (pref == 1) break; } } wpa_hexdump_buf(MSG_DEBUG, "WNM: BSS Transition Management Response candidate list", *buf); } #define BTM_RESP_MIN_SIZE 5 + ETH_ALEN static void wnm_send_bss_transition_mgmt_resp( struct wpa_supplicant *wpa_s, u8 dialog_token, enum bss_trans_mgmt_status_code status, enum mbo_transition_reject_reason reason, u8 delay, const u8 *target_bssid) { struct wpabuf *buf; int res; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response to " MACSTR " dialog_token=%u status=%u reason=%u delay=%d", MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay); if (!wpa_s->current_bss) { wpa_printf(MSG_DEBUG, "WNM: Current BSS not known - drop response"); return; } buf = wpabuf_alloc(BTM_RESP_MIN_SIZE); if (!buf) { wpa_printf(MSG_DEBUG, "WNM: Failed to allocate memory for BTM response"); return; } wpa_s->bss_tm_status = status; wpas_notify_bss_tm_status(wpa_s); wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP); wpabuf_put_u8(buf, dialog_token); wpabuf_put_u8(buf, status); wpabuf_put_u8(buf, delay); if (target_bssid) { wpabuf_put_data(buf, target_bssid, ETH_ALEN); } else if (status == WNM_BSS_TM_ACCEPT) { /* * P802.11-REVmc clarifies that the Target BSSID field is always * present when status code is zero, so use a fake value here if * no BSSID is yet known. */ wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN); } if (status == WNM_BSS_TM_ACCEPT) wnm_add_cand_list(wpa_s, &buf); #ifdef CONFIG_MBO if (status != WNM_BSS_TM_ACCEPT && wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) { u8 mbo[10]; size_t ret; ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo), reason); if (ret) { if (wpabuf_resize(&buf, ret) < 0) { wpabuf_free(buf); wpa_printf(MSG_DEBUG, "WNM: Failed to allocate memory for MBO IE"); return; } wpabuf_put_data(buf, mbo, ret); } } #endif /* CONFIG_MBO */ res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, wpabuf_head_u8(buf), wpabuf_len(buf), 0); if (res < 0) { wpa_printf(MSG_DEBUG, "WNM: Failed to send BSS Transition Management Response"); } wpabuf_free(buf); } static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, int after_new_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Transition to BSS " MACSTR " based on BSS Transition Management Request (old BSSID " MACSTR " after_new_scan=%d)", MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan); /* Send the BSS Management Response - Accept */ if (wpa_s->wnm_reply) { wpa_s->wnm_reply = 0; wpa_printf(MSG_DEBUG, "WNM: Sending successful BSS Transition Management Response"); wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, bss->bssid); } if (bss == wpa_s->current_bss) { wpa_printf(MSG_DEBUG, "WNM: Already associated with the preferred candidate"); wnm_deallocate_memory(wpa_s); return; } wpa_s->reassociate = 1; wpa_printf(MSG_DEBUG, "WNM: Issuing connect"); wpa_supplicant_connect(wpa_s, bss, ssid); wnm_deallocate_memory(wpa_s); } int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) { struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; enum mbo_transition_reject_reason reason = MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; if (!wpa_s->wnm_neighbor_report_elements) return 0; wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Process scan results for BSS Transition Management"); if (os_reltime_before(&wpa_s->wnm_cand_valid_until, &wpa_s->scan_trigger_time)) { wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); wnm_deallocate_memory(wpa_s); return 0; } if (!wpa_s->current_bss || os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); return 0; } /* Compare the Neighbor Report and scan results */ bss = compare_scan_neighbor_results(wpa_s, 0, &reason); if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; goto send_bss_resp_fail; } /* Associate to the network */ wnm_bss_tm_connect(wpa_s, bss, ssid, 1); return 1; send_bss_resp_fail: if (!reply_on_fail) return 0; /* Send reject response for all the failures */ if (wpa_s->wnm_reply) { wpa_s->wnm_reply = 0; wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, status, reason, 0, NULL); } wnm_deallocate_memory(wpa_s); return 0; } static int cand_pref_compar(const void *a, const void *b) { const struct neighbor_report *aa = a; const struct neighbor_report *bb = b; if (!aa->preference_present && !bb->preference_present) return 0; if (!aa->preference_present) return 1; if (!bb->preference_present) return -1; if (bb->preference > aa->preference) return 1; if (bb->preference < aa->preference) return -1; return 0; } static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) { if (!wpa_s->wnm_neighbor_report_elements) return; qsort(wpa_s->wnm_neighbor_report_elements, wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), cand_pref_compar); } static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) { unsigned int i; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); if (!wpa_s->wnm_neighbor_report_elements) return; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; wpa_printf(MSG_DEBUG, "%u: " MACSTR " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", i, MAC2STR(nei->bssid), nei->bssid_info, nei->regulatory_class, nei->channel_number, nei->phy_type, nei->preference_present ? nei->preference : -1, nei->freq); } } static int chan_supported(struct wpa_supplicant *wpa_s, int freq) { unsigned int i; for (i = 0; i < wpa_s->hw.num_modes; i++) { struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; int j; for (j = 0; j < mode->num_channels; j++) { struct hostapd_channel_data *chan; chan = &mode->channels[j]; if (chan->freq == freq && !(chan->flag & HOSTAPD_CHAN_DISABLED)) return 1; } } return 0; } static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) { int *freqs; int num_freqs = 0; unsigned int i; if (!wpa_s->wnm_neighbor_report_elements) return; if (wpa_s->hw.modes == NULL) return; os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); if (freqs == NULL) return; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; if (nei->freq <= 0) { wpa_printf(MSG_DEBUG, "WNM: Unknown neighbor operating frequency for " MACSTR " - scan all channels", MAC2STR(nei->bssid)); os_free(freqs); return; } if (chan_supported(wpa_s, nei->freq)) add_freq(freqs, &num_freqs, nei->freq); } if (num_freqs == 0) { os_free(freqs); return; } wpa_printf(MSG_DEBUG, "WNM: Scan %d frequencies based on transition candidate list", num_freqs); wpa_s->next_scan_freqs = freqs; } static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_scan_results *scan_res; struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; u8 i, found = 0; size_t j; wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Fetch current scan results from the driver for checking transition candidates"); scan_res = wpa_drv_get_scan_results2(wpa_s); if (!scan_res) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results"); return 0; } if (scan_res->fetch_time.sec == 0) os_get_reltime(&scan_res->fetch_time); filter_scan_res(wpa_s, scan_res); for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; if (nei->preference_present && nei->preference == 0) continue; for (j = 0; j < scan_res->num; j++) { struct wpa_scan_res *res; const u8 *ssid_ie; res = scan_res->res[j]; if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 || res->age > WNM_SCAN_RESULT_AGE * 1000) continue; bss = wpa_s->current_bss; ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID); - if (bss && ssid_ie && + if (bss && ssid_ie && ssid_ie[1] && (bss->ssid_len != ssid_ie[1] || os_memcmp(bss->ssid, ssid_ie + 2, bss->ssid_len) != 0)) - continue; + continue; /* Skip entries for other ESSs */ /* Potential candidate found */ found = 1; scan_snr(res); scan_est_throughput(wpa_s, res); wpa_bss_update_scan_res(wpa_s, res, &scan_res->fetch_time); } } wpa_scan_results_free(scan_res); if (!found) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: No transition candidate matches existing scan results"); return 0; } bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL); if (!bss) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Comparison of scan results against transition candidates did not find matches"); return 0; } /* Associate to the network */ wnm_bss_tm_connect(wpa_s, bss, ssid, 0); return 1; } static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { unsigned int beacon_int; u8 valid_int; #ifdef CONFIG_MBO const u8 *vendor; #endif /* CONFIG_MBO */ if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm) return; if (end - pos < 5) return; #ifdef CONFIG_MBO wpa_s->wnm_mbo_trans_reason_present = 0; wpa_s->wnm_mbo_transition_reason = 0; #endif /* CONFIG_MBO */ if (wpa_s->current_bss) beacon_int = wpa_s->current_bss->beacon_int; else beacon_int = 100; /* best guess */ wpa_s->wnm_dialog_token = pos[0]; wpa_s->wnm_mode = pos[1]; wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); valid_int = pos[4]; wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) if (wpa_s->reject_btm_req_reason) { wpa_printf(MSG_INFO, "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", wpa_s->reject_btm_req_reason); wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, wpa_s->reject_btm_req_reason, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); return; } #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ pos += 5; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { if (end - pos < 12) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); return; } os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; if (end - pos < 1 || 1 + pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); return; } os_memcpy(url, pos + 1, pos[0]); url[pos[0]] = '\0'; pos += 1 + pos[0]; wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", wpa_sm_pmf_enabled(wpa_s->wpa), wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { /* TODO: mark current BSS less preferred for * selection */ wpa_printf(MSG_DEBUG, "Trying to find another BSS"); wpa_supplicant_req_scan(wpa_s, 0, 0); } } #ifdef CONFIG_MBO vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); if (vendor) wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); #endif /* CONFIG_MBO */ if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { unsigned int valid_ms; wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); wnm_deallocate_memory(wpa_s); wpa_s->wnm_neighbor_report_elements = os_calloc( WNM_MAX_NEIGHBOR_REPORT, sizeof(struct neighbor_report)); if (wpa_s->wnm_neighbor_report_elements == NULL) return; while (end - pos >= 2 && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { u8 tag = *pos++; u8 len = *pos++; wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); if (len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Truncated request"); return; } if (tag == WLAN_EID_NEIGHBOR_REPORT) { struct neighbor_report *rep; rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); wpa_s->wnm_num_neighbor_report++; #ifdef CONFIG_MBO if (wpa_s->wnm_mbo_trans_reason_present && wpa_s->wnm_num_neighbor_report == 1) { rep->is_first = 1; wpa_printf(MSG_DEBUG, "WNM: First transition candidate is " MACSTR, MAC2STR(rep->bssid)); } #endif /* CONFIG_MBO */ } pos += len; } if (!wpa_s->wnm_num_neighbor_report) { wpa_printf(MSG_DEBUG, "WNM: Candidate list included bit is set, but no candidates found"); wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); return; } wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); valid_ms = valid_int * beacon_int * 128 / 125; wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; wpa_s->wnm_cand_valid_until.sec += wpa_s->wnm_cand_valid_until.usec / 1000000; wpa_s->wnm_cand_valid_until.usec %= 1000000; os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); /* * Fetch the latest scan results from the kernel and check for * candidates based on those results first. This can help in * finding more up-to-date information should the driver has * done some internal scanning operations after the last scan * result update in wpa_supplicant. */ if (wnm_fetch_scan_results(wpa_s) > 0) return; /* * Try to use previously received scan results, if they are * recent enough to use for a connection. */ if (wpa_s->last_scan_res_used > 0) { struct os_reltime now; os_get_reltime(&now); if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { wpa_printf(MSG_DEBUG, "WNM: Try to use recent scan results"); if (wnm_scan_process(wpa_s, 0) > 0) return; wpa_printf(MSG_DEBUG, "WNM: No match in previous scan results - try a new scan"); } } wnm_set_scan_freqs(wpa_s); if (wpa_s->wnm_num_neighbor_report == 1) { os_memcpy(wpa_s->next_scan_bssid, wpa_s->wnm_neighbor_report_elements[0].bssid, ETH_ALEN); wpa_printf(MSG_DEBUG, "WNM: Scan only for a specific BSSID since there is only a single candidate " MACSTR, MAC2STR(wpa_s->next_scan_bssid)); } wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) status = WNM_BSS_TM_ACCEPT; else { wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, status, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); } } #define BTM_QUERY_MIN_SIZE 4 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, u8 query_reason, const char *btm_candidates, int cand_list) { struct wpabuf *buf; int ret; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " MACSTR " query_reason=%u%s", MAC2STR(wpa_s->bssid), query_reason, cand_list ? " candidate list" : ""); buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE); if (!buf) return -1; wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY); wpabuf_put_u8(buf, 1); wpabuf_put_u8(buf, query_reason); if (cand_list) wnm_add_cand_list(wpa_s, &buf); if (btm_candidates) { const size_t max_len = 1000; ret = wpabuf_resize(&buf, max_len); if (ret < 0) { wpabuf_free(buf); return ret; } ret = ieee802_11_parse_candidate_list(btm_candidates, wpabuf_put(buf, 0), max_len); if (ret < 0) { wpabuf_free(buf); return ret; } wpabuf_put(buf, ret); } ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, wpabuf_head_u8(buf), wpabuf_len(buf), 0); wpabuf_free(buf); return ret; } static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *data, int len) { const u8 *pos, *end, *next; u8 ie, ie_len; pos = data; end = data + len; while (end - pos > 1) { ie = *pos++; ie_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", ie, ie_len); if (ie_len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Not enough room for " "subelement"); break; } next = pos + ie_len; if (ie_len < 4) { pos = next; continue; } wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", WPA_GET_BE24(pos), pos[3]); #ifdef CONFIG_HS20 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && WPA_GET_BE24(pos) == OUI_WFA && pos[3] == HS20_WNM_SUB_REM_NEEDED) { /* Subscription Remediation subelement */ const u8 *ie_end; u8 url_len; char *url; u8 osu_method; wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " "subelement"); ie_end = pos + ie_len; pos += 4; url_len = *pos++; if (url_len == 0) { wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); url = NULL; osu_method = 1; } else { if (url_len + 1 > ie_end - pos) { wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", url_len, (int) (ie_end - pos)); break; } url = os_malloc(url_len + 1); if (url == NULL) break; os_memcpy(url, pos, url_len); url[url_len] = '\0'; osu_method = pos[url_len]; } hs20_rx_subscription_remediation(wpa_s, url, osu_method); os_free(url); pos = next; continue; } if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && WPA_GET_BE24(pos) == OUI_WFA && pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { const u8 *ie_end; u8 url_len; char *url; u8 code; u16 reauth_delay; ie_end = pos + ie_len; pos += 4; code = *pos++; reauth_delay = WPA_GET_LE16(pos); pos += 2; url_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " "Imminent - Reason Code %u " "Re-Auth Delay %u URL Length %u", code, reauth_delay, url_len); if (url_len > ie_end - pos) break; url = os_malloc(url_len + 1); if (url == NULL) break; os_memcpy(url, pos, url_len); url[url_len] = '\0'; hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay, url); os_free(url); pos = next; continue; } if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && WPA_GET_BE24(pos) == OUI_WFA && pos[3] == HS20_WNM_T_C_ACCEPTANCE) { const u8 *ie_end; u8 url_len; char *url; ie_end = pos + ie_len; pos += 4; url_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)", url_len); if (url_len > ie_end - pos) break; url = os_malloc(url_len + 1); if (!url) break; os_memcpy(url, pos, url_len); url[url_len] = '\0'; hs20_rx_t_c_acceptance(wpa_s, url); os_free(url); pos = next; continue; } #endif /* CONFIG_HS20 */ pos = next; } } static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *frm, int len) { const u8 *pos, *end; u8 dialog_token, type; /* Dialog Token [1] | Type [1] | Subelements */ if (len < 2 || sa == NULL) return; end = frm + len; pos = frm; dialog_token = *pos++; type = *pos++; wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " "(dialog_token %u type %u sa " MACSTR ")", dialog_token, type, MAC2STR(sa)); wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", pos, end - pos); if (wpa_s->wpa_state != WPA_COMPLETED || os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " "from our AP - ignore it"); return; } switch (type) { case 1: ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); break; default: wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " "WNM-Notification type %u", type); break; } } static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *frm, int len) { u8 dialog_token, req_info, auto_report, timeout; if (!wpa_s->conf->coloc_intf_reporting) return; /* Dialog Token [1] | Request Info [1] */ if (len < 2) return; dialog_token = frm[0]; req_info = frm[1]; auto_report = req_info & 0x03; timeout = req_info >> 2; wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")", dialog_token, auto_report, timeout, MAC2STR(sa)); if (dialog_token == 0) return; /* only nonzero values are used for request */ if (wpa_s->wpa_state != WPA_COMPLETED || os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Collocated Interference Request frame not from current AP - ignore it"); return; } wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u", dialog_token, auto_report, timeout); wpa_s->coloc_intf_dialog_token = dialog_token; wpa_s->coloc_intf_auto_report = auto_report; wpa_s->coloc_intf_timeout = timeout; } void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { const u8 *pos, *end; u8 act; if (len < IEEE80211_HDRLEN + 2) return; pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; act = *pos++; end = ((const u8 *) mgmt) + len; wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, act, MAC2STR(mgmt->sa)); if (wpa_s->wpa_state < WPA_ASSOCIATED || os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " "frame"); return; } switch (act) { case WNM_BSS_TRANS_MGMT_REQ: ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, !(mgmt->da[0] & 0x01)); break; case WNM_SLEEP_MODE_RESP: ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); break; case WNM_NOTIFICATION_REQ: ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); break; case WNM_COLLOCATED_INTERFERENCE_REQ: ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, end - pos); break; default: wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, const struct wpabuf *elems) { struct wpabuf *buf; int ret; if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems) return -1; wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to " MACSTR " (dialog token %u)", MAC2STR(wpa_s->bssid), dialog_token); buf = wpabuf_alloc(3 + wpabuf_len(elems)); if (!buf) return -1; wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT); wpabuf_put_u8(buf, dialog_token); wpabuf_put_buf(buf, elems); ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, wpabuf_head_u8(buf), wpabuf_len(buf), 0); wpabuf_free(buf); return ret; } void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, struct wpabuf *elems) { wpabuf_free(wpa_s->coloc_intf_elems); if (elems && wpabuf_len(elems) == 0) { wpabuf_free(elems); elems = NULL; } wpa_s->coloc_intf_elems = elems; if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems && wpa_s->coloc_intf_dialog_token && (wpa_s->coloc_intf_auto_report == 1 || wpa_s->coloc_intf_auto_report == 3)) { /* TODO: Check that there has not been less than * wpa_s->coloc_intf_timeout * 200 TU from the last report. */ wnm_send_coloc_intf_report(wpa_s, wpa_s->coloc_intf_dialog_token, wpa_s->coloc_intf_elems); } } void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WNM wpa_s->coloc_intf_dialog_token = 0; wpa_s->coloc_intf_auto_report = 0; #endif /* CONFIG_WNM */ } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 0d9b9caa5906..cc184dba5feb 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1,8392 +1,8432 @@ /* * WPA Supplicant * Copyright (c) 2003-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. * * This file implements functions for registering and unregistering * %wpa_supplicant interfaces. In addition, this file contains number of * functions for managing network connections. */ #include "includes.h" #ifdef CONFIG_MATCH_IFACE #include #include #endif /* CONFIG_MATCH_IFACE */ #include "common.h" #include "crypto/random.h" #include "crypto/sha1.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" #include "eap_peer/eap_proxy.h" #include "eap_server/eap_methods.h" #include "rsn_supp/wpa.h" #include "eloop.h" #include "config.h" #include "utils/ext_password.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "ctrl_iface.h" #include "pcsc_funcs.h" #include "common/version.h" #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_common.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "common/gas_server.h" #include "common/dpp.h" #include "common/ptksa_cache.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "bssid_ignore.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" #include "gas_query.h" #include "ap.h" #include "p2p_supplicant.h" #include "wifi_display.h" #include "notify.h" #include "bgscan.h" #include "autoscan.h" #include "bss.h" #include "scan.h" #include "offchannel.h" #include "hs20_supplicant.h" #include "wnm_sta.h" #include "wpas_kay.h" #include "mesh.h" #include "dpp_supplicant.h" #ifdef CONFIG_MESH #include "ap/ap_config.h" #include "ap/hostapd.h" #endif /* CONFIG_MESH */ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" "Copyright (c) 2003-2019, Jouni Malinen and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" "See README for more details.\n" #ifdef EAP_TLS_OPENSSL "\nThis product includes software developed by the OpenSSL Project\n" "for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" #endif /* EAP_TLS_OPENSSL */ ; #ifndef CONFIG_NO_STDOUT_DEBUG /* Long text divided into parts in order to fit in C89 strings size limits. */ const char *const wpa_supplicant_full_license1 = ""; const char *const wpa_supplicant_full_license2 = "This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" "met:\n" "\n"; const char *const wpa_supplicant_full_license3 = "1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" "\n" "2. Redistributions in binary form must reproduce the above copyright\n" " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n"; const char *const wpa_supplicant_full_license4 = "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" " names of its contributors may be used to endorse or promote products\n" " derived from this software without specific prior written permission.\n" "\n" "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"; const char *const wpa_supplicant_full_license5 = "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "\n"; #endif /* CONFIG_NO_STDOUT_DEBUG */ static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx); #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s); #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ #ifdef CONFIG_OWE static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s); #endif /* CONFIG_OWE */ #ifdef CONFIG_WEP /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { int i, set = 0; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i] == 0) continue; set = 1; wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL, i, i == ssid->wep_tx_keyidx, NULL, 0, ssid->wep_key[i], ssid->wep_key_len[i], i == ssid->wep_tx_keyidx ? KEY_FLAG_GROUP_RX_TX_DEFAULT : KEY_FLAG_GROUP_RX_TX); } return set; } #endif /* CONFIG_WEP */ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { u8 key[32]; size_t keylen; enum wpa_alg alg; u8 seq[6] = { 0 }; int ret; /* IBSS/WPA-None uses only one key (Group) for both receiving and * sending unicast and multicast packets. */ if (ssid->mode != WPAS_MODE_IBSS) { wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not " "IBSS/ad-hoc) for WPA-None", ssid->mode); return -1; } if (!ssid->psk_set) { wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for " "WPA-None"); return -1; } switch (wpa_s->group_cipher) { case WPA_CIPHER_CCMP: os_memcpy(key, ssid->psk, 16); keylen = 16; alg = WPA_ALG_CCMP; break; case WPA_CIPHER_GCMP: os_memcpy(key, ssid->psk, 16); keylen = 16; alg = WPA_ALG_GCMP; break; case WPA_CIPHER_TKIP: /* WPA-None uses the same Michael MIC key for both TX and RX */ os_memcpy(key, ssid->psk, 16 + 8); os_memcpy(key + 16 + 8, ssid->psk + 16, 8); keylen = 32; alg = WPA_ALG_TKIP; break; default: wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for " "WPA-None", wpa_s->group_cipher); return -1; } /* TODO: should actually remember the previously used seq#, both for TX * and RX from each STA.. */ ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen, KEY_FLAG_GROUP_RX_TX_DEFAULT); os_memset(key, 0, sizeof(key)); return ret; } static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; const u8 *bssid = wpa_s->bssid; if (!is_zero_ether_addr(wpa_s->pending_bssid) && (wpa_s->wpa_state == WPA_AUTHENTICATING || wpa_s->wpa_state == WPA_ASSOCIATING)) bssid = wpa_s->pending_bssid; wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", MAC2STR(bssid)); wpa_bssid_ignore_add(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_s->reassociate = 1; /* * If we timed out, the AP or the local radio may be busy. * So, wait a second until scanning again. */ wpa_supplicant_req_scan(wpa_s, 1, 0); } /** * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication * @wpa_s: Pointer to wpa_supplicant data * @sec: Number of seconds after which to time out authentication * @usec: Number of microseconds after which to time out authentication * * This function is used to schedule a timeout for the current authentication * attempt. */ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec) { if (wpa_s->conf->ap_scan == 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) return; wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " "%d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); wpa_s->last_auth_timeout_sec = sec; eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); } /* * wpas_auth_timeout_restart - Restart and change timeout for authentication * @wpa_s: Pointer to wpa_supplicant data * @sec_diff: difference in seconds applied to original timeout value */ void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff) { int new_sec = wpa_s->last_auth_timeout_sec + sec_diff; if (eloop_is_timeout_registered(wpa_supplicant_timeout, wpa_s, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "Authentication timeout restart: %d sec", new_sec); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); eloop_register_timeout(new_sec, 0, wpa_supplicant_timeout, wpa_s, NULL); } } /** * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout * @wpa_s: Pointer to wpa_supplicant data * * This function is used to cancel authentication timeout scheduled with * wpa_supplicant_req_auth_timeout() and it is called when authentication has * been completed. */ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); wpa_bssid_ignore_del(wpa_s, wpa_s->bssid); os_free(wpa_s->last_con_fail_realm); wpa_s->last_con_fail_realm = NULL; wpa_s->last_con_fail_realm_len = 0; } /** * wpa_supplicant_initiate_eapol - Configure EAPOL state machine * @wpa_s: Pointer to wpa_supplicant data * * This function is used to configure EAPOL state machine based on the selected * authentication mode. */ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) { #ifdef IEEE8021X_EAPOL struct eapol_config eapol_conf; struct wpa_ssid *ssid = wpa_s->current_ssid; #ifdef CONFIG_IBSS_RSN if (ssid->mode == WPAS_MODE_IBSS && wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) { /* * RSN IBSS authentication is per-STA and we can disable the * per-BSSID EAPOL authentication. */ eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); eapol_sm_notify_eap_success(wpa_s->eapol, true); eapol_sm_notify_eap_fail(wpa_s->eapol, false); return; } #endif /* CONFIG_IBSS_RSN */ eapol_sm_notify_eap_success(wpa_s->eapol, false); eapol_sm_notify_eap_fail(wpa_s->eapol, false); if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); else eapol_sm_notify_portControl(wpa_s->eapol, Auto); os_memset(&eapol_conf, 0, sizeof(eapol_conf)); if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { eapol_conf.accept_802_1x_keys = 1; eapol_conf.required_keys = 0; if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) { eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST; } if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) { eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_BROADCAST; } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) eapol_conf.required_keys = 0; } eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; eapol_conf.eap_disabled = !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) && wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; eapol_conf.external_sim = wpa_s->conf->external_sim; #ifdef CONFIG_WPS if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE; if (wpa_s->current_bss) { struct wpabuf *ie; ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss, WPS_IE_VENDOR_TYPE); if (ie) { if (wps_is_20(ie)) eapol_conf.wps |= EAPOL_PEER_IS_WPS20_AP; wpabuf_free(ie); } } } #endif /* CONFIG_WPS */ eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); #ifdef CONFIG_MACSEC if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set) ieee802_1x_create_preshared_mka(wpa_s, ssid); else ieee802_1x_alloc_kay_sm(wpa_s, ssid); #endif /* CONFIG_MACSEC */ #endif /* IEEE8021X_EAPOL */ } /** * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode * @wpa_s: Pointer to wpa_supplicant data * @ssid: Configuration data for the network * * This function is used to configure WPA state machine and related parameters * to a mode where WPA is not enabled. This is called as part of the * authentication configuration when the selected network does not use WPA. */ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { #ifdef CONFIG_WEP int i; #endif /* CONFIG_WEP */ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) wpa_s->key_mgmt = WPA_KEY_MGMT_WPS; else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; else wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); wpa_s->rsnxe_len = 0; wpa_s->pairwise_cipher = WPA_CIPHER_NONE; wpa_s->group_cipher = WPA_CIPHER_NONE; wpa_s->mgmt_group_cipher = 0; #ifdef CONFIG_WEP for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i] > 5) { wpa_s->pairwise_cipher = WPA_CIPHER_WEP104; wpa_s->group_cipher = WPA_CIPHER_WEP104; break; } else if (ssid->wep_key_len[i] > 0) { wpa_s->pairwise_cipher = WPA_CIPHER_WEP40; wpa_s->group_cipher = WPA_CIPHER_WEP40; break; } } #endif /* CONFIG_WEP */ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, wpa_s->pairwise_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); pmksa_cache_clear_current(wpa_s->wpa); } void free_hw_features(struct wpa_supplicant *wpa_s) { int i; if (wpa_s->hw.modes == NULL) return; for (i = 0; i < wpa_s->hw.num_modes; i++) { os_free(wpa_s->hw.modes[i].channels); os_free(wpa_s->hw.modes[i].rates); } os_free(wpa_s->hw.modes); wpa_s->hw.modes = NULL; } static void remove_bss_tmp_disallowed_entry(struct wpa_supplicant *wpa_s, struct wpa_bss_tmp_disallowed *bss) { eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); dl_list_del(&bss->list); os_free(bss); } void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) { struct wpa_bss_tmp_disallowed *bss, *prev; dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) remove_bss_tmp_disallowed_entry(wpa_s, bss); } void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s) { struct fils_hlp_req *req; while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req, list)) != NULL) { dl_list_del(&req->list); wpabuf_free(req->pkt); os_free(req); } } void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) return; wpa_dbg(wpa_s, MSG_DEBUG, "Clear cached state on disabled interface"); wpa_bss_flush(wpa_s); } #ifdef CONFIG_TESTING_OPTIONS void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s) { struct driver_signal_override *dso; while ((dso = dl_list_first(&wpa_s->drv_signal_override, struct driver_signal_override, list))) { dl_list_del(&dso->list); os_free(dso); } } #endif /* CONFIG_TESTING_OPTIONS */ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { int i; bgscan_deinit(wpa_s); autoscan_deinit(wpa_s); scard_deinit(wpa_s->scard); wpa_s->scard = NULL; wpa_sm_set_scard_ctx(wpa_s->wpa, NULL); eapol_sm_register_scard_ctx(wpa_s->eapol, NULL); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; if (wpa_s->l2_br) { l2_packet_deinit(wpa_s->l2_br); wpa_s->l2_br = NULL; } #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(wpa_s->l2_test); wpa_s->l2_test = NULL; os_free(wpa_s->get_pref_freq_list_override); wpa_s->get_pref_freq_list_override = NULL; wpabuf_free(wpa_s->last_assoc_req_wpa_ie); wpa_s->last_assoc_req_wpa_ie = NULL; os_free(wpa_s->extra_sae_rejected_groups); wpa_s->extra_sae_rejected_groups = NULL; wpabuf_free(wpa_s->rsne_override_eapol); wpa_s->rsne_override_eapol = NULL; wpabuf_free(wpa_s->rsnxe_override_assoc); wpa_s->rsnxe_override_assoc = NULL; wpabuf_free(wpa_s->rsnxe_override_eapol); wpa_s->rsnxe_override_eapol = NULL; wpas_clear_driver_signal_override(wpa_s); #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) wpas_notify_network_removed(wpa_s, ssid); } os_free(wpa_s->confname); wpa_s->confname = NULL; os_free(wpa_s->confanother); wpa_s->confanother = NULL; os_free(wpa_s->last_con_fail_realm); wpa_s->last_con_fail_realm = NULL; wpa_s->last_con_fail_realm_len = 0; wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; rsn_preauth_deinit(wpa_s->wpa); #ifdef CONFIG_TDLS wpa_tdls_deinit(wpa_s->wpa); #endif /* CONFIG_TDLS */ wmm_ac_clear_saved_tspecs(wpa_s); pmksa_candidate_free(wpa_s->wpa); ptksa_cache_deinit(wpa_s->ptksa); wpa_s->ptksa = NULL; wpa_sm_deinit(wpa_s->wpa); wpa_s->wpa = NULL; wpa_bssid_ignore_clear(wpa_s); #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); #endif /* CONFIG_PASN */ wpa_bss_deinit(wpa_s); wpa_supplicant_cancel_delayed_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_cancel_auth_timeout(wpa_s); eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report, wpa_s, NULL); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL); wpas_wps_deinit(wpa_s); wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = NULL; #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; #endif /* CONFIG_IBSS_RSN */ sme_deinit(wpa_s); #ifdef CONFIG_AP wpa_supplicant_ap_deinit(wpa_s); #endif /* CONFIG_AP */ wpas_p2p_deinit(wpa_s); #ifdef CONFIG_OFFCHANNEL offchannel_deinit(wpa_s); #endif /* CONFIG_OFFCHANNEL */ wpa_supplicant_cancel_sched_scan(wpa_s); os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = NULL; os_free(wpa_s->select_network_scan_freqs); wpa_s->select_network_scan_freqs = NULL; os_free(wpa_s->manual_sched_scan_freqs); wpa_s->manual_sched_scan_freqs = NULL; wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL); /* * Need to remove any pending gas-query radio work before the * gas_query_deinit() call because gas_query::work has not yet been set * for works that have not been started. gas_query_free() will be unable * to cancel such pending radio works and once the pending gas-query * radio work eventually gets removed, the deinit notification call to * gas_query_start_cb() would result in dereferencing freed memory. */ if (wpa_s->radio) radio_remove_works(wpa_s, "gas-query", 0); gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; gas_server_deinit(wpa_s->gas_server); wpa_s->gas_server = NULL; free_hw_features(wpa_s); ieee802_1x_dealloc_kay_sm(wpa_s); os_free(wpa_s->bssid_filter); wpa_s->bssid_filter = NULL; os_free(wpa_s->disallow_aps_bssid); wpa_s->disallow_aps_bssid = NULL; os_free(wpa_s->disallow_aps_ssid); wpa_s->disallow_aps_ssid = NULL; wnm_bss_keep_alive_deinit(wpa_s); #ifdef CONFIG_WNM wnm_deallocate_memory(wpa_s); #endif /* CONFIG_WNM */ ext_password_deinit(wpa_s->ext_pw); wpa_s->ext_pw = NULL; wpabuf_free(wpa_s->last_gas_resp); wpa_s->last_gas_resp = NULL; wpabuf_free(wpa_s->prev_gas_resp); wpa_s->prev_gas_resp = NULL; os_free(wpa_s->last_scan_res); wpa_s->last_scan_res = NULL; #ifdef CONFIG_HS20 if (wpa_s->drv_priv) wpa_drv_configure_frame_filters(wpa_s, 0); hs20_deinit(wpa_s); #endif /* CONFIG_HS20 */ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { wpabuf_free(wpa_s->vendor_elem[i]); wpa_s->vendor_elem[i] = NULL; } wmm_ac_notify_disassoc(wpa_s); wpa_s->sched_scan_plans_num = 0; os_free(wpa_s->sched_scan_plans); wpa_s->sched_scan_plans = NULL; #ifdef CONFIG_MBO wpa_s->non_pref_chan_num = 0; os_free(wpa_s->non_pref_chan); wpa_s->non_pref_chan = NULL; #endif /* CONFIG_MBO */ free_bss_tmp_disallowed(wpa_s); wpabuf_free(wpa_s->lci); wpa_s->lci = NULL; wpas_clear_beacon_rep_data(wpa_s); #ifdef CONFIG_PMKSA_CACHE_EXTERNAL #ifdef CONFIG_MESH { struct external_pmksa_cache *entry; while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache, struct external_pmksa_cache, list)) != NULL) { dl_list_del(&entry->list); os_free(entry->pmksa_cache); os_free(entry); } } #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ wpas_flush_fils_hlp_req(wpa_s); wpabuf_free(wpa_s->ric_ies); wpa_s->ric_ies = NULL; #ifdef CONFIG_DPP wpas_dpp_deinit(wpa_s); dpp_global_deinit(wpa_s->dpp); wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); #endif /* CONFIG_PASN */ } /** * wpa_clear_keys - Clear keys configured for the driver * @wpa_s: Pointer to wpa_supplicant data * @addr: Previously used BSSID or %NULL if not available * * This function clears the encryption keys that has been previously configured * for the driver. */ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) { int i, max = 6; /* MLME-DELETEKEYS.request */ for (i = 0; i < max; i++) { if (wpa_s->keys_cleared & BIT(i)) continue; wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP); } /* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */ if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr && !is_zero_ether_addr(addr)) { if (!(wpa_s->keys_cleared & BIT(0))) wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); if (!(wpa_s->keys_cleared & BIT(15))) wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); /* MLME-SETPROTECTION.request(None) */ wpa_drv_mlme_setprotection( wpa_s, addr, MLME_SETPROTECTION_PROTECT_TYPE_NONE, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); } wpa_s->keys_cleared = (u32) -1; } /** * wpa_supplicant_state_txt - Get the connection state name as a text string * @state: State (wpa_state; WPA_*) * Returns: The state name as a printable text string */ const char * wpa_supplicant_state_txt(enum wpa_states state) { switch (state) { case WPA_DISCONNECTED: return "DISCONNECTED"; case WPA_INACTIVE: return "INACTIVE"; case WPA_INTERFACE_DISABLED: return "INTERFACE_DISABLED"; case WPA_SCANNING: return "SCANNING"; case WPA_AUTHENTICATING: return "AUTHENTICATING"; case WPA_ASSOCIATING: return "ASSOCIATING"; case WPA_ASSOCIATED: return "ASSOCIATED"; case WPA_4WAY_HANDSHAKE: return "4WAY_HANDSHAKE"; case WPA_GROUP_HANDSHAKE: return "GROUP_HANDSHAKE"; case WPA_COMPLETED: return "COMPLETED"; default: return "UNKNOWN"; } } #ifdef CONFIG_BGSCAN static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s) { if (wpa_s->bgscan_ssid) { bgscan_deinit(wpa_s); wpa_s->bgscan_ssid = NULL; } } /** * wpa_supplicant_reset_bgscan - Reset the bgscan for the current SSID. * @wpa_s: Pointer to the wpa_supplicant data * * Stop, start, or reconfigure the scan parameters depending on the method. */ void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s) { const char *name; if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) name = wpa_s->current_ssid->bgscan; else name = wpa_s->conf->bgscan; if (!name || name[0] == '\0') { wpa_supplicant_stop_bgscan(wpa_s); return; } if (wpas_driver_bss_selection(wpa_s)) return; #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) return; #endif /* CONFIG_P2P */ bgscan_deinit(wpa_s); if (wpa_s->current_ssid) { if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " "bgscan"); /* * Live without bgscan; it is only used as a roaming * optimization, so the initial connection is not * affected. */ } else { struct wpa_scan_results *scan_res; wpa_s->bgscan_ssid = wpa_s->current_ssid; scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); if (scan_res) { bgscan_notify_scan(wpa_s, scan_res); wpa_scan_results_free(scan_res); } } } else wpa_s->bgscan_ssid = NULL; } #endif /* CONFIG_BGSCAN */ static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s) { if (autoscan_init(wpa_s, 0)) wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan"); } static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s) { autoscan_deinit(wpa_s); } void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s) { if (wpa_s->wpa_state == WPA_DISCONNECTED || wpa_s->wpa_state == WPA_SCANNING) { autoscan_deinit(wpa_s); wpa_supplicant_start_autoscan(wpa_s); } } /** * wpa_supplicant_set_state - Set current connection state * @wpa_s: Pointer to wpa_supplicant data * @state: The new connection state * * This function is called whenever the connection state changes, e.g., * association is completed for WPA/WPA2 4-Way Handshake is started. */ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state) { enum wpa_states old_state = wpa_s->wpa_state; #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) bool update_fils_connect_params = false; #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s", wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_supplicant_state_txt(state)); if (state == WPA_COMPLETED && os_reltime_initialized(&wpa_s->roam_start)) { os_reltime_age(&wpa_s->roam_start, &wpa_s->roam_time); wpa_s->roam_start.sec = 0; wpa_s->roam_start.usec = 0; wpas_notify_auth_changed(wpa_s); wpas_notify_roam_time(wpa_s); wpas_notify_roam_complete(wpa_s); } else if (state == WPA_DISCONNECTED && os_reltime_initialized(&wpa_s->roam_start)) { wpa_s->roam_start.sec = 0; wpa_s->roam_start.usec = 0; wpa_s->roam_time.sec = 0; wpa_s->roam_time.usec = 0; wpas_notify_roam_complete(wpa_s); } if (state == WPA_INTERFACE_DISABLED) { /* Assure normal scan when interface is restored */ wpa_s->normal_scans = 0; } if (state == WPA_COMPLETED) { wpas_connect_work_done(wpa_s); /* Reinitialize normal_scan counter */ wpa_s->normal_scans = 0; } #ifdef CONFIG_P2P /* * P2PS client has to reply to Probe Request frames received on the * group operating channel. Enable Probe Request frame reporting for * P2P connected client in case p2p_cli_probe configuration property is * set to 1. */ if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_INFRA && wpa_s->current_ssid->p2p_group) { if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Enable CLI Probe Request RX reporting"); wpa_s->p2p_cli_probe = wpa_drv_probe_req_report(wpa_s, 1) >= 0; } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable CLI Probe Request RX reporting"); wpa_s->p2p_cli_probe = 0; wpa_drv_probe_req_report(wpa_s, 0); } } #endif /* CONFIG_P2P */ if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); if (state == WPA_COMPLETED && wpa_s->new_connection) { struct wpa_ssid *ssid = wpa_s->current_ssid; int fils_hlp_sent = 0; #ifdef CONFIG_SME if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && wpa_auth_alg_fils(wpa_s->sme.auth_alg)) fils_hlp_sent = 1; #endif /* CONFIG_SME */ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && wpa_auth_alg_fils(wpa_s->auth_alg)) fils_hlp_sent = 1; #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " MACSTR " completed [id=%d id_str=%s%s]", MAC2STR(wpa_s->bssid), ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : "", fils_hlp_sent ? " FILS_HLP_SENT" : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); wpa_s->consecutive_conn_failures = 0; wpa_s->new_connection = 0; wpa_drv_set_operstate(wpa_s, 1); #ifndef IEEE8021X_EAPOL wpa_drv_set_supp_port(wpa_s, 1); #endif /* IEEE8021X_EAPOL */ wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; wpas_p2p_completed(wpa_s); sme_sched_obss_scan(wpa_s, 1); #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) if (!fils_hlp_sent && ssid && ssid->eap.erp) update_fils_connect_params = true; #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ #ifdef CONFIG_OWE if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_OWE)) wpas_update_owe_connect_params(wpa_s); #endif /* CONFIG_OWE */ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; wpa_drv_set_operstate(wpa_s, 0); #ifndef IEEE8021X_EAPOL wpa_drv_set_supp_port(wpa_s, 0); #endif /* IEEE8021X_EAPOL */ sme_sched_obss_scan(wpa_s, 0); } wpa_s->wpa_state = state; #ifdef CONFIG_BGSCAN if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid) wpa_supplicant_reset_bgscan(wpa_s); else if (state < WPA_ASSOCIATED) wpa_supplicant_stop_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ if (state > WPA_SCANNING) wpa_supplicant_stop_autoscan(wpa_s); if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) wpa_supplicant_start_autoscan(wpa_s); if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED) wmm_ac_notify_disassoc(wpa_s); if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); /* * Notify the P2P Device interface about a state change in one * of the interfaces. */ wpas_p2p_indicate_state_change(wpa_s); if (wpa_s->wpa_state == WPA_COMPLETED || old_state == WPA_COMPLETED) wpas_notify_auth_changed(wpa_s); #ifdef CONFIG_DPP2 if (wpa_s->wpa_state == WPA_COMPLETED) wpas_dpp_connected(wpa_s); #endif /* CONFIG_DPP2 */ } #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) if (update_fils_connect_params) wpas_update_fils_connect_params(wpa_s); #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ } void wpa_supplicant_terminate_proc(struct wpa_global *global) { int pending = 0; #ifdef CONFIG_WPS struct wpa_supplicant *wpa_s = global->ifaces; while (wpa_s) { struct wpa_supplicant *next = wpa_s->next; if (wpas_wps_terminate_pending(wpa_s) == 1) pending = 1; #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE || (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)) wpas_p2p_disconnect(wpa_s); #endif /* CONFIG_P2P */ wpa_s = next; } #endif /* CONFIG_WPS */ if (pending) return; eloop_terminate(); } static void wpa_supplicant_terminate(int sig, void *signal_ctx) { struct wpa_global *global = signal_ctx; wpa_supplicant_terminate_proc(global); } void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) { enum wpa_states old_state = wpa_s->wpa_state; enum wpa_states new_state; if (old_state == WPA_SCANNING) new_state = WPA_SCANNING; else new_state = WPA_DISCONNECTED; wpa_s->pairwise_cipher = 0; wpa_s->group_cipher = 0; wpa_s->mgmt_group_cipher = 0; wpa_s->key_mgmt = 0; if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) wpa_supplicant_set_state(wpa_s, new_state); if (wpa_s->wpa_state != old_state) wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); } /** * wpa_supplicant_reload_configuration - Reload configuration data * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success or -1 if configuration parsing failed * * This function can be used to request that the configuration data is reloaded * (e.g., after configuration file change). This function is reloading * configuration only for one interface, so this may need to be called multiple * times if %wpa_supplicant is controlling multiple interfaces and all * interfaces need reconfiguration. */ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) { struct wpa_config *conf; int reconf_ctrl; int old_ap_scan; if (wpa_s->confname == NULL) return -1; conf = wpa_config_read(wpa_s->confname, NULL); if (conf == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " "file '%s' - exiting", wpa_s->confname); return -1; } if (wpa_s->confanother && !wpa_config_read(wpa_s->confanother, conf)) { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration file '%s' - exiting", wpa_s->confanother); return -1; } conf->changed_parameters = (unsigned int) -1; reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface || (conf->ctrl_interface && wpa_s->conf->ctrl_interface && os_strcmp(conf->ctrl_interface, wpa_s->conf->ctrl_interface) != 0); if (reconf_ctrl) { wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface); wpa_s->ctrl_iface = NULL; } eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } /* * TODO: should notify EAPOL SM about changes in opensc_engine_path, * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers. */ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { /* * Clear forced success to clear EAP state for next * authentication. */ eapol_sm_notify_eap_success(wpa_s->eapol, false); } eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_sm_set_config(wpa_s->wpa, NULL); wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); rsn_preauth_deinit(wpa_s->wpa); old_ap_scan = wpa_s->conf->ap_scan; wpa_config_free(wpa_s->conf); wpa_s->conf = conf; if (old_ap_scan != wpa_s->conf->ap_scan) wpas_notify_ap_scan_changed(wpa_s); if (reconf_ctrl) wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); wpa_supplicant_update_config(wpa_s); wpa_supplicant_clear_status(wpa_s); if (wpa_supplicant_enabled_networks(wpa_s)) { wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); } wpa_bssid_ignore_clear(wpa_s); wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); return 0; } static void wpa_supplicant_reconfig(int sig, void *signal_ctx) { struct wpa_global *global = signal_ctx; struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring", sig); if (wpa_supplicant_reload_configuration(wpa_s) < 0) { wpa_supplicant_terminate_proc(global); } } if (wpa_debug_reopen_file() < 0) { /* Ignore errors since we cannot really do much to fix this */ wpa_printf(MSG_DEBUG, "Could not reopen debug log file"); } } static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_ie_data *ie) { int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie); if (ret) { if (ret == -2) { wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE " "from association info"); } return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set " "cipher suites"); if (!(ie->group_cipher & ssid->group_cipher)) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group " "cipher 0x%x (mask 0x%x) - reject", ie->group_cipher, ssid->group_cipher); return -1; } if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise " "cipher 0x%x (mask 0x%x) - reject", ie->pairwise_cipher, ssid->pairwise_cipher); return -1; } if (!(ie->key_mgmt & ssid->key_mgmt)) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key " "management 0x%x (mask 0x%x) - reject", ie->key_mgmt, ssid->key_mgmt); return -1; } if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " "that does not support management frame protection - " "reject"); return -1; } return 0; } static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie, int freq) { if (!ie->has_group) ie->group_cipher = wpa_default_rsn_cipher(freq); if (!ie->has_pairwise) ie->pairwise_cipher = wpa_default_rsn_cipher(freq); return (ie->group_cipher & ssid->group_cipher) && (ie->pairwise_cipher & ssid->pairwise_cipher); } /** * wpa_supplicant_set_suites - Set authentication and encryption parameters * @wpa_s: Pointer to wpa_supplicant data * @bss: Scan results for the selected BSS, or %NULL if not available * @ssid: Configuration data for the selected network * @wpa_ie: Buffer for the WPA/RSN IE * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the * used buffer length in case the functions returns success. * Returns: 0 on success or -1 on failure * * This function is used to configure authentication and encryption parameters * based on the network configuration and scan result for the selected BSS (if * available). */ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, u8 *wpa_ie, size_t *wpa_ie_len) { struct wpa_ie_data ie; int sel, proto, sae_pwe; const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen; if (bss) { bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX); bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); } else { bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL; } if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && matching_ciphers(ssid, &ie, bss->freq) && (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); proto = WPA_PROTO_RSN; } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) && wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 && (ie.group_cipher & ssid->group_cipher) && (ie.pairwise_cipher & ssid->pairwise_cipher) && (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); proto = WPA_PROTO_WPA; #ifdef CONFIG_HS20 } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) && wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[1], &ie) == 0 && (ie.group_cipher & ssid->group_cipher) && (ie.pairwise_cipher & ssid->pairwise_cipher) && (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN"); proto = WPA_PROTO_OSEN; } else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) && wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && (ie.group_cipher & ssid->group_cipher) && (ie.pairwise_cipher & ssid->pairwise_cipher) && (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using OSEN (within RSN)"); proto = WPA_PROTO_RSN; #endif /* CONFIG_HS20 */ } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); wpa_dbg(wpa_s, MSG_DEBUG, "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", ssid->proto, ssid->pairwise_cipher, ssid->group_cipher, ssid->key_mgmt); wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s", MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), bss_wpa ? " WPA" : "", bss_rsn ? " RSN" : "", bss_osen ? " OSEN" : ""); if (bss_rsn) { wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]); if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) { wpa_dbg(wpa_s, MSG_DEBUG, "Could not parse RSN element"); } else { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", ie.pairwise_cipher, ie.group_cipher, ie.key_mgmt); } } if (bss_wpa) { wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]); if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) { wpa_dbg(wpa_s, MSG_DEBUG, "Could not parse WPA element"); } else { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", ie.pairwise_cipher, ie.group_cipher, ie.key_mgmt); } } return -1; } else { if (ssid->proto & WPA_PROTO_OSEN) proto = WPA_PROTO_OSEN; else if (ssid->proto & WPA_PROTO_RSN) proto = WPA_PROTO_RSN; else proto = WPA_PROTO_WPA; if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) { os_memset(&ie, 0, sizeof(ie)); ie.group_cipher = ssid->group_cipher; ie.pairwise_cipher = ssid->pairwise_cipher; ie.key_mgmt = ssid->key_mgmt; ie.mgmt_group_cipher = 0; if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { if (ssid->group_mgmt_cipher & WPA_CIPHER_BIP_GMAC_256) ie.mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; else if (ssid->group_mgmt_cipher & WPA_CIPHER_BIP_CMAC_256) ie.mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; else if (ssid->group_mgmt_cipher & WPA_CIPHER_BIP_GMAC_128) ie.mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; else ie.mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; } #ifdef CONFIG_OWE if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only && !bss_wpa && !bss_rsn && !bss_osen) { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_s->wpa_proto = 0; *wpa_ie_len = 0; return 0; } #endif /* CONFIG_OWE */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites " "based on configuration"); } else proto = ie.proto; } wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d " "pairwise %d key_mgmt %d proto %d", ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto); if (ssid->ieee80211w) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d", ie.mgmt_group_cipher); } wpa_s->wpa_proto = proto; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN))); if (bss || !wpa_s->ap_ies_from_associnfo) { if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, bss_wpa ? 2 + bss_wpa[1] : 0) || wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, bss_rsn ? 2 + bss_rsn[1] : 0) || wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx, bss_rsnx ? 2 + bss_rsnx[1] : 0)) return -1; } #ifdef CONFIG_NO_WPA wpa_s->group_cipher = WPA_CIPHER_NONE; wpa_s->pairwise_cipher = WPA_CIPHER_NONE; #else /* CONFIG_NO_WPA */ sel = ie.group_cipher & ssid->group_cipher; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: AP group 0x%x network profile group 0x%x; available group 0x%x", ie.group_cipher, ssid->group_cipher, sel); wpa_s->group_cipher = wpa_pick_group_cipher(sel); if (wpa_s->group_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " "cipher"); return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s", wpa_cipher_txt(wpa_s->group_cipher)); sel = ie.pairwise_cipher & ssid->pairwise_cipher; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: AP pairwise 0x%x network profile pairwise 0x%x; available pairwise 0x%x", ie.pairwise_cipher, ssid->pairwise_cipher, sel); wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1); if (wpa_s->pairwise_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " "cipher"); return -1; } wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s", wpa_cipher_txt(wpa_s->pairwise_cipher)); #endif /* CONFIG_NO_WPA */ sel = ie.key_mgmt & ssid->key_mgmt; #ifdef CONFIG_SAE if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211R if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME | WPA_DRIVER_FLAGS_UPDATE_FT_IES))) sel &= ~WPA_KEY_MGMT_FT; #endif /* CONFIG_IEEE80211R */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: AP key_mgmt 0x%x network profile key_mgmt 0x%x; available key_mgmt 0x%x", ie.key_mgmt, ssid->key_mgmt, sel); if (0) { #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SHA384 } else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) && os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X-SHA384"); if (!ssid->ft_eap_pmksa_caching && pmksa_cache_get_current(wpa_s->wpa)) { /* PMKSA caching with FT may have interoperability * issues, so disable that case by default for now. */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Disable PMKSA caching for FT/802.1X connection"); pmksa_cache_clear_current(wpa_s->wpa); } #endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SUITEB192 } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X with Suite B (192-bit)"); #endif /* CONFIG_SUITEB192 */ #ifdef CONFIG_SUITEB } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X with Suite B"); #endif /* CONFIG_SUITEB */ #ifdef CONFIG_FILS #ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384"); } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256"); #endif /* CONFIG_IEEE80211R */ } else if (sel & WPA_KEY_MGMT_FILS_SHA384) { wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384"); } else if (sel & WPA_KEY_MGMT_FILS_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256"); #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R } else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X) && os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); if (!ssid->ft_eap_pmksa_caching && pmksa_cache_get_current(wpa_s->wpa)) { /* PMKSA caching with FT may have interoperability * issues, so disable that case by default for now. */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Disable PMKSA caching for FT/802.1X connection"); pmksa_cache_clear_current(wpa_s->wpa); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_DPP } else if (sel & WPA_KEY_MGMT_DPP) { wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); #endif /* CONFIG_DPP */ #ifdef CONFIG_SAE } else if (sel & WPA_KEY_MGMT_FT_SAE) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE"); } else if (sel & WPA_KEY_MGMT_SAE) { wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); #endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); #endif /* CONFIG_IEEE80211R */ } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X with SHA256"); } else if (sel & WPA_KEY_MGMT_PSK_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT PSK with SHA256"); } else if (sel & WPA_KEY_MGMT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); } else if (sel & WPA_KEY_MGMT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); } else if (sel & WPA_KEY_MGMT_WPA_NONE) { wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); #ifdef CONFIG_HS20 } else if (sel & WPA_KEY_MGMT_OSEN) { wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN; wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN"); #endif /* CONFIG_HS20 */ #ifdef CONFIG_OWE } else if (sel & WPA_KEY_MGMT_OWE) { wpa_s->key_mgmt = WPA_KEY_MGMT_OWE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE"); #endif /* CONFIG_OWE */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); return -1; } wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, wpa_s->pairwise_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_msg(wpa_s, MSG_INFO, "RSN: Management frame protection required but the selected AP does not enable it"); return -1; } sel = ie.mgmt_group_cipher; if (ssid->group_mgmt_cipher) sel &= ssid->group_mgmt_cipher; if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel); if (sel & WPA_CIPHER_AES_128_CMAC) { wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " "AES-128-CMAC"); } else if (sel & WPA_CIPHER_BIP_GMAC_128) { wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " "BIP-GMAC-128"); } else if (sel & WPA_CIPHER_BIP_GMAC_256) { wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " "BIP-GMAC-256"); } else if (sel & WPA_CIPHER_BIP_CMAC_256) { wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " "BIP-CMAC-256"); } else { wpa_s->mgmt_group_cipher = 0; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); } wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, wpas_get_ssid_pmf(wpa_s, ssid)); #ifdef CONFIG_OCV if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv); #endif /* CONFIG_OCV */ sae_pwe = wpa_s->conf->sae_pwe; if (ssid->sae_password_id && sae_pwe != 3) sae_pwe = 1; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe); #ifdef CONFIG_SAE_PK wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK, wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_pk != SAE_PK_MODE_DISABLED && ((ssid->sae_password && sae_pk_valid_password(ssid->sae_password)) || (!ssid->sae_password && ssid->passphrase && sae_pk_valid_password(ssid->passphrase)))); #endif /* CONFIG_SAE_PK */ #ifdef CONFIG_TESTING_OPTIONS wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED, wpa_s->ft_rsnxe_used); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL, wpa_s->oci_freq_override_eapol); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2, wpa_s->oci_freq_override_eapol_g2); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC, wpa_s->oci_freq_override_ft_assoc); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FILS_ASSOC, wpa_s->oci_freq_override_fils_assoc); #endif /* CONFIG_TESTING_OPTIONS */ /* Extended Key ID is only supported in infrastructure BSS so far */ if (ssid->mode == WPAS_MODE_INFRA && wpa_s->conf->extended_key_id && (ssid->proto & WPA_PROTO_RSN) && ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) { int use_ext_key_id = 0; wpa_msg(wpa_s, MSG_DEBUG, "WPA: Enable Extended Key ID support"); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXT_KEY_ID, wpa_s->conf->extended_key_id); if (bss_rsn && wpa_s->conf->extended_key_id && wpa_s->pairwise_cipher != WPA_CIPHER_TKIP && (ie.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) use_ext_key_id = 1; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, use_ext_key_id); } else { wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXT_KEY_ID, 0); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0); } if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); return -1; } wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe); if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe, &wpa_s->rsnxe_len)) { wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE"); return -1; } if (0) { #ifdef CONFIG_DPP } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { /* Use PMK from DPP network introduction (PMKSA entry) */ wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); #ifdef CONFIG_DPP2 wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DPP_PFS, ssid->dpp_pfs); #endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ } else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { int psk_set = 0; int sae_only; sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256)) == 0; if (ssid->psk_set && !sae_only) { wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)", ssid->psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL); psk_set = 1; } if (wpa_key_mgmt_sae(ssid->key_mgmt) && (ssid->sae_password || ssid->passphrase)) psk_set = 1; #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase && !sae_only) { u8 psk[PMK_LEN]; pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len, 4096, psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } #endif /* CONFIG_NO_PBKDF2 */ #ifdef CONFIG_EXT_PASSWORD if (ssid->ext_psk && !sae_only) { struct wpabuf *pw = ext_password_get(wpa_s->ext_pw, ssid->ext_psk); char pw_str[64 + 1]; u8 psk[PMK_LEN]; if (pw == NULL) { wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK " "found from external storage"); return -1; } if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) { wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected " "PSK length %d in external storage", (int) wpabuf_len(pw)); ext_password_free(pw); return -1; } os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw)); pw_str[wpabuf_len(pw)] = '\0'; #ifndef CONFIG_NO_PBKDF2 if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss) { pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len, 4096, psk, PMK_LEN); os_memset(pw_str, 0, sizeof(pw_str)); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " "external passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else #endif /* CONFIG_NO_PBKDF2 */ if (wpabuf_len(pw) == 2 * PMK_LEN) { if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) { wpa_msg(wpa_s, MSG_INFO, "EXT PW: " "Invalid PSK hex string"); os_memset(pw_str, 0, sizeof(pw_str)); ext_password_free(pw); return -1; } wpa_hexdump_key(MSG_MSGDUMP, "PSK (from external PSK)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else { wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " "PSK available"); os_memset(pw_str, 0, sizeof(pw_str)); ext_password_free(pw); return -1; } os_memset(pw_str, 0, sizeof(pw_str)); ext_password_free(pw); } #endif /* CONFIG_EXT_PASSWORD */ if (!psk_set) { wpa_msg(wpa_s, MSG_INFO, "No PSK available for association"); wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE"); return -1; } #ifdef CONFIG_OWE } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) { /* OWE Diffie-Hellman exchange in (Re)Association * Request/Response frames set the PMK, so do not override it * here. */ #endif /* CONFIG_OWE */ } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); if (ssid->mode != WPAS_MODE_IBSS && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) && (ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER || (ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) { wpa_msg(wpa_s, MSG_INFO, "Disable PTK0 rekey support - replaced with reconnect"); wpa_s->deny_ptk0_rekey = 1; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 1); } else { wpa_s->deny_ptk0_rekey = 0; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 0); } return 0; } static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) { *pos = 0x00; switch (idx) { case 0: /* Bits 0-7 */ break; case 1: /* Bits 8-15 */ if (wpa_s->conf->coloc_intf_reporting) { /* Bit 13 - Collocated Interference Reporting */ *pos |= 0x20; } break; case 2: /* Bits 16-23 */ #ifdef CONFIG_WNM *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ if (!wpa_s->disable_mbo_oce && !wpa_s->conf->disable_btm) *pos |= 0x08; /* Bit 19 - BSS Transition */ #endif /* CONFIG_WNM */ break; case 3: /* Bits 24-31 */ #ifdef CONFIG_WNM *pos |= 0x02; /* Bit 25 - SSID List */ #endif /* CONFIG_WNM */ #ifdef CONFIG_INTERWORKING if (wpa_s->conf->interworking) *pos |= 0x80; /* Bit 31 - Interworking */ #endif /* CONFIG_INTERWORKING */ break; case 4: /* Bits 32-39 */ #ifdef CONFIG_INTERWORKING if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING) *pos |= 0x01; /* Bit 32 - QoS Map */ #endif /* CONFIG_INTERWORKING */ break; case 5: /* Bits 40-47 */ #ifdef CONFIG_HS20 if (wpa_s->conf->hs20) *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ break; case 7: /* Bits 56-63 */ break; case 8: /* Bits 64-71 */ if (wpa_s->conf->ftm_responder) *pos |= 0x40; /* Bit 70 - FTM responder */ if (wpa_s->conf->ftm_initiator) *pos |= 0x80; /* Bit 71 - FTM initiator */ break; case 9: /* Bits 72-79 */ #ifdef CONFIG_FILS if (!wpa_s->disable_fils) *pos |= 0x01; #endif /* CONFIG_FILS */ break; case 10: /* Bits 80-87 */ *pos |= 0x20; /* Bit 85 - Mirrored SCS */ break; } } int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) { u8 *pos = buf; u8 len = 11, i; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len; if (buflen < (size_t) len + 2) { wpa_printf(MSG_INFO, "Not enough room for building extended capabilities element"); return -1; } *pos++ = WLAN_EID_EXT_CAPAB; *pos++ = len; for (i = 0; i < len; i++, pos++) { wpas_ext_capab_byte(wpa_s, pos, i); if (i < wpa_s->extended_capa_len) { *pos &= ~wpa_s->extended_capa_mask[i]; *pos |= wpa_s->extended_capa[i]; } } while (len > 0 && buf[1 + len] == 0) { len--; buf[1] = len; } if (len == 0) return 0; return 2 + len; } static int wpas_valid_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss) { struct wpa_bss *bss; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (bss == test_bss) return 1; } return 0; } static int wpas_valid_ssid(struct wpa_supplicant *wpa_s, struct wpa_ssid *test_ssid) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == test_ssid) return 1; } return 0; } int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, struct wpa_ssid *test_ssid) { if (test_bss && !wpas_valid_bss(wpa_s, test_bss)) return 0; return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid); } void wpas_connect_work_free(struct wpa_connect_work *cwork) { if (cwork == NULL) return; os_free(cwork); } void wpas_connect_work_done(struct wpa_supplicant *wpa_s) { struct wpa_connect_work *cwork; struct wpa_radio_work *work = wpa_s->connect_work; if (!work) return; wpa_s->connect_work = NULL; cwork = work->ctx; work->ctx = NULL; wpas_connect_work_free(cwork); radio_work_done(work); } int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style) { struct os_reltime now; u8 addr[ETH_ALEN]; os_get_reltime(&now); if (wpa_s->last_mac_addr_style == style && wpa_s->last_mac_addr_change.sec != 0 && !os_reltime_expired(&now, &wpa_s->last_mac_addr_change, wpa_s->conf->rand_addr_lifetime)) { wpa_msg(wpa_s, MSG_DEBUG, "Previously selected random MAC address has not yet expired"); return 0; } switch (style) { case 1: if (random_mac_addr(addr) < 0) return -1; break; case 2: os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN); if (random_mac_addr_keep_oui(addr) < 0) return -1; break; default: return -1; } if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to set random MAC address"); return -1; } os_get_reltime(&wpa_s->last_mac_addr_change); wpa_s->mac_addr_changed = 1; wpa_s->last_mac_addr_style = style; if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Could not update MAC address information"); return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR, MAC2STR(addr)); return 0; } int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s) { if (wpa_s->wpa_state >= WPA_AUTHENTICATING || !wpa_s->conf->preassoc_mac_addr) return 0; return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr); } static void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid) { #ifdef CONFIG_SAE int *groups = conf->sae_groups; int default_groups[] = { 19, 20, 21, 0 }; const char *password; if (!groups || groups[0] <= 0) groups = default_groups; password = ssid->sae_password; if (!password) password = ssid->passphrase; if (!password || (conf->sae_pwe == 0 && !ssid->sae_password_id && !sae_pk_valid_password(password)) || conf->sae_pwe == 3) { /* PT derivation not needed */ sae_deinit_pt(ssid->pt); ssid->pt = NULL; return; } if (ssid->pt) return; /* PT already derived */ ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, (const u8 *) password, os_strlen(password), ssid->sae_password_id); #endif /* CONFIG_SAE */ } static void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s) { #if defined(CONFIG_SAE) && defined(CONFIG_SME) os_free(wpa_s->sme.sae_rejected_groups); wpa_s->sme.sae_rejected_groups = NULL; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->extra_sae_rejected_groups) { int i, *groups = wpa_s->extra_sae_rejected_groups; for (i = 0; groups[i]; i++) { wpa_printf(MSG_DEBUG, "TESTING: Indicate rejection of an extra SAE group %d", groups[i]); int_array_add_unique(&wpa_s->sme.sae_rejected_groups, groups[i]); } } #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_SAE && CONFIG_SME */ } int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s) { if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) { wpa_msg(wpa_s, MSG_INFO, "Could not restore permanent MAC address"); return -1; } wpa_s->mac_addr_changed = 0; if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Could not update MAC address information"); return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address"); return 0; } static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); /** * wpa_supplicant_associate - Request association * @wpa_s: Pointer to wpa_supplicant data * @bss: Scan results for the selected BSS, or %NULL if not available * @ssid: Configuration data for the selected network * * This function is used to request %wpa_supplicant to associate with a BSS. */ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { struct wpa_connect_work *cwork; int rand_style; wpa_s->own_disconnect_req = 0; wpa_s->own_reconnect_req = 0; /* * If we are starting a new connection, any previously pending EAPOL * RX cannot be valid anymore. */ wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = NULL; if (ssid->mac_addr == -1) rand_style = wpa_s->conf->mac_addr; else rand_style = ssid->mac_addr; wpa_s->multi_ap_ie = 0; wmm_ac_clear_saved_tspecs(wpa_s); wpa_s->reassoc_same_bss = 0; wpa_s->reassoc_same_ess = 0; #ifdef CONFIG_TESTING_OPTIONS wpa_s->testing_resend_assoc = 0; #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->last_ssid == ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); wpa_s->reassoc_same_ess = 1; if (wpa_s->current_bss && wpa_s->current_bss == bss) { wmm_ac_save_tspecs(wpa_s); wpa_s->reassoc_same_bss = 1; } else if (wpa_s->current_bss && wpa_s->current_bss != bss) { os_get_reltime(&wpa_s->roam_start); } } else { #ifdef CONFIG_SAE wpa_s_clear_sae_rejected(wpa_s); wpa_s_setup_sae_pt(wpa_s->conf, ssid); #endif /* CONFIG_SAE */ } if (rand_style > 0 && !wpa_s->reassoc_same_ess) { if (wpas_update_random_addr(wpa_s, rand_style) < 0) return; wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); } else if (rand_style == 0 && wpa_s->mac_addr_changed) { if (wpas_restore_permanent_mac_addr(wpa_s) < 0) return; } wpa_s->last_ssid = ssid; #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; #else /* CONFIG_IBSS_RSN */ if (ssid->mode == WPAS_MODE_IBSS && !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) { wpa_msg(wpa_s, MSG_INFO, "IBSS RSN not supported in the build"); return; } #endif /* CONFIG_IBSS_RSN */ if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { #ifdef CONFIG_AP if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) { wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP " "mode"); return; } if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) wpas_p2p_ap_setup_failed(wpa_s); return; } wpa_s->current_bss = bss; #else /* CONFIG_AP */ wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in " "the build"); #endif /* CONFIG_AP */ return; } if (ssid->mode == WPAS_MODE_MESH) { #ifdef CONFIG_MESH if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) { wpa_msg(wpa_s, MSG_INFO, "Driver does not support mesh mode"); return; } if (bss) ssid->frequency = bss->freq; if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh"); return; } wpa_s->current_bss = bss; #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); #endif /* CONFIG_MESH */ return; } /* * Set WPA state machine configuration to match the selected network now * so that the information is available before wpas_start_assoc_cb() * gets called. This is needed at least for RSN pre-authentication where * candidate APs are added to a list based on scan result processing * before completion of the first association. */ wpa_supplicant_rsn_supp_set_config(wpa_s, ssid); #ifdef CONFIG_DPP if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0) return; #endif /* CONFIG_DPP */ #ifdef CONFIG_TDLS if (bss) wpa_tdls_ap_ies(wpa_s->wpa, wpa_bss_ie_ptr(bss), bss->ie_len); #endif /* CONFIG_TDLS */ #ifdef CONFIG_MBO wpas_mbo_check_pmf(wpa_s, bss, ssid); #endif /* CONFIG_MBO */ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && ssid->mode == WPAS_MODE_INFRA) { sme_authenticate(wpa_s, bss, ssid); return; } if (wpa_s->connect_work) { wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist"); return; } if (radio_work_pending(wpa_s, "connect")) { wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist"); return; } #ifdef CONFIG_SME if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) { /* Clear possibly set auth_alg, if any, from last attempt. */ wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN; } #endif /* CONFIG_SME */ wpas_abort_ongoing_scan(wpa_s); cwork = os_zalloc(sizeof(*cwork)); if (cwork == NULL) return; cwork->bss = bss; cwork->ssid = ssid; if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1, wpas_start_assoc_cb, cwork) < 0) { os_free(cwork); } } static int bss_is_ibss(struct wpa_bss *bss) { return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) == IEEE80211_CAP_IBSS; } static int drv_supports_vht(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid) { enum hostapd_hw_mode hw_mode; struct hostapd_hw_modes *mode = NULL; u8 channel; int i; hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel); if (hw_mode == NUM_HOSTAPD_MODES) return 0; for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].mode == hw_mode) { mode = &wpa_s->hw.modes[i]; break; } } if (!mode) return 0; return mode->vht_capab != 0; } void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq) { int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode); enum hostapd_hw_mode hw_mode; struct hostapd_hw_modes *mode = NULL; int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; int vht80[] = { 36, 52, 100, 116, 132, 149 }; struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; u8 channel; int i, chan_idx, ht40 = -1, res, obss_scan = 1; unsigned int j, k; struct hostapd_freq_params vht_freq; int chwidth, seg0, seg1; u32 vht_caps = 0; int is_24ghz; freq->freq = ssid->frequency; for (j = 0; j < wpa_s->last_scan_res_used; j++) { struct wpa_bss *bss = wpa_s->last_scan_res[j]; if (ssid->mode != WPAS_MODE_IBSS) break; /* Don't adjust control freq in case of fixed_freq */ if (ssid->fixed_freq) break; if (!bss_is_ibss(bss)) continue; if (ssid->ssid_len == bss->ssid_len && os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) { wpa_printf(MSG_DEBUG, "IBSS already found in scan results, adjust control freq: %d", bss->freq); freq->freq = bss->freq; obss_scan = 0; break; } } /* For IBSS check HT_IBSS flag */ if (ssid->mode == WPAS_MODE_IBSS && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS)) return; if (wpa_s->group_cipher == WPA_CIPHER_WEP40 || wpa_s->group_cipher == WPA_CIPHER_WEP104 || wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "IBSS: WEP/TKIP detected, do not try to enable HT"); return; } hw_mode = ieee80211_freq_to_chan(freq->freq, &channel); for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].mode == hw_mode) { mode = &wpa_s->hw.modes[i]; break; } } if (!mode) return; freq->channel = channel; is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G || hw_mode == HOSTAPD_MODE_IEEE80211B; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht) { freq->ht_enabled = 0; return; } #endif /* CONFIG_HT_OVERRIDES */ freq->ht_enabled = ht_supported(mode); if (!freq->ht_enabled) return; /* Allow HE on 2.4 GHz without VHT: see nl80211_put_freq_params() */ if (is_24ghz) freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; #ifdef CONFIG_HE_OVERRIDES if (is_24ghz && ssid->disable_he) freq->he_enabled = 0; #endif /* CONFIG_HE_OVERRIDES */ /* Setup higher BW only for 5 GHz */ if (mode->mode != HOSTAPD_MODE_IEEE80211A) return; for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) { pri_chan = &mode->channels[chan_idx]; if (pri_chan->chan == channel) break; pri_chan = NULL; } if (!pri_chan) return; /* Check primary channel flags */ if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; freq->channel = pri_chan->chan; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht40) { #ifdef CONFIG_VHT_OVERRIDES if (ssid->disable_vht) return; #endif /* CONFIG_VHT_OVERRIDES */ goto skip_ht40; } #endif /* CONFIG_HT_OVERRIDES */ /* Check/setup HT40+/HT40- */ for (j = 0; j < ARRAY_SIZE(ht40plus); j++) { if (ht40plus[j] == channel) { ht40 = 1; break; } } /* Find secondary channel */ for (i = 0; i < mode->num_channels; i++) { sec_chan = &mode->channels[i]; if (sec_chan->chan == channel + ht40 * 4) break; sec_chan = NULL; } if (!sec_chan) return; /* Check secondary channel flags */ if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; if (ht40 == -1) { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) return; } else { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS)) return; } freq->sec_channel_offset = ht40; if (obss_scan) { struct wpa_scan_results *scan_res; scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); if (scan_res == NULL) { /* Back to HT20 */ freq->sec_channel_offset = 0; return; } res = check_40mhz_5g(scan_res, pri_chan, sec_chan); switch (res) { case 0: /* Back to HT20 */ freq->sec_channel_offset = 0; break; case 1: /* Configuration allowed */ break; case 2: /* Switch pri/sec channels */ freq->freq = hw_get_freq(mode, sec_chan->chan); freq->sec_channel_offset = -freq->sec_channel_offset; freq->channel = sec_chan->chan; break; default: freq->sec_channel_offset = 0; break; } wpa_scan_results_free(scan_res); } #ifdef CONFIG_HT_OVERRIDES skip_ht40: #endif /* CONFIG_HT_OVERRIDES */ wpa_printf(MSG_DEBUG, "IBSS/mesh: setup freq channel %d, sec_channel_offset %d", freq->channel, freq->sec_channel_offset); if (!drv_supports_vht(wpa_s, ssid)) return; /* For IBSS check VHT_IBSS flag */ if (ssid->mode == WPAS_MODE_IBSS && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) return; vht_freq = *freq; #ifdef CONFIG_VHT_OVERRIDES if (ssid->disable_vht) { freq->vht_enabled = 0; return; } #endif /* CONFIG_VHT_OVERRIDES */ vht_freq.vht_enabled = vht_supported(mode); if (!vht_freq.vht_enabled) return; /* Enable HE with VHT for 5 GHz */ freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; /* setup center_freq1, bandwidth */ for (j = 0; j < ARRAY_SIZE(vht80); j++) { if (freq->channel >= vht80[j] && freq->channel < vht80[j] + 16) break; } if (j == ARRAY_SIZE(vht80)) return; for (i = vht80[j]; i < vht80[j] + 16; i += 4) { struct hostapd_channel_data *chan; chan = hw_get_channel_chan(mode, i, NULL); if (!chan) return; /* Back to HT configuration if channel not usable */ if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; } chwidth = CHANWIDTH_80MHZ; seg0 = vht80[j] + 6; seg1 = 0; if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) { /* setup center_freq2, bandwidth */ for (k = 0; k < ARRAY_SIZE(vht80); k++) { /* Only accept 80 MHz segments separated by a gap */ if (j == k || abs(vht80[j] - vht80[k]) == 16) continue; for (i = vht80[k]; i < vht80[k] + 16; i += 4) { struct hostapd_channel_data *chan; chan = hw_get_channel_chan(mode, i, NULL); if (!chan) continue; if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR | HOSTAPD_CHAN_RADAR)) continue; /* Found a suitable second segment for 80+80 */ chwidth = CHANWIDTH_80P80MHZ; vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; seg1 = vht80[k] + 6; } if (chwidth == CHANWIDTH_80P80MHZ) break; } } else if (ssid->max_oper_chwidth == CHANWIDTH_160MHZ) { if (freq->freq == 5180) { chwidth = CHANWIDTH_160MHZ; vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; seg0 = 50; } else if (freq->freq == 5520) { chwidth = CHANWIDTH_160MHZ; vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; seg0 = 114; } } else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) { chwidth = CHANWIDTH_USE_HT; seg0 = vht80[j] + 2; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht40) seg0 = 0; #endif /* CONFIG_HT_OVERRIDES */ } #ifdef CONFIG_HE_OVERRIDES if (ssid->disable_he) { vht_freq.he_enabled = 0; freq->he_enabled = 0; } #endif /* CONFIG_HE_OVERRIDES */ if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, freq->channel, ssid->enable_edmg, ssid->edmg_channel, freq->ht_enabled, vht_freq.vht_enabled, freq->he_enabled, freq->sec_channel_offset, chwidth, seg0, seg1, vht_caps, &mode->he_capab[ieee80211_mode]) != 0) return; *freq = vht_freq; wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d", freq->center_freq1, freq->center_freq2, freq->bandwidth); } #ifdef CONFIG_FILS static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf, size_t ie_buf_len) { struct fils_hlp_req *req; size_t rem_len, hdr_len, hlp_len, len, ie_len = 0; const u8 *pos; u8 *buf = ie_buf; dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, list) { rem_len = ie_buf_len - ie_len; pos = wpabuf_head(req->pkt); hdr_len = 1 + 2 * ETH_ALEN + 6; hlp_len = wpabuf_len(req->pkt); if (rem_len < 2 + hdr_len + hlp_len) { wpa_printf(MSG_ERROR, "FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu", (unsigned long) rem_len, (unsigned long) (2 + hdr_len + hlp_len)); break; } len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len; /* Element ID */ *buf++ = WLAN_EID_EXTENSION; /* Length */ *buf++ = len; /* Element ID Extension */ *buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER; /* Destination MAC address */ os_memcpy(buf, req->dst, ETH_ALEN); buf += ETH_ALEN; /* Source MAC address */ os_memcpy(buf, wpa_s->own_addr, ETH_ALEN); buf += ETH_ALEN; /* LLC/SNAP Header */ os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6); buf += 6; /* HLP Packet */ os_memcpy(buf, pos, len - hdr_len); buf += len - hdr_len; pos += len - hdr_len; hlp_len -= len - hdr_len; ie_len += 2 + len; rem_len -= 2 + len; while (hlp_len) { len = (hlp_len > 255) ? 255 : hlp_len; if (rem_len < 2 + len) break; *buf++ = WLAN_EID_FRAGMENT; *buf++ = len; os_memcpy(buf, pos, len); buf += len; pos += len; hlp_len -= len; ie_len += 2 + len; rem_len -= 2 + len; } } return ie_len; } int wpa_is_fils_supported(struct wpa_supplicant *wpa_s) { return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) || (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD))); } int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_FILS_SK_PFS return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS); #else /* CONFIG_FILS_SK_PFS */ return 0; #endif /* CONFIG_FILS_SK_PFS */ } #endif /* CONFIG_FILS */ static u8 * wpas_populate_assoc_ies( struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params, enum wpa_drv_update_connect_params_mask *mask) { u8 *wpa_ie; size_t max_wpa_ie_len = 500; size_t wpa_ie_len; int algs = WPA_AUTH_ALG_OPEN; #ifdef CONFIG_MBO const u8 *mbo_ie; #endif #if defined(CONFIG_SAE) || defined(CONFIG_FILS) int pmksa_cached = 0; #endif /* CONFIG_SAE || CONFIG_FILS */ #ifdef CONFIG_FILS const u8 *realm, *username, *rrk; size_t realm_len, username_len, rrk_len; u16 next_seq_num; struct fils_hlp_req *req; dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, list) { max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) + 2 + 2 * wpabuf_len(req->pkt) / 255; } #endif /* CONFIG_FILS */ wpa_ie = os_malloc(max_wpa_ie_len); if (!wpa_ie) { wpa_printf(MSG_ERROR, "Failed to allocate connect IE buffer for %lu bytes", (unsigned long) max_wpa_ie_len); return NULL; } if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; const u8 *cache_id = NULL; try_opportunistic = (ssid->proactive_key_caching < 0 ? wpa_s->conf->okc : ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); #ifdef CONFIG_FILS if (wpa_key_mgmt_fils(ssid->key_mgmt)) cache_id = wpa_bss_get_fils_cache_id(bss); #endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, try_opportunistic, cache_id, 0) == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); #if defined(CONFIG_SAE) || defined(CONFIG_FILS) pmksa_cached = 1; #endif /* CONFIG_SAE || CONFIG_FILS */ } wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites"); os_free(wpa_ie); return NULL; } #ifdef CONFIG_HS20 } else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { /* No PMKSA caching, but otherwise similar to RSN/WPA */ wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites"); os_free(wpa_ie); return NULL; } #endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* * Both WPA and non-WPA IEEE 802.1X enabled in configuration - * use non-WPA since the scan results did not indicate that the * AP is using WPA or WPA2. */ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_ie_len = 0; wpa_s->wpa_proto = 0; } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites (no " "scan results)"); os_free(wpa_ie); return NULL; } #ifdef CONFIG_WPS } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { struct wpabuf *wps_ie; wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) { wpa_ie_len = wpabuf_len(wps_ie); os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len); } else wpa_ie_len = 0; wpabuf_free(wps_ie); wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY)) params->wps = WPS_MODE_PRIVACY; else params->wps = WPS_MODE_OPEN; wpa_s->wpa_proto = 0; #endif /* CONFIG_WPS */ } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_ie_len = 0; wpa_s->wpa_proto = 0; } #ifdef IEEE8021X_EAPOL if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (ssid->leap) { if (ssid->non_leap == 0) algs = WPA_AUTH_ALG_LEAP; else algs |= WPA_AUTH_ALG_LEAP; } } #ifdef CONFIG_FILS /* Clear FILS association */ wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0); if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) && ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) && eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username, &username_len, &realm, &realm_len, &next_seq_num, &rrk, &rrk_len) == 0 && (!wpa_s->last_con_fail_realm || wpa_s->last_con_fail_realm_len != realm_len || os_memcmp(wpa_s->last_con_fail_realm, realm, realm_len) != 0)) { algs = WPA_AUTH_ALG_FILS; params->fils_erp_username = username; params->fils_erp_username_len = username_len; params->fils_erp_realm = realm; params->fils_erp_realm_len = realm_len; params->fils_erp_next_seq_num = next_seq_num; params->fils_erp_rrk = rrk; params->fils_erp_rrk_len = rrk_len; if (mask) *mask |= WPA_DRV_UPDATE_FILS_ERP_INFO; } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) && ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) && pmksa_cached) { algs = WPA_AUTH_ALG_FILS; } #endif /* CONFIG_FILS */ #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_SAE if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) algs = WPA_AUTH_ALG_SAE; #endif /* CONFIG_SAE */ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); if (ssid->auth_alg) { algs = ssid->auth_alg; wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: 0x%x", algs); } #ifdef CONFIG_SAE if (pmksa_cached && algs == WPA_AUTH_ALG_SAE) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt"); algs = WPA_AUTH_ALG_OPEN; } #endif /* CONFIG_SAE */ #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; size_t len; int res; pos = wpa_ie + wpa_ie_len; len = max_wpa_ie_len - wpa_ie_len; res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, ssid->p2p_group); if (res >= 0) wpa_ie_len += res; } wpa_s->cross_connect_disallowed = 0; if (bss) { struct wpabuf *p2p; p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); if (p2p) { wpa_s->cross_connect_disallowed = p2p_get_cross_connect_disallowed(p2p); wpabuf_free(p2p); wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross " "connection", wpa_s->cross_connect_disallowed ? "disallows" : "allows"); } } os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ if (bss) { wpa_ie_len += wpas_supp_op_class_ie(wpa_s, ssid, bss, wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len); } /* * Workaround: Add Extended Capabilities element only if the AP * included this element in Beacon/Probe Response frames. Some older * APs seem to have interoperability issues if this element is * included, so while the standard may require us to include the * element in all cases, it is justifiable to skip it to avoid * interoperability issues. */ if (ssid->p2p_group) wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); else wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { u8 ext_capab[18]; int ext_capab_len; ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0 && wpa_ie_len + ext_capab_len <= max_wpa_ie_len) { u8 *pos = wpa_ie; if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) pos += 2 + pos[1]; os_memmove(pos + ext_capab_len, pos, wpa_ie_len - (pos - wpa_ie)); wpa_ie_len += ext_capab_len; os_memcpy(pos, ext_capab, ext_capab_len); } } #ifdef CONFIG_HS20 if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN); if (hs20) { int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); size_t len; wpas_hs20_add_indication(hs20, pps_mo_id, get_hs20_version(bss)); wpas_hs20_add_roam_cons_sel(hs20, ssid); len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(hs20) <= len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20), wpabuf_len(hs20)); wpa_ie_len += wpabuf_len(hs20); } wpabuf_free(hs20); hs20_configure_frame_filters(wpa_s); } } #endif /* CONFIG_HS20 */ if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(buf) <= len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(buf), wpabuf_len(buf)); wpa_ie_len += wpabuf_len(buf); } } #ifdef CONFIG_FST if (wpa_s->fst_ies) { int fst_ies_len = wpabuf_len(wpa_s->fst_ies); if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wpa_s->fst_ies), fst_ies_len); wpa_ie_len += fst_ies_len; } } #endif /* CONFIG_FST */ #ifdef CONFIG_MBO mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL; if (!wpa_s->disable_mbo_oce && mbo_ie) { int len; len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len, !!mbo_attr_from_mbo_ie(mbo_ie, OCE_ATTR_ID_CAPA_IND)); if (len >= 0) wpa_ie_len += len; } #endif /* CONFIG_MBO */ #ifdef CONFIG_FILS if (algs == WPA_AUTH_ALG_FILS) { size_t len; len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len); wpa_ie_len += len; } #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE #ifdef CONFIG_TESTING_OPTIONS if (get_ie_ext(wpa_ie, wpa_ie_len, WLAN_EID_EXT_OWE_DH_PARAM)) { wpa_printf(MSG_INFO, "TESTING: Override OWE DH element"); } else #endif /* CONFIG_TESTING_OPTIONS */ if (algs == WPA_AUTH_ALG_OPEN && ssid->key_mgmt == WPA_KEY_MGMT_OWE) { struct wpabuf *owe_ie; u16 group; if (ssid->owe_group) { group = ssid->owe_group; } else if (wpa_s->assoc_status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { if (wpa_s->last_owe_group == 19) group = 20; else if (wpa_s->last_owe_group == 20) group = 21; else group = OWE_DH_GROUP; } else { group = OWE_DH_GROUP; } wpa_s->last_owe_group = group; wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group); owe_ie = owe_build_assoc_req(wpa_s->wpa, group); if (owe_ie && wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(owe_ie), wpabuf_len(owe_ie)); wpa_ie_len += wpabuf_len(owe_ie); } wpabuf_free(owe_ie); } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 if (DPP_VERSION > 1 && wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP && ssid->dpp_netaccesskey && ssid->dpp_pfs != 2 && !ssid->dpp_pfs_fallback) { struct rsn_pmksa_cache_entry *pmksa; pmksa = pmksa_cache_get_current(wpa_s->wpa); if (!pmksa || !pmksa->dpp_pfs) goto pfs_fail; dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len); if (!wpa_s->dpp_pfs) { wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); /* Try to continue without PFS */ goto pfs_fail; } if (wpabuf_len(wpa_s->dpp_pfs->ie) <= max_wpa_ie_len - wpa_ie_len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wpa_s->dpp_pfs->ie), wpabuf_len(wpa_s->dpp_pfs->ie)); wpa_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie); } } pfs_fail: #endif /* CONFIG_DPP2 */ #ifdef CONFIG_IEEE80211R /* * Add MDIE under these conditions: the network profile allows FT, * the AP supports FT, and the mobility domain ID matches. */ if (bss && wpa_key_mgmt_ft(wpa_sm_get_key_mgmt(wpa_s->wpa))) { const u8 *mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); if (mdie && mdie[1] >= MOBILITY_DOMAIN_ID_LEN) { size_t len = 0; const u8 *md = mdie + 2; const u8 *wpa_md = wpa_sm_get_ft_md(wpa_s->wpa); if (os_memcmp(md, wpa_md, MOBILITY_DOMAIN_ID_LEN) == 0) { /* Add mobility domain IE */ len = wpa_ft_add_mdie( wpa_s->wpa, wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len, mdie); wpa_ie_len += len; } #ifdef CONFIG_SME if (len > 0 && wpa_s->sme.ft_used && wpa_sm_has_ptk(wpa_s->wpa)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT over-the-air"); algs |= WPA_AUTH_ALG_FT; } #endif /* CONFIG_SME */ } } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->rsnxe_override_assoc && wpabuf_len(wpa_s->rsnxe_override_assoc) <= max_wpa_ie_len - wpa_ie_len) { wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override"); os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wpa_s->rsnxe_override_assoc), wpabuf_len(wpa_s->rsnxe_override_assoc)); wpa_ie_len += wpabuf_len(wpa_s->rsnxe_override_assoc); } else #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->rsnxe_len > 0 && wpa_s->rsnxe_len <= max_wpa_ie_len - wpa_ie_len) { os_memcpy(wpa_ie + wpa_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len); wpa_ie_len += wpa_s->rsnxe_len; } if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS) && wpa_s->robust_av.valid_config) { struct wpabuf *mscs_ie; size_t mscs_ie_len, buf_len; buf_len = 3 + /* MSCS descriptor IE header */ 1 + /* Request type */ 2 + /* User priority control */ 4 + /* Stream timeout */ 3 + /* TCLAS Mask IE header */ wpa_s->robust_av.frame_classifier_len; mscs_ie = wpabuf_alloc(buf_len); if (!mscs_ie) { wpa_printf(MSG_INFO, "MSCS: Failed to allocate MSCS IE"); goto mscs_fail; } wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie); if ((wpa_ie_len + wpabuf_len(mscs_ie)) <= max_wpa_ie_len) { wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie); mscs_ie_len = wpabuf_len(mscs_ie); os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(mscs_ie), mscs_ie_len); wpa_ie_len += mscs_ie_len; } wpabuf_free(mscs_ie); } mscs_fail: if (ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len, MULTI_AP_BACKHAUL_STA); if (multi_ap_ie_len == 0) { wpa_printf(MSG_ERROR, "Multi-AP: Failed to build Multi-AP IE"); os_free(wpa_ie); return NULL; } wpa_ie_len += multi_ap_ie_len; } params->wpa_ie = wpa_ie; params->wpa_ie_len = wpa_ie_len; params->auth_alg = algs; if (mask) *mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE; return wpa_ie; } #ifdef CONFIG_OWE static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s) { struct wpa_driver_associate_params params; u8 *wpa_ie; os_memset(¶ms, 0, sizeof(params)); wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, ¶ms, NULL); if (!wpa_ie) return; wpa_drv_update_connect_params(wpa_s, ¶ms, WPA_DRV_UPDATE_ASSOC_IES); os_free(wpa_ie); } #endif /* CONFIG_OWE */ #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) { struct wpa_driver_associate_params params; enum wpa_drv_update_connect_params_mask mask = 0; u8 *wpa_ie; if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN) return; /* nothing to do */ os_memset(¶ms, 0, sizeof(params)); wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, ¶ms, &mask); if (!wpa_ie) return; if (params.auth_alg == WPA_AUTH_ALG_FILS) { wpa_s->auth_alg = params.auth_alg; wpa_drv_update_connect_params(wpa_s, ¶ms, mask); } os_free(wpa_ie); } #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ static u8 wpa_ie_get_edmg_oper_chans(const u8 *edmg_ie) { if (!edmg_ie || edmg_ie[1] < 6) return 0; return edmg_ie[EDMG_BSS_OPERATING_CHANNELS_OFFSET]; } static u8 wpa_ie_get_edmg_oper_chan_width(const u8 *edmg_ie) { if (!edmg_ie || edmg_ie[1] < 6) return 0; return edmg_ie[EDMG_OPERATING_CHANNEL_WIDTH_OFFSET]; } /* Returns the intersection of two EDMG configurations. * Note: The current implementation is limited to CB2 only (CB1 included), * i.e., the implementation supports up to 2 contiguous channels. * For supporting non-contiguous (aggregated) channels and for supporting * CB3 and above, this function will need to be extended. */ static struct ieee80211_edmg_config get_edmg_intersection(struct ieee80211_edmg_config a, struct ieee80211_edmg_config b, u8 primary_channel) { struct ieee80211_edmg_config result; int i, contiguous = 0; int max_contiguous = 0; result.channels = b.channels & a.channels; if (!result.channels) { wpa_printf(MSG_DEBUG, "EDMG not possible: cannot intersect channels 0x%x and 0x%x", a.channels, b.channels); goto fail; } if (!(result.channels & BIT(primary_channel - 1))) { wpa_printf(MSG_DEBUG, "EDMG not possible: the primary channel %d is not one of the intersected channels 0x%x", primary_channel, result.channels); goto fail; } /* Find max contiguous channels */ for (i = 0; i < 6; i++) { if (result.channels & BIT(i)) contiguous++; else contiguous = 0; if (contiguous > max_contiguous) max_contiguous = contiguous; } /* Assuming AP and STA supports ONLY contiguous channels, * bw configuration can have value between 4-7. */ if ((b.bw_config < a.bw_config)) result.bw_config = b.bw_config; else result.bw_config = a.bw_config; if ((max_contiguous >= 2 && result.bw_config < EDMG_BW_CONFIG_5) || (max_contiguous >= 1 && result.bw_config < EDMG_BW_CONFIG_4)) { wpa_printf(MSG_DEBUG, "EDMG not possible: not enough contiguous channels %d for supporting CB1 or CB2", max_contiguous); goto fail; } return result; fail: result.channels = 0; result.bw_config = 0; return result; } static struct ieee80211_edmg_config get_supported_edmg(struct wpa_supplicant *wpa_s, struct hostapd_freq_params *freq, struct ieee80211_edmg_config request_edmg) { enum hostapd_hw_mode hw_mode; struct hostapd_hw_modes *mode = NULL; u8 primary_channel; if (!wpa_s->hw.modes) goto fail; hw_mode = ieee80211_freq_to_chan(freq->freq, &primary_channel); if (hw_mode == NUM_HOSTAPD_MODES) goto fail; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, hw_mode, false); if (!mode) goto fail; return get_edmg_intersection(mode->edmg, request_edmg, primary_channel); fail: request_edmg.channels = 0; request_edmg.bw_config = 0; return request_edmg; } #ifdef CONFIG_MBO void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s) { struct wpa_driver_associate_params params; u8 *wpa_ie; /* * Update MBO connect params only in case of change of MBO attributes * when connected, if the AP support MBO. */ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid || !wpa_s->current_bss || !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) return; os_memset(¶ms, 0, sizeof(params)); wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, ¶ms, NULL); if (!wpa_ie) return; wpa_drv_update_connect_params(wpa_s, ¶ms, WPA_DRV_UPDATE_ASSOC_IES); os_free(wpa_ie); } #endif /* CONFIG_MBO */ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) { struct wpa_connect_work *cwork = work->ctx; struct wpa_bss *bss = cwork->bss; struct wpa_ssid *ssid = cwork->ssid; struct wpa_supplicant *wpa_s = work->wpa_s; u8 *wpa_ie; const u8 *edmg_ie_oper; int use_crypt, ret, bssid_changed; unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt; struct wpa_driver_associate_params params; #if defined(CONFIG_WEP) || defined(IEEE8021X_EAPOL) int wep_keys_set = 0; #endif /* CONFIG_WEP || IEEE8021X_EAPOL */ int assoc_failed = 0; struct wpa_ssid *old_ssid; u8 prev_bssid[ETH_ALEN]; #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES struct ieee80211_vht_capabilities vhtcaps; struct ieee80211_vht_capabilities vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ if (deinit) { if (work->started) { wpa_s->connect_work = NULL; /* cancel possible auth. timeout */ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); } wpas_connect_work_free(cwork); return; } wpa_s->connect_work = work; if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) || wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); wpas_connect_work_done(wpa_s); return; } os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; wpa_s->eap_expected_failure = 0; /* Starting new association, so clear the possibly used WPA IE from the * previous association. */ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); wpa_s->rsnxe_len = 0; wpa_s->mscs_setup_done = false; wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL); if (!wpa_ie) { wpas_connect_work_done(wpa_s); return; } if (bss && (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) { #ifdef CONFIG_IEEE80211R const u8 *ie, *md = NULL; #endif /* CONFIG_IEEE80211R */ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); if (bssid_changed) wpas_notify_bssid_changed(wpa_s); #ifdef CONFIG_IEEE80211R ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) md = ie + 2; wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); if (md) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && wpa_s->conf->ap_scan == 2 && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Use ap_scan==1 style network selection to find the network */ wpas_connect_work_done(wpa_s); wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); os_free(wpa_ie); return; #endif /* CONFIG_WPS */ } else { wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); if (bss) os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); else os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } if (!wpa_s->pno) wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; cipher_group = wpa_s->group_cipher; cipher_group_mgmt = wpa_s->mgmt_group_cipher; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) use_crypt = 0; #ifdef CONFIG_WEP if (wpa_set_wep_keys(wpa_s, ssid)) { use_crypt = 1; wep_keys_set = 1; } #endif /* CONFIG_WEP */ } if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) use_crypt = 0; #ifdef IEEE8021X_EAPOL if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if ((ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 && !wep_keys_set) { use_crypt = 0; } else { /* Assume that dynamic WEP-104 keys will be used and * set cipher suites in order for drivers to expect * encryption. */ cipher_pairwise = cipher_group = WPA_CIPHER_WEP104; } } #endif /* IEEE8021X_EAPOL */ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* Set the key before (and later after) association */ wpa_supplicant_set_wpa_none_key(wpa_s, ssid); } wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); if (bss) { params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { wpa_printf(MSG_DEBUG, "Limit connection to BSSID " MACSTR " freq=%u MHz based on scan results " "(bssid_set=%d wps=%d)", MAC2STR(bss->bssid), bss->freq, ssid->bssid_set, wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); params.bssid = bss->bssid; params.freq.freq = bss->freq; } params.bssid_hint = bss->bssid; params.freq_hint = bss->freq; params.pbss = bss_is_pbss(bss); } else { if (ssid->bssid_hint_set) params.bssid_hint = ssid->bssid_hint; params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0; } if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set && wpa_s->conf->ap_scan == 2) { params.bssid = ssid->bssid; params.fixed_bssid = 1; } /* Initial frequency for IBSS/mesh */ if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) && ssid->frequency > 0 && params.freq.freq == 0) ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); if (ssid->mode == WPAS_MODE_IBSS) { params.fixed_freq = ssid->fixed_freq; if (ssid->beacon_int) params.beacon_int = ssid->beacon_int; else params.beacon_int = wpa_s->conf->beacon_int; } if (bss && ssid->enable_edmg) edmg_ie_oper = wpa_bss_get_ie_ext(bss, WLAN_EID_EXT_EDMG_OPERATION); else edmg_ie_oper = NULL; if (edmg_ie_oper) { params.freq.edmg.channels = wpa_ie_get_edmg_oper_chans(edmg_ie_oper); params.freq.edmg.bw_config = wpa_ie_get_edmg_oper_chan_width(edmg_ie_oper); wpa_printf(MSG_DEBUG, "AP supports EDMG channels 0x%x, bw_config %d", params.freq.edmg.channels, params.freq.edmg.bw_config); /* User may ask for specific EDMG channel for EDMG connection * (must be supported by AP) */ if (ssid->edmg_channel) { struct ieee80211_edmg_config configured_edmg; enum hostapd_hw_mode hw_mode; u8 primary_channel; hw_mode = ieee80211_freq_to_chan(bss->freq, &primary_channel); if (hw_mode == NUM_HOSTAPD_MODES) goto edmg_fail; hostapd_encode_edmg_chan(ssid->enable_edmg, ssid->edmg_channel, primary_channel, &configured_edmg); if (ieee802_edmg_is_allowed(params.freq.edmg, configured_edmg)) { params.freq.edmg = configured_edmg; wpa_printf(MSG_DEBUG, "Use EDMG channel %d for connection", ssid->edmg_channel); } else { edmg_fail: params.freq.edmg.channels = 0; params.freq.edmg.bw_config = 0; wpa_printf(MSG_WARNING, "EDMG channel %d not supported by AP, fallback to DMG", ssid->edmg_channel); } } if (params.freq.edmg.channels) { wpa_printf(MSG_DEBUG, "EDMG before: channels 0x%x, bw_config %d", params.freq.edmg.channels, params.freq.edmg.bw_config); params.freq.edmg = get_supported_edmg(wpa_s, ¶ms.freq, params.freq.edmg); wpa_printf(MSG_DEBUG, "EDMG after: channels 0x%x, bw_config %d", params.freq.edmg.channels, params.freq.edmg.bw_config); } } params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; params.mgmt_group_suite = cipher_group_mgmt; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; wpa_s->auth_alg = params.auth_alg; params.mode = ssid->mode; params.bg_scan_period = ssid->bg_scan_period; #ifdef CONFIG_WEP { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) params.wep_key[i] = ssid->wep_key[i]; params.wep_key_len[i] = ssid->wep_key_len[i]; } params.wep_tx_keyidx = ssid->wep_tx_keyidx; } #endif /* CONFIG_WEP */ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && (params.key_mgmt_suite == WPA_KEY_MGMT_PSK || params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { params.passphrase = ssid->passphrase; if (ssid->psk_set) params.psk = ssid->psk; } if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) && (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) params.req_handshake_offload = 1; if (wpa_s->conf->key_mgmt_offload) { if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) params.req_key_mgmt_offload = ssid->proactive_key_caching < 0 ? wpa_s->conf->okc : ssid->proactive_key_caching; else params.req_key_mgmt_offload = 1; if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK || params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) && ssid->psk_set) params.psk = ssid->psk; } params.drop_unencrypted = use_crypt; params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid); if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports " "MFP: require MFP"); params.mgmt_frame_protection = MGMT_FRAME_PROTECTION_REQUIRED; #ifdef CONFIG_OWE } else if (!rsn && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only) { params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION; #endif /* CONFIG_OWE */ } } params.p2p = ssid->p2p_group; if (wpa_s->p2pdev->set_sta_uapsd) params.uapsd = wpa_s->p2pdev->sta_uapsd; else params.uapsd = -1; #ifdef CONFIG_HT_OVERRIDES os_memset(&htcaps, 0, sizeof(htcaps)); os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); params.htcaps = (u8 *) &htcaps; params.htcaps_mask = (u8 *) &htcaps_mask; wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES os_memset(&vhtcaps, 0, sizeof(vhtcaps)); os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask)); params.vhtcaps = &vhtcaps; params.vhtcaps_mask = &vhtcaps_mask; wpa_supplicant_apply_vht_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_VHT_OVERRIDES */ #ifdef CONFIG_HE_OVERRIDES wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_HE_OVERRIDES */ #ifdef CONFIG_P2P /* * If multi-channel concurrency is not supported, check for any * frequency conflict. In case of any frequency conflict, remove the * least prioritized connection. */ if (wpa_s->num_multichan_concurrent < 2) { int freq, num; num = get_shared_radio_freqs(wpa_s, &freq, 1); if (num > 0 && freq > 0 && freq != params.freq.freq) { wpa_printf(MSG_DEBUG, "Assoc conflicting freq found (%d != %d)", freq, params.freq.freq); if (wpas_p2p_handle_frequency_conflicts( wpa_s, params.freq.freq, ssid) < 0) { wpas_connect_work_done(wpa_s); os_free(wpa_ie); return; } } } #endif /* CONFIG_P2P */ if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) && wpa_s->current_ssid) params.prev_bssid = prev_bssid; #ifdef CONFIG_SAE params.sae_pwe = wpa_s->conf->sae_pwe; #endif /* CONFIG_SAE */ ret = wpa_drv_associate(wpa_s, ¶ms); os_free(wpa_ie); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) { /* * The driver is known to mean what is saying, so we * can stop right here; the association will not * succeed. */ wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); return; } /* try to continue anyway; new association will be tried again * after timeout */ assoc_failed = 1; } if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* Set the key after the association just in case association * cleared the previously configured key. */ wpa_supplicant_set_wpa_none_key(wpa_s, ssid); /* No need to timeout authentication since there is no key * management. */ wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); #ifdef CONFIG_IBSS_RSN } else if (ssid->mode == WPAS_MODE_IBSS && wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) { /* * RSN IBSS authentication is per-STA and we can disable the * per-BSSID authentication. */ wpa_supplicant_cancel_auth_timeout(wpa_s); #endif /* CONFIG_IBSS_RSN */ } else { /* Timeout for IEEE 802.11 authentication and association */ int timeout = 60; if (assoc_failed) { /* give IBSS a bit more time */ timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5; } else if (wpa_s->conf->ap_scan == 1) { /* give IBSS a bit more time */ timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10; } wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } #ifdef CONFIG_WEP if (wep_keys_set && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, ssid); } #endif /* CONFIG_WEP */ if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) { /* * Do not allow EAP session resumption between different * network configurations. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) { wpa_s->current_bss = bss; #ifdef CONFIG_HS20 hs20_configure_frame_filters(wpa_s); #endif /* CONFIG_HS20 */ } wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); } static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, const u8 *addr) { struct wpa_ssid *old_ssid; wpas_connect_work_done(wpa_s); wpa_clear_keys(wpa_s, addr); old_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); } /** * wpa_supplicant_deauthenticate - Deauthenticate the current connection * @wpa_s: Pointer to wpa_supplicant data * @reason_code: IEEE 802.11 reason code for the deauthenticate frame * * This function is used to request %wpa_supplicant to deauthenticate from the * current AP. */ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason_code) { u8 *addr = NULL; union wpa_event_data event; int zero_addr = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR " pending_bssid=" MACSTR " reason=%d (%s) state=%s", MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), reason_code, reason2str(reason_code), wpa_supplicant_state_txt(wpa_s->wpa_state)); if (!is_zero_ether_addr(wpa_s->pending_bssid) && (wpa_s->wpa_state == WPA_AUTHENTICATING || wpa_s->wpa_state == WPA_ASSOCIATING)) addr = wpa_s->pending_bssid; else if (!is_zero_ether_addr(wpa_s->bssid)) addr = wpa_s->bssid; else if (wpa_s->wpa_state == WPA_ASSOCIATING) { /* * When using driver-based BSS selection, we may not know the * BSSID with which we are currently trying to associate. We * need to notify the driver of this disconnection even in such * a case, so use the all zeros address here. */ addr = wpa_s->bssid; zero_addr = 1; } if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) wpa_s->enabled_4addr_mode = 0; #ifdef CONFIG_TDLS wpa_tdls_teardown_peers(wpa_s->wpa); #endif /* CONFIG_TDLS */ #ifdef CONFIG_MESH if (wpa_s->ifmsh) { struct mesh_conf *mconf; mconf = wpa_s->ifmsh->mconf; wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", wpa_s->ifname); wpas_notify_mesh_group_removed(wpa_s, mconf->meshid, mconf->meshid_len, reason_code); wpa_supplicant_leave_mesh(wpa_s, true); } #endif /* CONFIG_MESH */ if (addr) { wpa_drv_deauthenticate(wpa_s, addr, reason_code); os_memset(&event, 0, sizeof(event)); event.deauth_info.reason_code = reason_code; event.deauth_info.locally_generated = 1; wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event); if (zero_addr) addr = NULL; } wpa_supplicant_clear_connection(wpa_s, addr); } void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s) { wpa_s->own_reconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); } static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { if (!ssid || !ssid->disabled || ssid->disabled == 2) return; ssid->disabled = 0; ssid->owe_transition_bss_select_count = 0; wpas_clear_temp_disabled(wpa_s, ssid, 1); wpas_notify_network_enabled_changed(wpa_s, ssid); /* * Try to reassociate since there is no current configuration and a new * network was made available. */ if (!wpa_s->current_ssid && !wpa_s->disconnected) wpa_s->reassociate = 1; } /** * wpa_supplicant_add_network - Add a new network * @wpa_s: wpa_supplicant structure for a network interface * Returns: The new network configuration or %NULL if operation failed * * This function performs the following operations: * 1. Adds a new network. * 2. Send network addition notification. * 3. Marks the network disabled. * 4. Set network default parameters. */ struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; ssid = wpa_config_add_network(wpa_s->conf); if (!ssid) return NULL; wpas_notify_network_added(wpa_s, ssid); ssid->disabled = 1; wpa_config_set_network_defaults(ssid); return ssid; } /** * wpa_supplicant_remove_network - Remove a configured network based on id * @wpa_s: wpa_supplicant structure for a network interface * @id: Unique network id to search for * Returns: 0 on success, or -1 if the network was not found, -2 if the network * could not be removed * * This function performs the following operations: * 1. Removes the network. * 2. Send network removal notification. * 3. Update internal state machines. * 4. Stop any running sched scans. */ int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id) { struct wpa_ssid *ssid; int was_disabled; ssid = wpa_config_get_network(wpa_s->conf, id); if (!ssid) return -1; wpas_notify_network_removed(wpa_s, ssid); if (wpa_s->last_ssid == ssid) wpa_s->last_ssid = NULL; if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ /* * Invalidate the EAP session cache if the current or * previously used network is removed. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } if (ssid == wpa_s->current_ssid) { wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } was_disabled = ssid->disabled; if (wpa_config_remove_network(wpa_s->conf, id) < 0) return -2; if (!was_disabled && wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove network from filters"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } return 0; } /** * wpa_supplicant_remove_all_networks - Remove all configured networks * @wpa_s: wpa_supplicant structure for a network interface * Returns: 0 on success (errors are currently ignored) * * This function performs the following operations: * 1. Remove all networks. * 2. Send network removal notifications. * 3. Update internal state machines. * 4. Stop any running sched scans. */ int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; if (wpa_s->sched_scanning) wpa_supplicant_cancel_sched_scan(wpa_s); eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } ssid = wpa_s->conf->ssid; while (ssid) { struct wpa_ssid *remove_ssid = ssid; int id; id = ssid->id; ssid = ssid->next; if (wpa_s->last_ssid == remove_ssid) wpa_s->last_ssid = NULL; wpas_notify_network_removed(wpa_s, remove_ssid); wpa_config_remove_network(wpa_s->conf, id); } return 0; } /** * wpa_supplicant_enable_network - Mark a configured network as enabled * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network or %NULL * * Enables the specified network or all networks if no network specified. */ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { if (ssid == NULL) { for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) wpa_supplicant_enable_one_network(wpa_s, ssid); } else wpa_supplicant_enable_one_network(wpa_s, ssid); if (wpa_s->reassociate && !wpa_s->disconnected && (!wpa_s->current_ssid || wpa_s->wpa_state == WPA_DISCONNECTED || wpa_s->wpa_state == WPA_SCANNING)) { if (wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add " "new network to scan filters"); wpa_supplicant_cancel_sched_scan(wpa_s); } if (wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } } } /** * wpa_supplicant_disable_network - Mark a configured network as disabled * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network or %NULL * * Disables the specified network or all networks if no network specified. */ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct wpa_ssid *other_ssid; int was_disabled; if (ssid == NULL) { if (wpa_s->sched_scanning) wpa_supplicant_cancel_sched_scan(wpa_s); for (other_ssid = wpa_s->conf->ssid; other_ssid; other_ssid = other_ssid->next) { was_disabled = other_ssid->disabled; if (was_disabled == 2) continue; /* do not change persistent P2P group * data */ other_ssid->disabled = 1; if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed( wpa_s, other_ssid); } if (wpa_s->current_ssid) { if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } } else if (ssid->disabled != 2) { if (ssid == wpa_s->current_ssid) { if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } was_disabled = ssid->disabled; ssid->disabled = 1; if (was_disabled != ssid->disabled) { wpas_notify_network_enabled_changed(wpa_s, ssid); if (wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan " "to remove network from filters"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } } } } /** * wpa_supplicant_select_network - Attempt association with a network * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network or %NULL for any network */ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct wpa_ssid *other_ssid; int disconnected = 0; if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { if (wpa_s->wpa_state >= WPA_AUTHENTICATING) wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); disconnected = 1; } if (ssid) wpas_clear_temp_disabled(wpa_s, ssid, 1); /* * Mark all other networks disabled or mark all networks enabled if no * network specified. */ for (other_ssid = wpa_s->conf->ssid; other_ssid; other_ssid = other_ssid->next) { int was_disabled = other_ssid->disabled; if (was_disabled == 2) continue; /* do not change persistent P2P group data */ other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0; if (was_disabled && !other_ssid->disabled) wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, other_ssid); } if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid && wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* We are already associated with the selected network */ wpa_printf(MSG_DEBUG, "Already associated with the " "selected network - do nothing"); return; } if (ssid) { wpa_s->current_ssid = ssid; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->connect_without_scan = (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL; /* * Don't optimize next scan freqs since a new ESS has been * selected. */ os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; } else { wpa_s->connect_without_scan = NULL; } wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s_clear_sae_rejected(wpa_s); wpa_s->last_owe_group = 0; if (ssid) { ssid->owe_transition_bss_select_count = 0; wpa_s_setup_sae_pt(wpa_s->conf, ssid); } if (wpa_s->connect_without_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_s->scan_req = NORMAL_SCAN_REQ; wpas_scan_reset_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); } if (ssid) wpas_notify_network_selected(wpa_s, ssid); } /** * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path * @wpa_s: wpa_supplicant structure for a network interface * @pkcs11_engine_path: PKCS #11 engine path or NULL * @pkcs11_module_path: PKCS #11 module path or NULL * Returns: 0 on success; -1 on failure * * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid * path. If resetting the EAPOL state machine with the new PKCS #11 engine and * module path fails the paths will be reset to the default value (NULL). */ int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, const char *pkcs11_engine_path, const char *pkcs11_module_path) { char *pkcs11_engine_path_copy = NULL; char *pkcs11_module_path_copy = NULL; if (pkcs11_engine_path != NULL) { pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path); if (pkcs11_engine_path_copy == NULL) return -1; } if (pkcs11_module_path != NULL) { pkcs11_module_path_copy = os_strdup(pkcs11_module_path); if (pkcs11_module_path_copy == NULL) { os_free(pkcs11_engine_path_copy); return -1; } } os_free(wpa_s->conf->pkcs11_engine_path); os_free(wpa_s->conf->pkcs11_module_path); wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy; wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy; wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; if (wpa_supplicant_init_eapol(wpa_s)) { /* Error -> Reset paths to the default value (NULL) once. */ if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL) wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL, NULL); return -1; } wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); return 0; } /** * wpa_supplicant_set_ap_scan - Set AP scan mode for interface * @wpa_s: wpa_supplicant structure for a network interface * @ap_scan: AP scan mode * Returns: 0 if succeed or -1 if ap_scan has an invalid value * */ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan) { int old_ap_scan; if (ap_scan < 0 || ap_scan > 2) return -1; if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) { wpa_printf(MSG_INFO, "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures"); } #ifdef ANDROID if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan && wpa_s->wpa_state >= WPA_ASSOCIATING && wpa_s->wpa_state < WPA_COMPLETED) { wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while " "associating", wpa_s->conf->ap_scan, ap_scan); return 0; } #endif /* ANDROID */ old_ap_scan = wpa_s->conf->ap_scan; wpa_s->conf->ap_scan = ap_scan; if (old_ap_scan != wpa_s->conf->ap_scan) wpas_notify_ap_scan_changed(wpa_s); return 0; } /** * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age * @wpa_s: wpa_supplicant structure for a network interface * @expire_age: Expiration age in seconds * Returns: 0 if succeed or -1 if expire_age has an invalid value * */ int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, unsigned int bss_expire_age) { if (bss_expire_age < 10) { wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u", bss_expire_age); return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec", bss_expire_age); wpa_s->conf->bss_expiration_age = bss_expire_age; return 0; } /** * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count * @wpa_s: wpa_supplicant structure for a network interface * @expire_count: number of scans after which an unseen BSS is reclaimed * Returns: 0 if succeed or -1 if expire_count has an invalid value * */ int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, unsigned int bss_expire_count) { if (bss_expire_count < 1) { wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u", bss_expire_count); return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u", bss_expire_count); wpa_s->conf->bss_expiration_scan_count = bss_expire_count; return 0; } /** * wpa_supplicant_set_scan_interval - Set scan interval * @wpa_s: wpa_supplicant structure for a network interface * @scan_interval: scan interval in seconds * Returns: 0 if succeed or -1 if scan_interval has an invalid value * */ int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, int scan_interval) { if (scan_interval < 0) { wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d", scan_interval); return -1; } wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec", scan_interval); wpa_supplicant_update_scan_int(wpa_s, scan_interval); return 0; } /** * wpa_supplicant_set_debug_params - Set global debug params * @global: wpa_global structure * @debug_level: debug level * @debug_timestamp: determines if show timestamp in debug data * @debug_show_keys: determines if show keys in debug data * Returns: 0 if succeed or -1 if debug_level has wrong value */ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int debug_timestamp, int debug_show_keys) { int old_level, old_timestamp, old_show_keys; /* check for allowed debuglevels */ if (debug_level != MSG_EXCESSIVE && debug_level != MSG_MSGDUMP && debug_level != MSG_DEBUG && debug_level != MSG_INFO && debug_level != MSG_WARNING && debug_level != MSG_ERROR) return -1; old_level = wpa_debug_level; old_timestamp = wpa_debug_timestamp; old_show_keys = wpa_debug_show_keys; wpa_debug_level = debug_level; wpa_debug_timestamp = debug_timestamp ? 1 : 0; wpa_debug_show_keys = debug_show_keys ? 1 : 0; if (wpa_debug_level != old_level) wpas_notify_debug_level_changed(global); if (wpa_debug_timestamp != old_timestamp) wpas_notify_debug_timestamp_changed(global); if (wpa_debug_show_keys != old_show_keys) wpas_notify_debug_show_keys_changed(global); return 0; } #ifdef CONFIG_OWE static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *entry_ssid, size_t entry_ssid_len) { const u8 *owe, *pos, *end; u8 ssid_len; struct wpa_bss *bss; /* Check network profile SSID aganst the SSID in the * OWE Transition Mode element. */ bss = wpa_bss_get_bssid_latest(wpa_s, bssid); if (!bss) return 0; owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (!owe) return 0; pos = owe + 6; end = owe + 2 + owe[1]; if (end - pos < ETH_ALEN + 1) return 0; pos += ETH_ALEN; ssid_len = *pos++; if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) return 0; return entry_ssid_len == ssid_len && os_memcmp(pos, entry_ssid, ssid_len) == 0; } #endif /* CONFIG_OWE */ /** * wpa_supplicant_get_ssid - Get a pointer to the current network structure * @wpa_s: Pointer to wpa_supplicant data * Returns: A pointer to the current network structure or %NULL on failure */ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) { struct wpa_ssid *entry; u8 ssid[SSID_MAX_LEN]; int res; size_t ssid_len; u8 bssid[ETH_ALEN]; int wired; res = wpa_drv_get_ssid(wpa_s, ssid); if (res < 0) { wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from " "driver"); return NULL; } ssid_len = res; if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from " "driver"); return NULL; } wired = wpa_s->conf->ap_scan == 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED); entry = wpa_s->conf->ssid; while (entry) { if (!wpas_network_disabled(wpa_s, entry) && ((ssid_len == entry->ssid_len && (!entry->ssid || os_memcmp(ssid, entry->ssid, ssid_len) == 0)) || wired) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #ifdef CONFIG_WPS if (!wpas_network_disabled(wpa_s, entry) && (entry->key_mgmt & WPA_KEY_MGMT_WPS) && (entry->ssid == NULL || entry->ssid_len == 0) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #endif /* CONFIG_WPS */ #ifdef CONFIG_OWE if (!wpas_network_disabled(wpa_s, entry) && owe_trans_ssid_match(wpa_s, bssid, entry->ssid, entry->ssid_len) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #endif /* CONFIG_OWE */ if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set && entry->ssid_len == 0 && os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0) return entry; entry = entry->next; } return NULL; } static int select_driver(struct wpa_supplicant *wpa_s, int i) { struct wpa_global *global = wpa_s->global; if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) { global->drv_priv[i] = wpa_drivers[i]->global_init(global); if (global->drv_priv[i] == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize driver " "'%s'", wpa_drivers[i]->name); return -1; } } wpa_s->driver = wpa_drivers[i]; wpa_s->global_drv_priv = global->drv_priv[i]; return 0; } static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, const char *name) { int i; size_t len; const char *pos, *driver = name; if (wpa_s == NULL) return -1; if (wpa_drivers[0] == NULL) { wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into " "wpa_supplicant"); return -1; } if (name == NULL) { /* default to first driver in the list */ return select_driver(wpa_s, 0); } do { pos = os_strchr(driver, ','); if (pos) len = pos - driver; else len = os_strlen(driver); for (i = 0; wpa_drivers[i]; i++) { if (os_strlen(wpa_drivers[i]->name) == len && os_strncmp(driver, wpa_drivers[i]->name, len) == 0) { /* First driver that succeeds wins */ if (select_driver(wpa_s, i) == 0) return 0; } } driver = pos + 1; } while (pos); wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name); return -1; } /** * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant * @ctx: Context pointer (wpa_s); this is the ctx variable registered * with struct wpa_driver_ops::init() * @src_addr: Source address of the EAPOL frame * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header) * @len: Length of the EAPOL data * * This function is called for each received EAPOL frame. Most driver * interfaces rely on more generic OS mechanism for receiving frames through * l2_packet, but if such a mechanism is not available, the driver wrapper may * take care of received EAPOL frames and deliver them to the core supplicant * code by calling this function. */ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_supplicant *wpa_s = ctx; wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); if (wpa_s->own_disconnect_req) { wpa_printf(MSG_DEBUG, "Drop received EAPOL frame as we are disconnecting"); return; } #ifdef CONFIG_TESTING_OPTIONS wpa_msg_ctrl(wpa_s, MSG_INFO, "EAPOL-RX " MACSTR " %zu", MAC2STR(src_addr), len); if (wpa_s->ignore_auth_resp) { wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!"); return; } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->wpa_state < WPA_ASSOCIATED || (wpa_s->last_eapol_matches_bssid && #ifdef CONFIG_AP !wpa_s->ap_iface && #endif /* CONFIG_AP */ os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) { /* * There is possible race condition between receiving the * association event and the EAPOL frame since they are coming * through different paths from the driver. In order to avoid * issues in trying to process the EAPOL frame before receiving * association information, lets queue it for processing until * the association event is received. This may also be needed in * driver-based roaming case, so also use src_addr != BSSID as a * trigger if we have previously confirmed that the * Authenticator uses BSSID as the src_addr (which is not the * case with wired IEEE 802.1X). */ wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing " "of received EAPOL frame (state=%s bssid=" MACSTR ")", wpa_supplicant_state_txt(wpa_s->wpa_state), MAC2STR(wpa_s->bssid)); wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); if (wpa_s->pending_eapol_rx) { os_get_reltime(&wpa_s->pending_eapol_rx_time); os_memcpy(wpa_s->pending_eapol_rx_src, src_addr, ETH_ALEN); } return; } wpa_s->last_eapol_matches_bssid = os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0; #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len); return; } #endif /* CONFIG_AP */ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since " "no key management is configured"); return; } if (wpa_s->eapol_received == 0 && (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) || !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_s->wpa_state != WPA_COMPLETED) && (wpa_s->current_ssid == NULL || wpa_s->current_ssid->mode != WPAS_MODE_IBSS)) { /* Timeout for completing IEEE 802.1X and WPA authentication */ int timeout = 10; if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { /* Use longer timeout for IEEE 802.1X/EAP */ timeout = 70; } #ifdef CONFIG_WPS if (wpa_s->current_ssid && wpa_s->current_bss && (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) && eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { /* * Use shorter timeout if going through WPS AP iteration * for PIN config method with an AP that does not * advertise Selected Registrar. */ struct wpabuf *wps_ie; wps_ie = wpa_bss_get_vendor_ie_multi( wpa_s->current_bss, WPS_IE_VENDOR_TYPE); if (wps_ie && !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) timeout = 10; wpabuf_free(wps_ie); } #endif /* CONFIG_WPS */ wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } wpa_s->eapol_received++; if (wpa_s->countermeasures) { wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped " "EAPOL packet"); return; } #ifdef CONFIG_IBSS_RSN if (wpa_s->current_ssid && wpa_s->current_ssid->mode == WPAS_MODE_IBSS) { ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len); return; } #endif /* CONFIG_IBSS_RSN */ /* Source address of the incoming EAPOL frame could be compared to the * current BSSID. However, it is possible that a centralized * Authenticator could be using another MAC address than the BSSID of * an AP, so just allow any address to be used for now. The replies are * still sent to the current BSSID (if available), though. */ os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) && wpa_s->key_mgmt != WPA_KEY_MGMT_OWE && wpa_s->key_mgmt != WPA_KEY_MGMT_DPP && eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0) return; wpa_drv_poll(wpa_s); if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len); else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { /* * Set portValid = true here since we are going to skip 4-way * handshake processing which would normally set portValid. We * need this to allow the EAPOL state machines to be completed * without going through EAPOL-Key handshake. */ eapol_sm_notify_portValid(wpa_s->eapol, true); } } static int wpas_eapol_needs_l2_packet(struct wpa_supplicant *wpa_s) { return !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_CONTROL_PORT) || !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX); } int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) { if ((!wpa_s->p2p_mgmt || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { l2_packet_deinit(wpa_s->l2); wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), ETH_P_EAPOL, wpas_eapol_needs_l2_packet(wpa_s) ? wpa_supplicant_rx_eapol : NULL, wpa_s, 0); if (wpa_s->l2 == NULL) return -1; if (l2_packet_set_packet_filter(wpa_s->l2, L2_PACKET_FILTER_PKTTYPE)) wpa_dbg(wpa_s, MSG_DEBUG, "Failed to attach pkt_type filter"); if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address"); return -1; } } else { const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); } wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); wpas_wps_update_mac_addr(wpa_s); #ifdef CONFIG_FST if (wpa_s->fst) fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr); #endif /* CONFIG_FST */ return 0; } static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_supplicant *wpa_s = ctx; const struct l2_ethhdr *eth; if (len < sizeof(*eth)) return; eth = (const struct l2_ethhdr *) buf; if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 && !(eth->h_dest[0] & 0x01)) { wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR " (bridge - not for this interface - ignore)", MAC2STR(src_addr), MAC2STR(eth->h_dest)); return; } wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR " (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest)); wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth), len - sizeof(*eth)); } int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s, const char *bridge_ifname) { if (wpa_s->wpa_state > WPA_SCANNING) return -EBUSY; if (bridge_ifname && os_strlen(bridge_ifname) >= sizeof(wpa_s->bridge_ifname)) return -EINVAL; if (!bridge_ifname) bridge_ifname = ""; if (os_strcmp(wpa_s->bridge_ifname, bridge_ifname) == 0) return 0; if (wpa_s->l2_br) { l2_packet_deinit(wpa_s->l2_br); wpa_s->l2_br = NULL; } os_strlcpy(wpa_s->bridge_ifname, bridge_ifname, sizeof(wpa_s->bridge_ifname)); if (wpa_s->bridge_ifname[0]) { wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge interface '%s'", wpa_s->bridge_ifname); wpa_s->l2_br = l2_packet_init_bridge( wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr, ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1); if (!wpa_s->l2_br) { wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet connection for the bridge interface '%s'", wpa_s->bridge_ifname); goto fail; } } #ifdef CONFIG_TDLS if (!wpa_s->p2p_mgmt && wpa_tdls_init(wpa_s->wpa)) goto fail; #endif /* CONFIG_TDLS */ return 0; fail: wpa_s->bridge_ifname[0] = 0; if (wpa_s->l2_br) { l2_packet_deinit(wpa_s->l2_br); wpa_s->l2_br = NULL; } #ifdef CONFIG_TDLS if (!wpa_s->p2p_mgmt) wpa_tdls_init(wpa_s->wpa); #endif /* CONFIG_TDLS */ return -EIO; } /** * wpa_supplicant_driver_init - Initialize driver interface parameters * @wpa_s: Pointer to wpa_supplicant data * Returns: 0 on success, -1 on failure * * This function is called to initialize driver interface parameters. * wpa_drv_init() must have been called before this function to initialize the * driver interface. */ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) { static int interface_count = 0; if (wpa_supplicant_update_mac_addr(wpa_s) < 0) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, MAC2STR(wpa_s->own_addr)); os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN); wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); if (wpa_s->bridge_ifname[0] && wpas_eapol_needs_l2_packet(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " "interface '%s'", wpa_s->bridge_ifname); wpa_s->l2_br = l2_packet_init_bridge( wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr, ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1); if (wpa_s->l2_br == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet " "connection for the bridge interface '%s'", wpa_s->bridge_ifname); return -1; } } if (wpa_s->conf->ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) { wpa_printf(MSG_INFO, "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures"); } wpa_clear_keys(wpa_s, NULL); /* Make sure that TKIP countermeasures are not left enabled (could * happen if wpa_supplicant is killed during countermeasures. */ wpa_drv_set_countermeasures(wpa_s, 0); wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver"); wpa_drv_flush_pmkid(wpa_s); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; wpa_s->prev_scan_wildcard = 0; if (wpa_supplicant_enabled_networks(wpa_s)) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); interface_count = 0; } #ifndef ANDROID if (!wpa_s->p2p_mgmt && wpa_supplicant_delayed_sched_scan(wpa_s, interface_count % 3, 100000)) wpa_supplicant_req_scan(wpa_s, interface_count % 3, 100000); #endif /* ANDROID */ interface_count++; } else wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return 0; } static int wpa_supplicant_daemon(const char *pid_file) { wpa_printf(MSG_DEBUG, "Daemonize.."); return os_daemonize(pid_file); } static struct wpa_supplicant * wpa_supplicant_alloc(struct wpa_supplicant *parent) { struct wpa_supplicant *wpa_s; wpa_s = os_zalloc(sizeof(*wpa_s)); if (wpa_s == NULL) return NULL; wpa_s->scan_req = INITIAL_SCAN_REQ; wpa_s->scan_interval = 5; wpa_s->new_connection = 1; wpa_s->parent = parent ? parent : wpa_s; wpa_s->p2pdev = wpa_s->parent; wpa_s->sched_scanning = 0; wpa_s->setband_mask = WPA_SETBAND_AUTO; dl_list_init(&wpa_s->bss_tmp_disallowed); dl_list_init(&wpa_s->fils_hlp_req); #ifdef CONFIG_TESTING_OPTIONS dl_list_init(&wpa_s->drv_signal_override); #endif /* CONFIG_TESTING_OPTIONS */ return wpa_s; } #ifdef CONFIG_HT_OVERRIDES static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, const char *ht_mcs) { /* parse ht_mcs into hex array */ int i; const char *tmp = ht_mcs; char *end = NULL; /* If ht_mcs is null, do not set anything */ if (!ht_mcs) return 0; /* This is what we are setting in the kernel */ os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN); wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs); for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { long v; errno = 0; v = strtol(tmp, &end, 16); if (errno == 0) { wpa_msg(wpa_s, MSG_DEBUG, "htcap value[%i]: %ld end: %p tmp: %p", i, v, end, tmp); if (end == tmp) break; htcaps->supported_mcs_set[i] = v; tmp = end; } else { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse ht-mcs: %s, error: %s\n", ht_mcs, strerror(errno)); return -1; } } /* * If we were able to parse any values, then set mask for the MCS set. */ if (i) { os_memset(&htcaps_mask->supported_mcs_set, 0xff, IEEE80211_HT_MCS_MASK_LEN - 1); /* skip the 3 reserved bits */ htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] = 0x1f; } return 0; } static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { le16 msk; if (disabled == -1) return 0; wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE); htcaps_mask->ht_capabilities_info |= msk; if (disabled) htcaps->ht_capabilities_info &= msk; else htcaps->ht_capabilities_info |= msk; return 0; } static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int factor) { if (factor == -1) return 0; wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); if (factor < 0 || factor > 3) { wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. " "Must be 0-3 or -1", factor); return -EINVAL; } htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */ htcaps->a_mpdu_params &= ~0x3; htcaps->a_mpdu_params |= factor & 0x3; return 0; } static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int density) { if (density == -1) return 0; wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); if (density < 0 || density > 7) { wpa_msg(wpa_s, MSG_ERROR, "ampdu_density: %d out of range. Must be 0-7 or -1.", density); return -EINVAL; } htcaps_mask->a_mpdu_params |= 0x1C; htcaps->a_mpdu_params &= ~(0x1C); htcaps->a_mpdu_params |= (density << 2) & 0x1C; return 0; } static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { if (disabled) wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); set_disable_ht40(htcaps, disabled); set_disable_ht40(htcaps_mask, 0); return 0; } static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { /* Masking these out disables SGI */ le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | HT_CAP_INFO_SHORT_GI40MHZ); if (disabled) wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); if (disabled) htcaps->ht_capabilities_info &= ~msk; else htcaps->ht_capabilities_info |= msk; htcaps_mask->ht_capabilities_info |= msk; return 0; } static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { /* Masking these out disables LDPC */ le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP); if (disabled) wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled); if (disabled) htcaps->ht_capabilities_info &= ~msk; else htcaps->ht_capabilities_info |= msk; htcaps_mask->ht_capabilities_info |= msk; return 0; } static int wpa_set_tx_stbc(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int tx_stbc) { le16 msk = host_to_le16(HT_CAP_INFO_TX_STBC); if (tx_stbc == -1) return 0; wpa_msg(wpa_s, MSG_DEBUG, "set_tx_stbc: %d", tx_stbc); if (tx_stbc < 0 || tx_stbc > 1) { wpa_msg(wpa_s, MSG_ERROR, "tx_stbc: %d out of range. Must be 0-1 or -1", tx_stbc); return -EINVAL; } htcaps_mask->ht_capabilities_info |= msk; htcaps->ht_capabilities_info &= ~msk; htcaps->ht_capabilities_info |= (tx_stbc << 7) & msk; return 0; } static int wpa_set_rx_stbc(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps, struct ieee80211_ht_capabilities *htcaps_mask, int rx_stbc) { le16 msk = host_to_le16(HT_CAP_INFO_RX_STBC_MASK); if (rx_stbc == -1) return 0; wpa_msg(wpa_s, MSG_DEBUG, "set_rx_stbc: %d", rx_stbc); if (rx_stbc < 0 || rx_stbc > 3) { wpa_msg(wpa_s, MSG_ERROR, "rx_stbc: %d out of range. Must be 0-3 or -1", rx_stbc); return -EINVAL; } htcaps_mask->ht_capabilities_info |= msk; htcaps->ht_capabilities_info &= ~msk; htcaps->ht_capabilities_info |= (rx_stbc << 8) & msk; return 0; } void wpa_supplicant_apply_ht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params) { struct ieee80211_ht_capabilities *htcaps; struct ieee80211_ht_capabilities *htcaps_mask; if (!ssid) return; params->disable_ht = ssid->disable_ht; if (!params->htcaps || !params->htcaps_mask) return; htcaps = (struct ieee80211_ht_capabilities *) params->htcaps; htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask; wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs); wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask, ssid->disable_max_amsdu); wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor); wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density); wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40); wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi); wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc); wpa_set_rx_stbc(wpa_s, htcaps, htcaps_mask, ssid->rx_stbc); wpa_set_tx_stbc(wpa_s, htcaps, htcaps_mask, ssid->tx_stbc); if (ssid->ht40_intolerant) { le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT); htcaps->ht_capabilities_info |= bit; htcaps_mask->ht_capabilities_info |= bit; } } #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES void wpa_supplicant_apply_vht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params) { struct ieee80211_vht_capabilities *vhtcaps; struct ieee80211_vht_capabilities *vhtcaps_mask; if (!ssid) return; params->disable_vht = ssid->disable_vht; vhtcaps = (void *) params->vhtcaps; vhtcaps_mask = (void *) params->vhtcaps_mask; if (!vhtcaps || !vhtcaps_mask) return; vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa); vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask); #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_sgi) { vhtcaps_mask->vht_capabilities_info |= (VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160); vhtcaps->vht_capabilities_info &= ~(VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160); wpa_msg(wpa_s, MSG_DEBUG, "disable-sgi override specified, vht-caps: 0x%x", vhtcaps->vht_capabilities_info); } /* if max ampdu is <= 3, we have to make the HT cap the same */ if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) { int max_ampdu; max_ampdu = (ssid->vht_capa & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >> VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT; max_ampdu = max_ampdu < 3 ? max_ampdu : 3; wpa_set_ampdu_factor(wpa_s, (void *) params->htcaps, (void *) params->htcaps_mask, max_ampdu); } #endif /* CONFIG_HT_OVERRIDES */ #define OVERRIDE_MCS(i) \ if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \ vhtcaps_mask->vht_supported_mcs_set.tx_map |= \ host_to_le16(3 << 2 * (i - 1)); \ vhtcaps->vht_supported_mcs_set.tx_map |= \ host_to_le16(ssid->vht_tx_mcs_nss_ ##i << \ 2 * (i - 1)); \ } \ if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \ vhtcaps_mask->vht_supported_mcs_set.rx_map |= \ host_to_le16(3 << 2 * (i - 1)); \ vhtcaps->vht_supported_mcs_set.rx_map |= \ host_to_le16(ssid->vht_rx_mcs_nss_ ##i << \ 2 * (i - 1)); \ } OVERRIDE_MCS(1); OVERRIDE_MCS(2); OVERRIDE_MCS(3); OVERRIDE_MCS(4); OVERRIDE_MCS(5); OVERRIDE_MCS(6); OVERRIDE_MCS(7); OVERRIDE_MCS(8); } #endif /* CONFIG_VHT_OVERRIDES */ #ifdef CONFIG_HE_OVERRIDES void wpa_supplicant_apply_he_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params) { if (!ssid) return; params->disable_he = ssid->disable_he; } #endif /* CONFIG_HE_OVERRIDES */ static int pcsc_reader_init(struct wpa_supplicant *wpa_s) { #ifdef PCSC_FUNCS size_t len; if (!wpa_s->conf->pcsc_reader) return 0; wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader); if (!wpa_s->scard) return 1; if (wpa_s->conf->pcsc_pin && scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) { scard_deinit(wpa_s->scard); wpa_s->scard = NULL; wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed"); return -1; } len = sizeof(wpa_s->imsi) - 1; if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) { scard_deinit(wpa_s->scard); wpa_s->scard = NULL; wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI"); return -1; } wpa_s->imsi[len] = '\0'; wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard); wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)", wpa_s->imsi, wpa_s->mnc_len); wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); #endif /* PCSC_FUNCS */ return 0; } int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) { char *val, *pos; ext_password_deinit(wpa_s->ext_pw); wpa_s->ext_pw = NULL; eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL); if (!wpa_s->conf->ext_password_backend) return 0; val = os_strdup(wpa_s->conf->ext_password_backend); if (val == NULL) return -1; pos = os_strchr(val, ':'); if (pos) *pos++ = '\0'; wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val); wpa_s->ext_pw = ext_password_init(val, pos); os_free(val); if (wpa_s->ext_pw == NULL) { wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend"); return -1; } eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw); return 0; } #ifdef CONFIG_FST static const u8 * wpas_fst_get_bssid_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; return (is_zero_ether_addr(wpa_s->bssid) || wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid; } static void wpas_fst_get_channel_info_cb(void *ctx, enum hostapd_hw_mode *hw_mode, u8 *channel) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->current_bss) { *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq, channel); } else if (wpa_s->hw.num_modes) { *hw_mode = wpa_s->hw.modes[0].mode; } else { WPA_ASSERT(0); *hw_mode = 0; } } static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes) { struct wpa_supplicant *wpa_s = ctx; *modes = wpa_s->hw.modes; return wpa_s->hw.num_modes; } static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies) { struct wpa_supplicant *wpa_s = ctx; wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies); wpa_s->fst_ies = fst_ies; } static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data) { struct wpa_supplicant *wpa_s = ctx; if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR, __func__, MAC2STR(wpa_s->bssid), MAC2STR(da)); return -1; } return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, wpabuf_head(data), wpabuf_len(data), 0); } static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr) { struct wpa_supplicant *wpa_s = ctx; WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0); return wpa_s->received_mb_ies; } static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr, const u8 *buf, size_t size) { struct wpa_supplicant *wpa_s = ctx; struct mb_ies_info info; WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0); if (!mb_ies_info_by_ies(&info, buf, size)) { wpabuf_free(wpa_s->received_mb_ies); wpa_s->received_mb_ies = mb_ies_by_info(&info); } } static const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, bool mb_only) { struct wpa_supplicant *wpa_s = ctx; *get_ctx = NULL; if (!is_zero_ether_addr(wpa_s->bssid)) return (wpa_s->received_mb_ies || !mb_only) ? wpa_s->bssid : NULL; return NULL; } static const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx, bool mb_only) { return NULL; } void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, struct fst_wpa_obj *iface_obj) { iface_obj->ctx = wpa_s; iface_obj->get_bssid = wpas_fst_get_bssid_cb; iface_obj->get_channel_info = wpas_fst_get_channel_info_cb; iface_obj->get_hw_modes = wpas_fst_get_hw_modes; iface_obj->set_ies = wpas_fst_set_ies_cb; iface_obj->send_action = wpas_fst_send_action_cb; iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb; iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb; iface_obj->get_peer_first = wpas_fst_get_peer_first; iface_obj->get_peer_next = wpas_fst_get_peer_next; } #endif /* CONFIG_FST */ static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, const struct wpa_driver_capa *capa) { struct wowlan_triggers *triggers; int ret = 0; if (!wpa_s->conf->wowlan_triggers) return 0; triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa); if (triggers) { ret = wpa_drv_wowlan(wpa_s, triggers); os_free(triggers); } return ret; } enum wpa_radio_work_band wpas_freq_to_band(int freq) { if (freq < 3000) return BAND_2_4_GHZ; if (freq > 50000) return BAND_60_GHZ; return BAND_5_GHZ; } unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs) { int i; unsigned int band = 0; if (freqs) { /* freqs are specified for the radio work */ for (i = 0; freqs[i]; i++) band |= wpas_freq_to_band(freqs[i]); } else { /* * freqs are not specified, implies all * the supported freqs by HW */ for (i = 0; i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].num_channels != 0) { if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211B || wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211G) band |= BAND_2_4_GHZ; else if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A) band |= BAND_5_GHZ; else if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211AD) band |= BAND_60_GHZ; else if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211ANY) band = BAND_2_4_GHZ | BAND_5_GHZ | BAND_60_GHZ; } } } return band; } static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s, const char *rn) { struct wpa_supplicant *iface = wpa_s->global->ifaces; struct wpa_radio *radio; while (rn && iface) { radio = iface->radio; if (radio && os_strcmp(rn, radio->name) == 0) { wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s", wpa_s->ifname, rn); dl_list_add(&radio->ifaces, &wpa_s->radio_list); return radio; } iface = iface->next; } wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s", wpa_s->ifname, rn ? rn : "N/A"); radio = os_zalloc(sizeof(*radio)); if (radio == NULL) return NULL; if (rn) os_strlcpy(radio->name, rn, sizeof(radio->name)); dl_list_init(&radio->ifaces); dl_list_init(&radio->work); dl_list_add(&radio->ifaces, &wpa_s->radio_list); return radio; } static void radio_work_free(struct wpa_radio_work *work) { if (work->wpa_s->scan_work == work) { /* This should not really happen. */ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work", work->type, work, work->started); work->wpa_s->scan_work = NULL; } #ifdef CONFIG_P2P if (work->wpa_s->p2p_scan_work == work) { /* This should not really happen. */ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work", work->type, work, work->started); work->wpa_s->p2p_scan_work = NULL; } #endif /* CONFIG_P2P */ if (work->started) { work->wpa_s->radio->num_active_works--; wpa_dbg(work->wpa_s, MSG_DEBUG, "radio_work_free('%s'@%p): num_active_works --> %u", work->type, work, work->wpa_s->radio->num_active_works); } dl_list_del(&work->list); os_free(work); } static int radio_work_is_connect(struct wpa_radio_work *work) { return os_strcmp(work->type, "sme-connect") == 0 || os_strcmp(work->type, "connect") == 0; } static int radio_work_is_scan(struct wpa_radio_work *work) { return os_strcmp(work->type, "scan") == 0 || os_strcmp(work->type, "p2p-scan") == 0; } static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) { struct wpa_radio_work *active_work = NULL; struct wpa_radio_work *tmp; /* Get the active work to know the type and band. */ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { if (tmp->started) { active_work = tmp; break; } } if (!active_work) { /* No active work, start one */ radio->num_active_works = 0; dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { if (os_strcmp(tmp->type, "scan") == 0 && external_scan_running(radio) && (((struct wpa_driver_scan_params *) tmp->ctx)->only_new_results || tmp->wpa_s->clear_driver_scan_cache)) continue; return tmp; } return NULL; } if (radio_work_is_connect(active_work)) { /* * If the active work is either connect or sme-connect, * do not parallelize them with other radio works. */ wpa_dbg(active_work->wpa_s, MSG_DEBUG, "Do not parallelize radio work with %s", active_work->type); return NULL; } dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { if (tmp->started) continue; /* * If connect or sme-connect are enqueued, parallelize only * those operations ahead of them in the queue. */ if (radio_work_is_connect(tmp)) break; /* Serialize parallel scan and p2p_scan operations on the same * interface since the driver_nl80211 mechanism for tracking * scan cookies does not yet have support for this. */ if (active_work->wpa_s == tmp->wpa_s && radio_work_is_scan(active_work) && radio_work_is_scan(tmp)) { wpa_dbg(active_work->wpa_s, MSG_DEBUG, "Do not start work '%s' when another work '%s' is already scheduled", tmp->type, active_work->type); continue; } /* * Check that the radio works are distinct and * on different bands. */ if (os_strcmp(active_work->type, tmp->type) != 0 && (active_work->bands != tmp->bands)) { /* * If a scan has to be scheduled through nl80211 scan * interface and if an external scan is already running, * do not schedule the scan since it is likely to get * rejected by kernel. */ if (os_strcmp(tmp->type, "scan") == 0 && external_scan_running(radio) && (((struct wpa_driver_scan_params *) tmp->ctx)->only_new_results || tmp->wpa_s->clear_driver_scan_cache)) continue; wpa_dbg(active_work->wpa_s, MSG_DEBUG, "active_work:%s new_work:%s", active_work->type, tmp->type); return tmp; } } /* Did not find a radio work to schedule in parallel. */ return NULL; } static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) { struct wpa_radio *radio = eloop_ctx; struct wpa_radio_work *work; struct os_reltime now, diff; struct wpa_supplicant *wpa_s; work = dl_list_first(&radio->work, struct wpa_radio_work, list); if (work == NULL) { radio->num_active_works = 0; return; } wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant, radio_list); if (!(wpa_s && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) { if (work->started) return; /* already started and still in progress */ if (wpa_s && external_scan_running(wpa_s->radio)) { wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); return; } } else { work = NULL; if (radio->num_active_works < MAX_ACTIVE_WORKS) { /* get the work to schedule next */ work = radio_work_get_next_work(radio); } if (!work) return; } wpa_s = work->wpa_s; os_get_reltime(&now); os_reltime_sub(&now, &work->time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait", work->type, work, diff.sec, diff.usec); work->started = 1; work->time = now; radio->num_active_works++; work->cb(work, 0); if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) && radio->num_active_works < MAX_ACTIVE_WORKS) radio_work_check_next(wpa_s); } /* * This function removes both started and pending radio works running on * the provided interface's radio. * Prior to the removal of the radio work, its callback (cb) is called with * deinit set to be 1. Each work's callback is responsible for clearing its * internal data and restoring to a correct state. * @wpa_s: wpa_supplicant data * @type: type of works to be removed * @remove_all: 1 to remove all the works on this radio, 0 to remove only * this interface's works. */ void radio_remove_works(struct wpa_supplicant *wpa_s, const char *type, int remove_all) { struct wpa_radio_work *work, *tmp; struct wpa_radio *radio = wpa_s->radio; dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work, list) { if (type && os_strcmp(type, work->type) != 0) continue; /* skip other ifaces' works */ if (!remove_all && work->wpa_s != wpa_s) continue; wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s", work->type, work, work->started ? " (started)" : ""); work->cb(work, 1); radio_work_free(work); } /* in case we removed the started work */ radio_work_check_next(wpa_s); } void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx) { struct wpa_radio_work *work; struct wpa_radio *radio = wpa_s->radio; dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { if (work->ctx != ctx) continue; wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s", work->type, work, work->started ? " (started)" : ""); radio_work_free(work); break; } } static void radio_remove_interface(struct wpa_supplicant *wpa_s) { struct wpa_radio *radio = wpa_s->radio; if (!radio) return; wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s", wpa_s->ifname, radio->name); dl_list_del(&wpa_s->radio_list); radio_remove_works(wpa_s, NULL, 0); /* If the interface that triggered the external scan was removed, the * external scan is no longer running. */ if (wpa_s == radio->external_scan_req_interface) radio->external_scan_req_interface = NULL; wpa_s->radio = NULL; if (!dl_list_empty(&radio->ifaces)) return; /* Interfaces remain for this radio */ wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name); eloop_cancel_timeout(radio_start_next_work, radio, NULL); os_free(radio); } void radio_work_check_next(struct wpa_supplicant *wpa_s) { struct wpa_radio *radio = wpa_s->radio; if (dl_list_empty(&radio->work)) return; if (wpa_s->ext_work_in_progress) { wpa_printf(MSG_DEBUG, "External radio work in progress - delay start of pending item"); return; } eloop_cancel_timeout(radio_start_next_work, radio, NULL); eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL); } /** * radio_add_work - Add a radio work item * @wpa_s: Pointer to wpa_supplicant data * @freq: Frequency of the offchannel operation in MHz or 0 * @type: Unique identifier for each type of work * @next: Force as the next work to be executed * @cb: Callback function for indicating when radio is available * @ctx: Context pointer for the work (work->ctx in cb()) * Returns: 0 on success, -1 on failure * * This function is used to request time for an operation that requires * exclusive radio control. Once the radio is available, the registered callback * function will be called. radio_work_done() must be called once the exclusive * radio operation has been completed, so that the radio is freed for other * operations. The special case of deinit=1 is used to free the context data * during interface removal. That does not allow the callback function to start * the radio operation, i.e., it must free any resources allocated for the radio * work and return. * * The @freq parameter can be used to indicate a single channel on which the * offchannel operation will occur. This may allow multiple radio work * operations to be performed in parallel if they apply for the same channel. * Setting this to 0 indicates that the work item may use multiple channels or * requires exclusive control of the radio. */ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, const char *type, int next, void (*cb)(struct wpa_radio_work *work, int deinit), void *ctx) { struct wpa_radio *radio = wpa_s->radio; struct wpa_radio_work *work; int was_empty; work = os_zalloc(sizeof(*work)); if (work == NULL) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work); os_get_reltime(&work->time); work->freq = freq; work->type = type; work->wpa_s = wpa_s; work->cb = cb; work->ctx = ctx; if (freq) work->bands = wpas_freq_to_band(freq); else if (os_strcmp(type, "scan") == 0 || os_strcmp(type, "p2p-scan") == 0) work->bands = wpas_get_bands(wpa_s, ((struct wpa_driver_scan_params *) ctx)->freqs); else work->bands = wpas_get_bands(wpa_s, NULL); was_empty = dl_list_empty(&wpa_s->radio->work); if (next) dl_list_add(&wpa_s->radio->work, &work->list); else dl_list_add_tail(&wpa_s->radio->work, &work->list); if (was_empty) { wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately"); radio_work_check_next(wpa_s); } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) && radio->num_active_works < MAX_ACTIVE_WORKS) { wpa_dbg(wpa_s, MSG_DEBUG, "Try to schedule a radio work (num_active_works=%u)", radio->num_active_works); radio_work_check_next(wpa_s); } return 0; } /** * radio_work_done - Indicate that a radio work item has been completed * @work: Completed work * * This function is called once the callback function registered with * radio_add_work() has completed its work. */ void radio_work_done(struct wpa_radio_work *work) { struct wpa_supplicant *wpa_s = work->wpa_s; struct os_reltime now, diff; unsigned int started = work->started; os_get_reltime(&now); os_reltime_sub(&now, &work->time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds", work->type, work, started ? "done" : "canceled", diff.sec, diff.usec); radio_work_free(work); if (started) radio_work_check_next(wpa_s); } struct wpa_radio_work * radio_work_pending(struct wpa_supplicant *wpa_s, const char *type) { struct wpa_radio_work *work; struct wpa_radio *radio = wpa_s->radio; dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0) return work; } return NULL; } static int wpas_init_driver(struct wpa_supplicant *wpa_s, const struct wpa_interface *iface) { const char *ifname, *driver, *rn; driver = iface->driver; next_driver: if (wpa_supplicant_set_driver(wpa_s, driver) < 0) return -1; wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); if (wpa_s->drv_priv == NULL) { const char *pos; int level = MSG_ERROR; pos = driver ? os_strchr(driver, ',') : NULL; if (pos) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " "driver interface - try next driver wrapper"); driver = pos + 1; goto next_driver; } #ifdef CONFIG_MATCH_IFACE if (wpa_s->matched == WPA_IFACE_MATCHED_NULL) level = MSG_DEBUG; #endif /* CONFIG_MATCH_IFACE */ wpa_msg(wpa_s, level, "Failed to initialize driver interface"); return -1; } if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected " "driver_param '%s'", wpa_s->conf->driver_param); return -1; } ifname = wpa_drv_get_ifname(wpa_s); if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced " "interface name with '%s'", ifname); os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); } rn = wpa_driver_get_radio_name(wpa_s); if (rn && rn[0] == '\0') rn = NULL; wpa_s->radio = radio_add_interface(wpa_s, rn); if (wpa_s->radio == NULL) return -1; return 0; } #ifdef CONFIG_GAS_SERVER static void wpas_gas_server_tx_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result) { wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR " result=%s", freq, MAC2STR(dst), result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : "FAILED")); gas_server_tx_status(wpa_s->gas_server, dst, data, data_len, result == OFFCHANNEL_SEND_ACTION_SUCCESS); } static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da, struct wpabuf *buf, unsigned int wait_time) { struct wpa_supplicant *wpa_s = ctx; const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (wait_time > wpa_s->max_remain_on_chan) wait_time = wpa_s->max_remain_on_chan; offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast, wpabuf_head(buf), wpabuf_len(buf), wait_time, wpas_gas_server_tx_status, 0); } #endif /* CONFIG_GAS_SERVER */ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, const struct wpa_interface *iface) { struct wpa_driver_capa capa; int capa_res; u8 dfs_domain; wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, iface->confname ? iface->confname : "N/A", iface->driver ? iface->driver : "default", iface->ctrl_interface ? iface->ctrl_interface : "N/A", iface->bridge_ifname ? iface->bridge_ifname : "N/A"); if (iface->confname) { #ifdef CONFIG_BACKEND_FILE wpa_s->confname = os_rel2abs_path(iface->confname); if (wpa_s->confname == NULL) { wpa_printf(MSG_ERROR, "Failed to get absolute path " "for configuration file '%s'.", iface->confname); return -1; } wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'", iface->confname, wpa_s->confname); #else /* CONFIG_BACKEND_FILE */ wpa_s->confname = os_strdup(iface->confname); #endif /* CONFIG_BACKEND_FILE */ wpa_s->conf = wpa_config_read(wpa_s->confname, NULL); if (wpa_s->conf == NULL) { wpa_printf(MSG_ERROR, "Failed to read or parse " "configuration '%s'.", wpa_s->confname); return -1; } wpa_s->confanother = os_rel2abs_path(iface->confanother); if (wpa_s->confanother && !wpa_config_read(wpa_s->confanother, wpa_s->conf)) { wpa_printf(MSG_ERROR, "Failed to read or parse configuration '%s'.", wpa_s->confanother); return -1; } /* * Override ctrl_interface and driver_param if set on command * line. */ if (iface->ctrl_interface) { os_free(wpa_s->conf->ctrl_interface); wpa_s->conf->ctrl_interface = os_strdup(iface->ctrl_interface); } if (iface->driver_param) { os_free(wpa_s->conf->driver_param); wpa_s->conf->driver_param = os_strdup(iface->driver_param); } if (iface->p2p_mgmt && !iface->ctrl_interface) { os_free(wpa_s->conf->ctrl_interface); wpa_s->conf->ctrl_interface = NULL; } } else wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, iface->driver_param); if (wpa_s->conf == NULL) { wpa_printf(MSG_ERROR, "\nNo configuration found."); return -1; } if (iface->ifname == NULL) { wpa_printf(MSG_ERROR, "\nInterface name is required."); return -1; } if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) { wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.", iface->ifname); return -1; } os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname)); #ifdef CONFIG_MATCH_IFACE wpa_s->matched = iface->matched; #endif /* CONFIG_MATCH_IFACE */ if (iface->bridge_ifname) { if (os_strlen(iface->bridge_ifname) >= sizeof(wpa_s->bridge_ifname)) { wpa_printf(MSG_ERROR, "\nToo long bridge interface " "name '%s'.", iface->bridge_ifname); return -1; } os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname, sizeof(wpa_s->bridge_ifname)); } /* RSNA Supplicant Key Management - INITIALIZE */ eapol_sm_notify_portEnabled(wpa_s->eapol, false); eapol_sm_notify_portValid(wpa_s->eapol, false); /* Initialize driver interface and register driver event handler before * L2 receive handler so that association events are processed before * EAPOL-Key packets if both become available for the same select() * call. */ if (wpas_init_driver(wpa_s, iface) < 0) return -1; if (wpa_supplicant_init_wpa(wpa_s) < 0) return -1; wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname : NULL); wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); if (wpa_s->conf->dot11RSNAConfigPMKLifetime && wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, wpa_s->conf->dot11RSNAConfigPMKLifetime)) { wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " "dot11RSNAConfigPMKLifetime"); return -1; } if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold && wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) { wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " "dot11RSNAConfigPMKReauthThreshold"); return -1; } if (wpa_s->conf->dot11RSNAConfigSATimeout && wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, wpa_s->conf->dot11RSNAConfigSATimeout)) { wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " "dot11RSNAConfigSATimeout"); return -1; } wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags, &dfs_domain); if (wpa_s->hw.modes) { u16 i; for (i = 0; i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].vht_capab) { wpa_s->hw_capab = CAPAB_VHT; break; } if (wpa_s->hw.modes[i].ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) wpa_s->hw_capab = CAPAB_HT40; else if (wpa_s->hw.modes[i].ht_capab && wpa_s->hw_capab == CAPAB_NO_HT_VHT) wpa_s->hw_capab = CAPAB_HT; } } capa_res = wpa_drv_get_capa(wpa_s, &capa); if (capa_res == 0) { wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; wpa_s->drv_flags2 = capa.flags2; wpa_s->drv_enc = capa.enc; wpa_s->drv_rrm_flags = capa.rrm_flags; wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans; wpa_s->max_sched_scan_plan_interval = capa.max_sched_scan_plan_interval; wpa_s->max_sched_scan_plan_iterations = capa.max_sched_scan_plan_iterations; wpa_s->sched_scan_supported = capa.sched_scan_supported; wpa_s->max_match_sets = capa.max_match_sets; wpa_s->max_remain_on_chan = capa.max_remain_on_chan; wpa_s->max_stations = capa.max_stations; wpa_s->extended_capa = capa.extended_capa; wpa_s->extended_capa_mask = capa.extended_capa_mask; wpa_s->extended_capa_len = capa.extended_capa_len; wpa_s->num_multichan_concurrent = capa.num_multichan_concurrent; wpa_s->wmm_ac_supported = capa.wmm_ac_supported; if (capa.mac_addr_rand_scan_supported) wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN; if (wpa_s->sched_scan_supported && capa.mac_addr_rand_sched_scan_supported) wpa_s->mac_addr_rand_supported |= (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO); wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); if (wpa_s->extended_capa && wpa_s->extended_capa_len >= 3 && wpa_s->extended_capa[2] & 0x40) wpa_s->multi_bss_support = 1; } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; /* * Only take p2p_mgmt parameters when P2P Device is supported. * Doing it here as it determines whether l2_packet_init() will be done * during wpa_supplicant_driver_init(). */ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) wpa_s->p2p_mgmt = iface->p2p_mgmt; if (wpa_s->num_multichan_concurrent == 0) wpa_s->num_multichan_concurrent = 1; if (wpa_supplicant_driver_init(wpa_s) < 0) return -1; #ifdef CONFIG_TDLS if (!iface->p2p_mgmt && wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ if (wpa_s->conf->country[0] && wpa_s->conf->country[1] && wpa_drv_set_country(wpa_s, wpa_s->conf->country)) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country"); return -1; } #ifdef CONFIG_FST if (wpa_s->conf->fst_group_id) { struct fst_iface_cfg cfg; struct fst_wpa_obj iface_obj; fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id, sizeof(cfg.group_id)); cfg.priority = wpa_s->conf->fst_priority; cfg.llt = wpa_s->conf->fst_llt; wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr, &iface_obj, &cfg); if (!wpa_s->fst) { wpa_msg(wpa_s, MSG_ERROR, "FST: Cannot attach iface %s to group %s", wpa_s->ifname, cfg.group_id); return -1; } } #endif /* CONFIG_FST */ if (wpas_wps_init(wpa_s)) return -1; #ifdef CONFIG_GAS_SERVER wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx); if (!wpa_s->gas_server) { wpa_printf(MSG_ERROR, "Failed to initialize GAS server"); return -1; } #endif /* CONFIG_GAS_SERVER */ #ifdef CONFIG_DPP if (wpas_dpp_init(wpa_s) < 0) return -1; #endif /* CONFIG_DPP */ if (wpa_supplicant_init_eapol(wpa_s) < 0) return -1; wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); if (wpa_s->ctrl_iface == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize control interface '%s'.\n" "You may have another wpa_supplicant process " "already running or the file was\n" "left by an unclean termination of wpa_supplicant " "in which case you will need\n" "to manually remove this file before starting " "wpa_supplicant again.\n", wpa_s->conf->ctrl_interface); return -1; } wpa_s->gas = gas_query_init(wpa_s); if (wpa_s->gas == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize GAS query"); return -1; } if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) || wpa_s->p2p_mgmt) && wpas_p2p_init(wpa_s->global, wpa_s) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P"); return -1; } if (wpa_bss_init(wpa_s) < 0) return -1; #ifdef CONFIG_PMKSA_CACHE_EXTERNAL #ifdef CONFIG_MESH dl_list_init(&wpa_s->mesh_external_pmksa_cache); #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ /* * Set Wake-on-WLAN triggers, if configured. * Note: We don't restore/remove the triggers on shutdown (it doesn't * have effect anyway when the interface is down). */ if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0) return -1; #ifdef CONFIG_EAP_PROXY { size_t len; wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1, wpa_s->imsi, &len); if (wpa_s->mnc_len > 0) { wpa_s->imsi[len] = '\0'; wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", wpa_s->imsi, wpa_s->mnc_len); } else { wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); } } #endif /* CONFIG_EAP_PROXY */ if (pcsc_reader_init(wpa_s) < 0) return -1; if (wpas_init_ext_pw(wpa_s) < 0) return -1; wpas_rrm_reset(wpa_s); wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); #ifdef CONFIG_HS20 hs20_init(wpa_s); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO if (!wpa_s->disable_mbo_oce && wpa_s->conf->oce) { if ((wpa_s->conf->oce & OCE_STA) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA)) wpa_s->enable_oce = OCE_STA; if ((wpa_s->conf->oce & OCE_STA_CFON) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) { /* TODO: Need to add STA-CFON support */ wpa_printf(MSG_ERROR, "OCE STA-CFON feature is not yet supported"); } } wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan); #endif /* CONFIG_MBO */ wpa_supplicant_set_default_scan_ies(wpa_s); return 0; } static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, int notify, int terminate) { struct wpa_global *global = wpa_s->global; struct wpa_supplicant *iface, *prev; if (wpa_s == wpa_s->parent) wpas_p2p_group_remove(wpa_s, "*"); iface = global->ifaces; while (iface) { if (iface->p2pdev == wpa_s) iface->p2pdev = iface->parent; if (iface == wpa_s || iface->parent != wpa_s) { iface = iface->next; continue; } wpa_printf(MSG_DEBUG, "Remove remaining child interface %s from parent %s", iface->ifname, wpa_s->ifname); prev = iface; iface = iface->next; wpa_supplicant_remove_iface(global, prev, terminate); } wpa_s->disconnected = 1; if (wpa_s->drv_priv) { /* * Don't deauthenticate if WoWLAN is enable and not explicitly * been configured to disconnect. */ if (!wpa_drv_get_wowlan(wpa_s) || wpa_s->conf->wowlan_disconnect_on_deinit) { wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_drv_set_countermeasures(wpa_s, 0); wpa_clear_keys(wpa_s, NULL); } else { wpa_msg(wpa_s, MSG_INFO, "Do not deauthenticate as part of interface deinit since WoWLAN is enabled"); } } wpa_supplicant_cleanup(wpa_s); wpas_p2p_deinit_iface(wpa_s); wpas_ctrl_radio_work_flush(wpa_s); radio_remove_interface(wpa_s); #ifdef CONFIG_FST if (wpa_s->fst) { fst_detach(wpa_s->fst); wpa_s->fst = NULL; } if (wpa_s->received_mb_ies) { wpabuf_free(wpa_s->received_mb_ies); wpa_s->received_mb_ies = NULL; } #endif /* CONFIG_FST */ if (wpa_s->drv_priv) wpa_drv_deinit(wpa_s); if (notify) wpas_notify_iface_removed(wpa_s); if (terminate) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING); wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface); wpa_s->ctrl_iface = NULL; #ifdef CONFIG_MESH if (wpa_s->ifmsh) { wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh, true); wpa_s->ifmsh = NULL; } #endif /* CONFIG_MESH */ if (wpa_s->conf != NULL) { wpa_config_free(wpa_s->conf); wpa_s->conf = NULL; } os_free(wpa_s->ssids_from_scan_req); os_free(wpa_s->last_scan_freqs); os_free(wpa_s); } #ifdef CONFIG_MATCH_IFACE /** * wpa_supplicant_match_iface - Match an interface description to a name * @global: Pointer to global data from wpa_supplicant_init() * @ifname: Name of the interface to match * Returns: Pointer to the created interface description or %NULL on failure */ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, const char *ifname) { int i; struct wpa_interface *iface, *miface; for (i = 0; i < global->params.match_iface_count; i++) { miface = &global->params.match_ifaces[i]; if (!miface->ifname || fnmatch(miface->ifname, ifname, 0) == 0) { iface = os_zalloc(sizeof(*iface)); if (!iface) return NULL; *iface = *miface; if (!miface->ifname) iface->matched = WPA_IFACE_MATCHED_NULL; else iface->matched = WPA_IFACE_MATCHED; iface->ifname = ifname; return iface; } } return NULL; } /** * wpa_supplicant_match_existing - Match existing interfaces * @global: Pointer to global data from wpa_supplicant_init() * Returns: 0 on success, -1 on failure */ static int wpa_supplicant_match_existing(struct wpa_global *global) { struct if_nameindex *ifi, *ifp; struct wpa_supplicant *wpa_s; struct wpa_interface *iface; ifp = if_nameindex(); if (!ifp) { wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno)); return -1; } for (ifi = ifp; ifi->if_name; ifi++) { wpa_s = wpa_supplicant_get_iface(global, ifi->if_name); if (wpa_s) continue; iface = wpa_supplicant_match_iface(global, ifi->if_name); if (iface) { wpa_supplicant_add_iface(global, iface, NULL); os_free(iface); } } if_freenameindex(ifp); return 0; } #endif /* CONFIG_MATCH_IFACE */ /** * wpa_supplicant_add_iface - Add a new network interface * @global: Pointer to global data from wpa_supplicant_init() * @iface: Interface configuration options * @parent: Parent interface or %NULL to assign new interface as parent * Returns: Pointer to the created interface or %NULL on failure * * This function is used to add new network interfaces for %wpa_supplicant. * This can be called before wpa_supplicant_run() to add interfaces before the * main event loop has been started. In addition, new interfaces can be added * dynamically while %wpa_supplicant is already running. This could happen, * e.g., when a hotplug network adapter is inserted. */ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface, struct wpa_supplicant *parent) { struct wpa_supplicant *wpa_s; struct wpa_interface t_iface; struct wpa_ssid *ssid; if (global == NULL || iface == NULL) return NULL; wpa_s = wpa_supplicant_alloc(parent); if (wpa_s == NULL) return NULL; wpa_s->global = global; t_iface = *iface; if (global->params.override_driver) { wpa_printf(MSG_DEBUG, "Override interface parameter: driver " "('%s' -> '%s')", iface->driver, global->params.override_driver); t_iface.driver = global->params.override_driver; } if (global->params.override_ctrl_interface) { wpa_printf(MSG_DEBUG, "Override interface parameter: " "ctrl_interface ('%s' -> '%s')", iface->ctrl_interface, global->params.override_ctrl_interface); t_iface.ctrl_interface = global->params.override_ctrl_interface; } if (wpa_supplicant_init_iface(wpa_s, &t_iface)) { wpa_printf(MSG_DEBUG, "Failed to add interface %s", iface->ifname); wpa_supplicant_deinit_iface(wpa_s, 0, 0); return NULL; } if (iface->p2p_mgmt == 0) { /* Notify the control interfaces about new iface */ if (wpas_notify_iface_added(wpa_s)) { wpa_supplicant_deinit_iface(wpa_s, 1, 0); return NULL; } for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) wpas_notify_network_added(wpa_s, ssid); } wpa_s->next = global->ifaces; global->ifaces = wpa_s; wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); #ifdef CONFIG_P2P if (wpa_s->global->p2p == NULL && !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && wpas_p2p_add_p2pdev_interface( wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { wpa_printf(MSG_INFO, "P2P: Failed to enable P2P Device interface"); /* Try to continue without. P2P will be disabled. */ } #endif /* CONFIG_P2P */ return wpa_s; } /** * wpa_supplicant_remove_iface - Remove a network interface * @global: Pointer to global data from wpa_supplicant_init() * @wpa_s: Pointer to the network interface to be removed * Returns: 0 if interface was removed, -1 if interface was not found * * This function can be used to dynamically remove network interfaces from * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In * addition, this function is used to remove all remaining interfaces when * %wpa_supplicant is terminated. */ int wpa_supplicant_remove_iface(struct wpa_global *global, struct wpa_supplicant *wpa_s, int terminate) { struct wpa_supplicant *prev; #ifdef CONFIG_MESH unsigned int mesh_if_created = wpa_s->mesh_if_created; char *ifname = NULL; struct wpa_supplicant *parent = wpa_s->parent; #endif /* CONFIG_MESH */ /* Remove interface from the global list of interfaces */ prev = global->ifaces; if (prev == wpa_s) { global->ifaces = wpa_s->next; } else { while (prev && prev->next != wpa_s) prev = prev->next; if (prev == NULL) return -1; prev->next = wpa_s->next; } wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname); #ifdef CONFIG_MESH if (mesh_if_created) { ifname = os_strdup(wpa_s->ifname); if (ifname == NULL) { wpa_dbg(wpa_s, MSG_ERROR, "mesh: Failed to malloc ifname"); return -1; } } #endif /* CONFIG_MESH */ if (global->p2p_group_formation == wpa_s) global->p2p_group_formation = NULL; if (global->p2p_invite_group == wpa_s) global->p2p_invite_group = NULL; wpa_supplicant_deinit_iface(wpa_s, 1, terminate); #ifdef CONFIG_MESH if (mesh_if_created) { wpa_drv_if_remove(parent, WPA_IF_MESH, ifname); os_free(ifname); } #endif /* CONFIG_MESH */ return 0; } /** * wpa_supplicant_get_eap_mode - Get the current EAP mode * @wpa_s: Pointer to the network interface * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found */ const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s) { const char *eapol_method; if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 && wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) { return "NO-EAP"; } eapol_method = eapol_sm_get_method_name(wpa_s->eapol); if (eapol_method == NULL) return "UNKNOWN-EAP"; return eapol_method; } /** * wpa_supplicant_get_iface - Get a new network interface * @global: Pointer to global data from wpa_supplicant_init() * @ifname: Interface name * Returns: Pointer to the interface or %NULL if not found */ struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, const char *ifname) { struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) return wpa_s; } return NULL; } #ifndef CONFIG_NO_WPA_MSG static const char * wpa_supplicant_msg_ifname_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s == NULL) return NULL; return wpa_s->ifname; } #endif /* CONFIG_NO_WPA_MSG */ #ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL #define WPA_SUPPLICANT_CLEANUP_INTERVAL 10 #endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */ /* Periodic cleanup tasks */ static void wpas_periodic(void *eloop_ctx, void *timeout_ctx) { struct wpa_global *global = eloop_ctx; struct wpa_supplicant *wpa_s; eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0, wpas_periodic, global, NULL); #ifdef CONFIG_P2P if (global->p2p) p2p_expire_peers(global->p2p); #endif /* CONFIG_P2P */ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); #ifdef CONFIG_AP ap_periodic(wpa_s); #endif /* CONFIG_AP */ } } /** * wpa_supplicant_init - Initialize %wpa_supplicant * @params: Parameters for %wpa_supplicant * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure * * This function is used to initialize %wpa_supplicant. After successful * initialization, the returned data pointer can be used to add and remove * network interfaces, and eventually, to deinitialize %wpa_supplicant. */ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) { struct wpa_global *global; int ret, i; if (params == NULL) return NULL; #ifdef CONFIG_DRIVER_NDIS { void driver_ndis_init_ops(void); driver_ndis_init_ops(); } #endif /* CONFIG_DRIVER_NDIS */ #ifndef CONFIG_NO_WPA_MSG wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb); #endif /* CONFIG_NO_WPA_MSG */ if (params->wpa_debug_file_path) wpa_debug_open_file(params->wpa_debug_file_path); if (!params->wpa_debug_file_path && !params->wpa_debug_syslog) wpa_debug_setup_stdout(); if (params->wpa_debug_syslog) wpa_debug_open_syslog(); if (params->wpa_debug_tracing) { ret = wpa_debug_open_linux_tracing(); if (ret) { wpa_printf(MSG_ERROR, "Failed to enable trace logging"); return NULL; } } ret = eap_register_methods(); if (ret) { wpa_printf(MSG_ERROR, "Failed to register EAP methods"); if (ret == -2) wpa_printf(MSG_ERROR, "Two or more EAP methods used " "the same EAP type."); return NULL; } global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; dl_list_init(&global->p2p_srv_bonjour); dl_list_init(&global->p2p_srv_upnp); global->params.daemonize = params->daemonize; global->params.wait_for_monitor = params->wait_for_monitor; global->params.dbus_ctrl_interface = params->dbus_ctrl_interface; if (params->pid_file) global->params.pid_file = os_strdup(params->pid_file); if (params->ctrl_interface) global->params.ctrl_interface = os_strdup(params->ctrl_interface); if (params->ctrl_interface_group) global->params.ctrl_interface_group = os_strdup(params->ctrl_interface_group); if (params->override_driver) global->params.override_driver = os_strdup(params->override_driver); if (params->override_ctrl_interface) global->params.override_ctrl_interface = os_strdup(params->override_ctrl_interface); #ifdef CONFIG_MATCH_IFACE global->params.match_iface_count = params->match_iface_count; if (params->match_iface_count) { global->params.match_ifaces = os_calloc(params->match_iface_count, sizeof(struct wpa_interface)); os_memcpy(global->params.match_ifaces, params->match_ifaces, params->match_iface_count * sizeof(struct wpa_interface)); } #endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_P2P if (params->conf_p2p_dev) global->params.conf_p2p_dev = os_strdup(params->conf_p2p_dev); #endif /* CONFIG_P2P */ wpa_debug_level = global->params.wpa_debug_level = params->wpa_debug_level; wpa_debug_show_keys = global->params.wpa_debug_show_keys = params->wpa_debug_show_keys; wpa_debug_timestamp = global->params.wpa_debug_timestamp = params->wpa_debug_timestamp; wpa_printf(MSG_DEBUG, "wpa_supplicant v%s", VERSION_STR); if (eloop_init()) { wpa_printf(MSG_ERROR, "Failed to initialize event loop"); wpa_supplicant_deinit(global); return NULL; } random_init(params->entropy_file); global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); if (global->ctrl_iface == NULL) { wpa_supplicant_deinit(global); return NULL; } if (wpas_notify_supplicant_initialized(global)) { wpa_supplicant_deinit(global); return NULL; } for (i = 0; wpa_drivers[i]; i++) global->drv_count++; if (global->drv_count == 0) { wpa_printf(MSG_ERROR, "No drivers enabled"); wpa_supplicant_deinit(global); return NULL; } global->drv_priv = os_calloc(global->drv_count, sizeof(void *)); if (global->drv_priv == NULL) { wpa_supplicant_deinit(global); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (wifi_display_init(global) < 0) { wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display"); wpa_supplicant_deinit(global); return NULL; } #endif /* CONFIG_WIFI_DISPLAY */ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0, wpas_periodic, global, NULL); return global; } /** * wpa_supplicant_run - Run the %wpa_supplicant main event loop * @global: Pointer to global data from wpa_supplicant_init() * Returns: 0 after successful event loop run, -1 on failure * * This function starts the main event loop and continues running as long as * there are any remaining events. In most cases, this function is running as * long as the %wpa_supplicant process in still in use. */ int wpa_supplicant_run(struct wpa_global *global) { struct wpa_supplicant *wpa_s; if (global->params.daemonize && (wpa_supplicant_daemon(global->params.pid_file) || eloop_sock_requeue())) return -1; #ifdef CONFIG_MATCH_IFACE if (wpa_supplicant_match_existing(global)) return -1; #endif if (global->params.wait_for_monitor) { for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt) wpa_supplicant_ctrl_iface_wait( wpa_s->ctrl_iface); } eloop_register_signal_terminate(wpa_supplicant_terminate, global); eloop_register_signal_reconfig(wpa_supplicant_reconfig, global); eloop_run(); return 0; } /** * wpa_supplicant_deinit - Deinitialize %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() * * This function is called to deinitialize %wpa_supplicant and to free all * allocated resources. Remaining network interfaces will also be removed. */ void wpa_supplicant_deinit(struct wpa_global *global) { int i; if (global == NULL) return; eloop_cancel_timeout(wpas_periodic, global, NULL); #ifdef CONFIG_WIFI_DISPLAY wifi_display_deinit(global); #endif /* CONFIG_WIFI_DISPLAY */ while (global->ifaces) wpa_supplicant_remove_iface(global, global->ifaces, 1); if (global->ctrl_iface) wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface); wpas_notify_supplicant_deinitialized(global); eap_peer_unregister_methods(); #ifdef CONFIG_AP eap_server_unregister_methods(); #endif /* CONFIG_AP */ for (i = 0; wpa_drivers[i] && global->drv_priv; i++) { if (!global->drv_priv[i]) continue; wpa_drivers[i]->global_deinit(global->drv_priv[i]); } os_free(global->drv_priv); random_deinit(); eloop_destroy(); if (global->params.pid_file) { os_daemonize_terminate(global->params.pid_file); os_free(global->params.pid_file); } os_free(global->params.ctrl_interface); os_free(global->params.ctrl_interface_group); os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); #ifdef CONFIG_MATCH_IFACE os_free(global->params.match_ifaces); #endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_P2P os_free(global->params.conf_p2p_dev); #endif /* CONFIG_P2P */ os_free(global->p2p_disallow_freq.range); os_free(global->p2p_go_avoid_freq.range); os_free(global->add_psk); os_free(global); wpa_debug_close_syslog(); wpa_debug_close_file(); wpa_debug_close_linux_tracing(); } void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) { if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && wpa_s->conf->country[0] && wpa_s->conf->country[1]) { char country[3]; country[0] = wpa_s->conf->country[0]; country[1] = wpa_s->conf->country[1]; country[2] = '\0'; if (wpa_drv_set_country(wpa_s, country) < 0) { wpa_printf(MSG_ERROR, "Failed to set country code " "'%s'", country); } } if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND) wpas_init_ext_pw(wpa_s); if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS) wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) { struct wpa_driver_capa capa; int res = wpa_drv_get_capa(wpa_s, &capa); if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0) wpa_printf(MSG_ERROR, "Failed to update wowlan_triggers to '%s'", wpa_s->conf->wowlan_triggers); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM) wpa_supplicant_set_default_scan_ies(wpa_s); #ifdef CONFIG_BGSCAN /* * We default to global bgscan parameters only when per-network bgscan * parameters aren't set. Only bother resetting bgscan parameters if * this is the case. */ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_BGSCAN) && wpa_s->current_ssid && !wpa_s->current_ssid->bgscan && wpa_s->wpa_state == WPA_COMPLETED) wpa_supplicant_reset_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ wpas_p2p_update_config(wpa_s); wpa_s->conf->changed_parameters = 0; } void add_freq(int *freqs, int *num_freqs, int freq) { int i; for (i = 0; i < *num_freqs; i++) { if (freqs[i] == freq) return; } freqs[*num_freqs] = freq; (*num_freqs)++; } static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss, *cbss; const int max_freqs = 10; int *freqs; int num_freqs = 0; freqs = os_calloc(max_freqs + 1, sizeof(int)); if (freqs == NULL) return NULL; cbss = wpa_s->current_bss; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (bss == cbss) continue; if (bss->ssid_len == cbss->ssid_len && os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 && !wpa_bssid_ignore_is_listed(wpa_s, bss->bssid)) { add_freq(freqs, &num_freqs, bss->freq); if (num_freqs == max_freqs) break; } } if (num_freqs == 0) { os_free(freqs); freqs = NULL; } return freqs; } void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) { int timeout; int count; int *freqs = NULL; wpas_connect_work_done(wpa_s); /* * Remove possible authentication timeout since the connection failed. */ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); /* * There is no point in ignoring the AP temporarily if this event is * generated based on local request to disconnect. */ if (wpa_s->own_disconnect_req || wpa_s->own_reconnect_req) { wpa_s->own_disconnect_req = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure due to local request to disconnect"); return; } if (wpa_s->disconnected) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure " "indication since interface has been put into " "disconnected state"); return; } /* * Add the failed BSSID into the ignore list and speed up next scan * attempt if there could be other APs that could accept association. */ count = wpa_bssid_ignore_add(wpa_s, bssid); if (count == 1 && wpa_s->current_bss) { /* * This BSS was not in the ignore list before. If there is * another BSS available for the same ESS, we should try that * next. Otherwise, we may as well try this one once more * before allowing other, likely worse, ESSes to be considered. */ freqs = get_bss_freqs_in_ess(wpa_s); if (freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS " "has been seen; try it next"); wpa_bssid_ignore_add(wpa_s, bssid); /* * On the next scan, go through only the known channels * used in this ESS based on previous scans to speed up * common load balancing use case. */ os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = freqs; } } wpa_s->consecutive_conn_failures++; if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) { wpa_printf(MSG_DEBUG, "Continuous association failures - " "consider temporary network disabling"); wpas_auth_failed(wpa_s, "CONN_FAILED"); } /* * Multiple consecutive connection failures mean that other APs are * either not available or have already been tried, so we can start * increasing the delay here to avoid constant scanning. */ switch (wpa_s->consecutive_conn_failures) { case 1: timeout = 100; break; case 2: timeout = 500; break; case 3: timeout = 1000; break; case 4: timeout = 5000; break; default: timeout = 10000; break; } wpa_dbg(wpa_s, MSG_DEBUG, "Consecutive connection failures: %d --> request scan in %d ms", wpa_s->consecutive_conn_failures, timeout); /* * TODO: if more than one possible AP is available in scan results, * could try the other ones before requesting a new scan. */ /* speed up the connection attempt with normal scan */ wpa_s->normal_scans = 0; wpa_supplicant_req_scan(wpa_s, timeout / 1000, 1000 * (timeout % 1000)); } #ifdef CONFIG_FILS + +void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + + /* Clear the PMKSA cache entry if FILS authentication was rejected. + * Check for ERP keys existing to limit when this can be done since + * the rejection response is not protected and such triggers should + * really not allow internal state to be modified unless required to + * avoid significant issues in functionality. In addition, drop + * externally configure PMKSA entries even without ERP keys since it + * is possible for an external component to add PMKSA entries for FILS + * authentication without restoring previously generated ERP keys. + * + * In this case, this is needed to allow recovery from cases where the + * AP or authentication server has dropped PMKSAs and ERP keys. */ + if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt)) + return; + + if (eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, + &username, &username_len, + &realm, &realm_len, &next_seq_num, + &rrk, &rrk_len) != 0 || + !realm) { + wpa_dbg(wpa_s, MSG_DEBUG, + "FILS: Drop external PMKSA cache entry"); + wpa_sm_aborted_external_cached(wpa_s->wpa); + wpa_sm_external_pmksa_cache_flush(wpa_s->wpa, ssid); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "FILS: Drop PMKSA cache entry"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); +} + + void fils_connection_failure(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; const u8 *realm, *username, *rrk; size_t realm_len, username_len, rrk_len; u16 next_seq_num; if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt) || eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username, &username_len, &realm, &realm_len, &next_seq_num, &rrk, &rrk_len) != 0 || !realm) return; wpa_hexdump_ascii(MSG_DEBUG, "FILS: Store last connection failure realm", realm, realm_len); os_free(wpa_s->last_con_fail_realm); wpa_s->last_con_fail_realm = os_malloc(realm_len); if (wpa_s->last_con_fail_realm) { wpa_s->last_con_fail_realm_len = realm_len; os_memcpy(wpa_s->last_con_fail_realm, realm, realm_len); } } #endif /* CONFIG_FILS */ int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s) { return wpa_s->conf->ap_scan == 2 || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION); } #if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW) int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const char *field, const char *value) { #ifdef IEEE8021X_EAPOL struct eap_peer_config *eap = &ssid->eap; wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field); wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value", (const u8 *) value, os_strlen(value)); switch (wpa_supplicant_ctrl_req_from_string(field)) { case WPA_CTRL_REQ_EAP_IDENTITY: os_free(eap->identity); eap->identity = (u8 *) os_strdup(value); eap->identity_len = os_strlen(value); eap->pending_req_identity = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_PASSWORD: bin_clear_free(eap->password, eap->password_len); eap->password = (u8 *) os_strdup(value); eap->password_len = os_strlen(value); eap->pending_req_password = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_NEW_PASSWORD: bin_clear_free(eap->new_password, eap->new_password_len); eap->new_password = (u8 *) os_strdup(value); eap->new_password_len = os_strlen(value); eap->pending_req_new_password = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_PIN: str_clear_free(eap->cert.pin); eap->cert.pin = os_strdup(value); eap->pending_req_pin = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_OTP: bin_clear_free(eap->otp, eap->otp_len); eap->otp = (u8 *) os_strdup(value); eap->otp_len = os_strlen(value); os_free(eap->pending_req_otp); eap->pending_req_otp = NULL; eap->pending_req_otp_len = 0; break; case WPA_CTRL_REQ_EAP_PASSPHRASE: str_clear_free(eap->cert.private_key_passwd); eap->cert.private_key_passwd = os_strdup(value); eap->pending_req_passphrase = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_SIM: str_clear_free(eap->external_sim_resp); eap->external_sim_resp = os_strdup(value); eap->pending_req_sim = 0; break; case WPA_CTRL_REQ_PSK_PASSPHRASE: if (wpa_config_set(ssid, "psk", value, 0) < 0) return -1; ssid->mem_only_psk = 1; if (ssid->passphrase) wpa_config_update_psk(ssid); if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning) wpa_supplicant_req_scan(wpa_s, 0, 0); break; case WPA_CTRL_REQ_EXT_CERT_CHECK: if (eap->pending_ext_cert_check != PENDING_CHECK) return -1; if (os_strcmp(value, "good") == 0) eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD; else if (os_strcmp(value, "bad") == 0) eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD; else return -1; break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; } return 0; #else /* IEEE8021X_EAPOL */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included"); return -1; #endif /* IEEE8021X_EAPOL */ } #endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { #ifdef CONFIG_WEP int i; unsigned int drv_enc; #endif /* CONFIG_WEP */ if (wpa_s->p2p_mgmt) return 1; /* no normal network profiles on p2p_mgmt interface */ if (ssid == NULL) return 1; if (ssid->disabled) return 1; #ifdef CONFIG_WEP if (wpa_s->drv_capa_known) drv_enc = wpa_s->drv_enc; else drv_enc = (unsigned int) -1; for (i = 0; i < NUM_WEP_KEYS; i++) { size_t len = ssid->wep_key_len[i]; if (len == 0) continue; if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40)) continue; if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104)) continue; if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128)) continue; return 1; /* invalid WEP key */ } #endif /* CONFIG_WEP */ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) && !ssid->mem_only_psk) return 1; return 0; } int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL && !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) { /* * Driver does not support BIP -- ignore pmf=1 default * since the connection with PMF would fail and the * configuration does not require PMF to be enabled. */ return NO_MGMT_FRAME_PROTECTION; } if (ssid && (ssid->key_mgmt & ~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS | WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) { /* * Do not use the default PMF value for non-RSN networks * since PMF is available only with RSN and pmf=2 * configuration would otherwise prevent connections to * all open networks. */ return NO_MGMT_FRAME_PROTECTION; } return wpa_s->conf->pmf; } return ssid->ieee80211w; } int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) { if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P) return 1; if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA) return 0; return -1; } void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason) { struct wpa_ssid *ssid = wpa_s->current_ssid; int dur; struct os_reltime now; if (ssid == NULL) { wpa_printf(MSG_DEBUG, "Authentication failure but no known " "SSID block"); return; } if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) return; ssid->auth_failures++; #ifdef CONFIG_P2P if (ssid->p2p_group && (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) { /* * Skip the wait time since there is a short timeout on the * connection to a P2P group. */ return; } #endif /* CONFIG_P2P */ if (ssid->auth_failures > 50) dur = 300; else if (ssid->auth_failures > 10) dur = 120; else if (ssid->auth_failures > 5) dur = 90; else if (ssid->auth_failures > 3) dur = 60; else if (ssid->auth_failures > 2) dur = 30; else if (ssid->auth_failures > 1) dur = 20; else dur = 10; if (ssid->auth_failures > 1 && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) dur += os_random() % (ssid->auth_failures * 10); os_get_reltime(&now); if (now.sec + dur <= ssid->disabled_until.sec) return; ssid->disabled_until.sec = now.sec + dur; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), ssid->auth_failures, dur, reason); } void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int clear_failures) { if (ssid == NULL) return; if (ssid->disabled_until.sec) { wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED "id=%d ssid=\"%s\"", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); } ssid->disabled_until.sec = 0; ssid->disabled_until.usec = 0; if (clear_failures) ssid->auth_failures = 0; } int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid) { size_t i; if (wpa_s->disallow_aps_bssid == NULL) return 0; for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) { if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN, bssid, ETH_ALEN) == 0) return 1; } return 0; } int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len) { size_t i; if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL) return 0; for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) { struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i]; if (ssid_len == s->ssid_len && os_memcmp(ssid, s->ssid, ssid_len) == 0) return 1; } return 0; } /** * wpas_request_connection - Request a new connection * @wpa_s: Pointer to the network interface * * This function is used to request a new connection to be found. It will mark * the interface to allow reassociation and request a new scan to find a * suitable network to connect to. */ void wpas_request_connection(struct wpa_supplicant *wpa_s) { wpa_s->normal_scans = 0; wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_reinit_autoscan(wpa_s); wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->last_owe_group = 0; if (wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); else wpa_s->reattach = 0; } /** * wpas_request_disconnection - Request disconnection * @wpa_s: Pointer to the network interface * * This function is used to request disconnection from the currently connected * network. This will stop any ongoing scans and initiate deauthentication. */ void wpas_request_disconnection(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_s->reassociate = 0; wpa_s->disconnected = 1; wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); radio_remove_works(wpa_s, "connect", 0); radio_remove_works(wpa_s, "sme-connect", 0); } void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, struct wpa_used_freq_data *freqs_data, unsigned int len) { unsigned int i; wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s", len, title); for (i = 0; i < len; i++) { struct wpa_used_freq_data *cur = &freqs_data[i]; wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X", i, cur->freq, cur->flags); } } /* * Find the operating frequencies of any of the virtual interfaces that * are using the same radio as the current interface, and in addition, get * information about the interface types that are using the frequency. */ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs_data, unsigned int len) { struct wpa_supplicant *ifs; u8 bssid[ETH_ALEN]; int freq; unsigned int idx = 0, i; wpa_dbg(wpa_s, MSG_DEBUG, "Determining shared radio frequencies (max len %u)", len); os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len); dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { if (idx == len) break; if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) continue; if (ifs->current_ssid->mode == WPAS_MODE_AP || ifs->current_ssid->mode == WPAS_MODE_P2P_GO || ifs->current_ssid->mode == WPAS_MODE_MESH) freq = ifs->current_ssid->frequency; else if (wpa_drv_get_bssid(ifs, bssid) == 0) freq = ifs->assoc_freq; else continue; /* Hold only distinct freqs */ for (i = 0; i < idx; i++) if (freqs_data[i].freq == freq) break; if (i == idx) freqs_data[idx++].freq = freq; if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { freqs_data[i].flags |= ifs->current_ssid->p2p_group ? WPA_FREQ_USED_BY_P2P_CLIENT : WPA_FREQ_USED_BY_INFRA_STATION; } } dump_freq_data(wpa_s, "completed iteration", freqs_data, idx); return idx; } /* * Find the operating frequencies of any of the virtual interfaces that * are using the same radio as the current interface. */ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, int *freq_array, unsigned int len) { struct wpa_used_freq_data *freqs_data; int num, i; os_memset(freq_array, 0, sizeof(int) * len); freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data)); if (!freqs_data) return -1; num = get_shared_radio_freqs_data(wpa_s, freqs_data, len); for (i = 0; i < num; i++) freq_array[i] = freqs_data[i].freq; os_free(freqs_data); return num; } struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) { switch (frame) { #ifdef CONFIG_P2P case VENDOR_ELEM_PROBE_REQ_P2P: case VENDOR_ELEM_PROBE_RESP_P2P: case VENDOR_ELEM_PROBE_RESP_P2P_GO: case VENDOR_ELEM_BEACON_P2P_GO: case VENDOR_ELEM_P2P_PD_REQ: case VENDOR_ELEM_P2P_PD_RESP: case VENDOR_ELEM_P2P_GO_NEG_REQ: case VENDOR_ELEM_P2P_GO_NEG_RESP: case VENDOR_ELEM_P2P_GO_NEG_CONF: case VENDOR_ELEM_P2P_INV_REQ: case VENDOR_ELEM_P2P_INV_RESP: case VENDOR_ELEM_P2P_ASSOC_REQ: case VENDOR_ELEM_P2P_ASSOC_RESP: return wpa_s->p2pdev; #endif /* CONFIG_P2P */ default: return wpa_s; } } void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s) { unsigned int i; char buf[30]; wpa_printf(MSG_DEBUG, "Update vendor elements"); for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { if (wpa_s->vendor_elem[i]) { int res; res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); if (!os_snprintf_error(sizeof(buf), res)) { wpa_hexdump_buf(MSG_DEBUG, buf, wpa_s->vendor_elem[i]); } } } #ifdef CONFIG_P2P if (wpa_s->parent == wpa_s && wpa_s->global->p2p && !wpa_s->global->p2p_disabled) p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); #endif /* CONFIG_P2P */ } int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, const u8 *elem, size_t len) { u8 *ie, *end; ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); for (; ie + 1 < end; ie += 2 + ie[1]) { if (ie + len > end) break; if (os_memcmp(ie, elem, len) != 0) continue; if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { wpabuf_free(wpa_s->vendor_elem[frame]); wpa_s->vendor_elem[frame] = NULL; } else { os_memmove(ie, ie + len, end - (ie + len)); wpa_s->vendor_elem[frame]->used -= len; } wpas_vendor_elem_update(wpa_s); return 0; } return -1; } struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, u16 num_modes, enum hostapd_hw_mode mode, bool is_6ghz) { u16 i; for (i = 0; i < num_modes; i++) { if (modes[i].mode != mode || !modes[i].num_channels || !modes[i].channels) continue; if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) || (is_6ghz && is_6ghz_freq(modes[i].channels[0].freq))) return &modes[i]; } return NULL; } struct hostapd_hw_modes * get_mode_with_freq(struct hostapd_hw_modes *modes, u16 num_modes, int freq) { int i, j; for (i = 0; i < num_modes; i++) { for (j = 0; j < modes[i].num_channels; j++) { if (freq == modes[i].channels[j].freq) return &modes[i]; } } return NULL; } static struct wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss_tmp_disallowed *bss; dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0) return bss; } return NULL; } static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s) { struct wpa_bss_tmp_disallowed *tmp; unsigned int num_bssid = 0; u8 *bssids; int ret; bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN); if (!bssids) return -1; dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid, ETH_ALEN); num_bssid++; } ret = wpa_drv_set_bssid_tmp_disallow(wpa_s, num_bssid, bssids); os_free(bssids); return ret; } static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx; /* Make sure the bss is not already freed */ dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { if (bss == tmp) { remove_bss_tmp_disallowed_entry(wpa_s, tmp); wpa_set_driver_tmp_disallow_list(wpa_s); break; } } } void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, unsigned int sec, int rssi_threshold) { struct wpa_bss_tmp_disallowed *bss; bss = wpas_get_disallowed_bss(wpa_s, bssid); if (bss) { eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); goto finish; } bss = os_malloc(sizeof(*bss)); if (!bss) { wpa_printf(MSG_DEBUG, "Failed to allocate memory for temp disallow BSS"); return; } os_memcpy(bss->bssid, bssid, ETH_ALEN); dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list); wpa_set_driver_tmp_disallow_list(wpa_s); finish: bss->rssi_threshold = rssi_threshold; eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, wpa_s, bss); } int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { struct wpa_bss_tmp_disallowed *disallowed = NULL, *tmp, *prev; dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { if (os_memcmp(bss->bssid, tmp->bssid, ETH_ALEN) == 0) { disallowed = tmp; break; } } if (!disallowed) return 0; if (disallowed->rssi_threshold != 0 && bss->level > disallowed->rssi_threshold) { remove_bss_tmp_disallowed_entry(wpa_s, disallowed); wpa_set_driver_tmp_disallow_list(wpa_s); return 0; } return 1; } int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s, unsigned int type, const u8 *addr, const u8 *mask) { if ((addr && !mask) || (!addr && mask)) { wpa_printf(MSG_INFO, "MAC_ADDR_RAND_SCAN invalid addr/mask combination"); return -1; } if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) { wpa_printf(MSG_INFO, "MAC_ADDR_RAND_SCAN cannot allow multicast address"); return -1; } if (type & MAC_ADDR_RAND_SCAN) { if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, addr, mask)) return -1; } if (type & MAC_ADDR_RAND_SCHED_SCAN) { if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, addr, mask)) return -1; if (wpa_s->sched_scanning && !wpa_s->pno) wpas_scan_restart_sched_scan(wpa_s); } if (type & MAC_ADDR_RAND_PNO) { if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, addr, mask)) return -1; if (wpa_s->pno) { wpas_stop_pno(wpa_s); wpas_start_pno(wpa_s); } } return 0; } int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s, unsigned int type) { wpas_mac_addr_rand_scan_clear(wpa_s, type); if (wpa_s->pno) { if (type & MAC_ADDR_RAND_PNO) { wpas_stop_pno(wpa_s); wpas_start_pno(wpa_s); } } else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) { wpas_scan_restart_sched_scan(wpa_s); } return 0; } int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s, struct wpa_signal_info *si) { int res; if (!wpa_s->driver->signal_poll) return -1; res = wpa_s->driver->signal_poll(wpa_s->drv_priv, si); #ifdef CONFIG_TESTING_OPTIONS if (res == 0) { struct driver_signal_override *dso; dl_list_for_each(dso, &wpa_s->drv_signal_override, struct driver_signal_override, list) { if (os_memcmp(wpa_s->bssid, dso->bssid, ETH_ALEN) != 0) continue; wpa_printf(MSG_DEBUG, "Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d", si->current_signal, dso->si_current_signal, si->avg_signal, dso->si_avg_signal, si->avg_beacon_signal, dso->si_avg_beacon_signal, si->current_noise, dso->si_current_noise); si->current_signal = dso->si_current_signal; si->avg_signal = dso->si_avg_signal; si->avg_beacon_signal = dso->si_avg_beacon_signal; si->current_noise = dso->si_current_noise; break; } } #endif /* CONFIG_TESTING_OPTIONS */ return res; } struct wpa_scan_results * wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s) { struct wpa_scan_results *scan_res; #ifdef CONFIG_TESTING_OPTIONS size_t idx; #endif /* CONFIG_TESTING_OPTIONS */ if (!wpa_s->driver->get_scan_results2) return NULL; scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv); #ifdef CONFIG_TESTING_OPTIONS for (idx = 0; scan_res && idx < scan_res->num; idx++) { struct driver_signal_override *dso; struct wpa_scan_res *res = scan_res->res[idx]; dl_list_for_each(dso, &wpa_s->drv_signal_override, struct driver_signal_override, list) { if (os_memcmp(res->bssid, dso->bssid, ETH_ALEN) != 0) continue; wpa_printf(MSG_DEBUG, "Override driver scan signal level %d->%d for " MACSTR, res->level, dso->scan_level, MAC2STR(res->bssid)); res->flags |= WPA_SCAN_QUAL_INVALID; if (dso->scan_level < 0) res->flags |= WPA_SCAN_LEVEL_DBM; else res->flags &= ~WPA_SCAN_LEVEL_DBM; res->level = dso->scan_level; break; } } #endif /* CONFIG_TESTING_OPTIONS */ return scan_res; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 6877f5a9960d..60acb53c5c38 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1,1749 +1,1750 @@ /* * wpa_supplicant - Internal definitions * Copyright (c) 2003-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #ifndef WPA_SUPPLICANT_I_H #define WPA_SUPPLICANT_I_H #include "utils/bitfield.h" #include "utils/list.h" #include "common/defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" #include "crypto/sha384.h" #include "eapol_supp/eapol_supp_sm.h" #include "wps/wps_defs.h" #include "config_ssid.h" #include "wmm_ac.h" extern const char *const wpa_supplicant_version; extern const char *const wpa_supplicant_license; #ifndef CONFIG_NO_STDOUT_DEBUG extern const char *const wpa_supplicant_full_license1; extern const char *const wpa_supplicant_full_license2; extern const char *const wpa_supplicant_full_license3; extern const char *const wpa_supplicant_full_license4; extern const char *const wpa_supplicant_full_license5; #endif /* CONFIG_NO_STDOUT_DEBUG */ struct wpa_sm; struct wpa_supplicant; struct ibss_rsn; struct scan_info; struct wpa_bss; struct wpa_scan_results; struct hostapd_hw_modes; struct wpa_driver_associate_params; /* * Forward declarations of private structures used within the ctrl_iface * backends. Other parts of wpa_supplicant do not have access to data stored in * these structures. */ struct ctrl_iface_priv; struct ctrl_iface_global_priv; struct wpas_dbus_priv; struct wpas_binder_priv; /** * struct wpa_interface - Parameters for wpa_supplicant_add_iface() */ struct wpa_interface { /** * confname - Configuration name (file or profile) name * * This can also be %NULL when a configuration file is not used. In * that case, ctrl_interface must be set to allow the interface to be * configured. */ const char *confname; /** * confanother - Additional configuration name (file or profile) name * * This can also be %NULL when the additional configuration file is not * used. */ const char *confanother; /** * ctrl_interface - Control interface parameter * * If a configuration file is not used, this variable can be used to * set the ctrl_interface parameter that would have otherwise been read * from the configuration file. If both confname and ctrl_interface are * set, ctrl_interface is used to override the value from configuration * file. */ const char *ctrl_interface; /** * driver - Driver interface name, or %NULL to use the default driver */ const char *driver; /** * driver_param - Driver interface parameters * * If a configuration file is not used, this variable can be used to * set the driver_param parameters that would have otherwise been read * from the configuration file. If both confname and driver_param are * set, driver_param is used to override the value from configuration * file. */ const char *driver_param; /** * ifname - Interface name */ const char *ifname; /** * bridge_ifname - Optional bridge interface name * * If the driver interface (ifname) is included in a Linux bridge * device, the bridge interface may need to be used for receiving EAPOL * frames. This can be enabled by setting this variable to enable * receiving of EAPOL frames from an additional interface. */ const char *bridge_ifname; /** * p2p_mgmt - Interface used for P2P management (P2P Device operations) * * Indicates whether wpas_p2p_init() must be called for this interface. * This is used only when the driver supports a dedicated P2P Device * interface that is not a network interface. */ int p2p_mgmt; #ifdef CONFIG_MATCH_IFACE /** * matched - Interface was matched rather than specified * */ enum { WPA_IFACE_NOT_MATCHED, WPA_IFACE_MATCHED_NULL, WPA_IFACE_MATCHED } matched; #endif /* CONFIG_MATCH_IFACE */ }; /** * struct wpa_params - Parameters for wpa_supplicant_init() */ struct wpa_params { /** * daemonize - Run %wpa_supplicant in the background */ int daemonize; /** * wait_for_monitor - Wait for a monitor program before starting */ int wait_for_monitor; /** * pid_file - Path to a PID (process ID) file * * If this and daemonize are set, process ID of the background process * will be written to the specified file. */ char *pid_file; /** * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO) */ int wpa_debug_level; /** * wpa_debug_show_keys - Whether keying material is included in debug * * This parameter can be used to allow keying material to be included * in debug messages. This is a security risk and this option should * not be enabled in normal configuration. If needed during * development or while troubleshooting, this option can provide more * details for figuring out what is happening. */ int wpa_debug_show_keys; /** * wpa_debug_timestamp - Whether to include timestamp in debug messages */ int wpa_debug_timestamp; /** * ctrl_interface - Global ctrl_iface path/parameter */ char *ctrl_interface; /** * ctrl_interface_group - Global ctrl_iface group */ char *ctrl_interface_group; /** * dbus_ctrl_interface - Enable the DBus control interface */ int dbus_ctrl_interface; /** * wpa_debug_file_path - Path of debug file or %NULL to use stdout */ const char *wpa_debug_file_path; /** * wpa_debug_syslog - Enable log output through syslog */ int wpa_debug_syslog; /** * wpa_debug_tracing - Enable log output through Linux tracing */ int wpa_debug_tracing; /** * override_driver - Optional driver parameter override * * This parameter can be used to override the driver parameter in * dynamic interface addition to force a specific driver wrapper to be * used instead. */ char *override_driver; /** * override_ctrl_interface - Optional ctrl_interface override * * This parameter can be used to override the ctrl_interface parameter * in dynamic interface addition to force a control interface to be * created. */ char *override_ctrl_interface; /** * entropy_file - Optional entropy file * * This parameter can be used to configure wpa_supplicant to maintain * its internal entropy store over restarts. */ char *entropy_file; #ifdef CONFIG_P2P /** * conf_p2p_dev - Configuration file used to hold the * P2P Device configuration parameters. * * This can also be %NULL. In such a case, if a P2P Device dedicated * interfaces is created, the main configuration file will be used. */ char *conf_p2p_dev; #endif /* CONFIG_P2P */ #ifdef CONFIG_MATCH_IFACE /** * match_ifaces - Interface descriptions to match */ struct wpa_interface *match_ifaces; /** * match_iface_count - Number of defined matching interfaces */ int match_iface_count; #endif /* CONFIG_MATCH_IFACE */ }; struct p2p_srv_bonjour { struct dl_list list; struct wpabuf *query; struct wpabuf *resp; }; struct p2p_srv_upnp { struct dl_list list; u8 version; char *service; }; /** * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces * * This structure is initialized by calling wpa_supplicant_init() when starting * %wpa_supplicant. */ struct wpa_global { struct wpa_supplicant *ifaces; struct wpa_params params; struct ctrl_iface_global_priv *ctrl_iface; struct wpas_dbus_priv *dbus; struct wpas_binder_priv *binder; void **drv_priv; size_t drv_count; struct os_time suspend_time; struct p2p_data *p2p; struct wpa_supplicant *p2p_init_wpa_s; struct wpa_supplicant *p2p_group_formation; struct wpa_supplicant *p2p_invite_group; u8 p2p_dev_addr[ETH_ALEN]; struct os_reltime p2p_go_wait_client; struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */ int p2p_disabled; int cross_connection; int p2p_long_listen; /* remaining time in long Listen state in ms */ struct wpa_freq_range_list p2p_disallow_freq; struct wpa_freq_range_list p2p_go_avoid_freq; enum wpa_conc_pref { WPA_CONC_PREF_NOT_SET, WPA_CONC_PREF_STA, WPA_CONC_PREF_P2P } conc_pref; unsigned int p2p_per_sta_psk:1; unsigned int p2p_fail_on_wps_complete:1; unsigned int p2p_24ghz_social_channels:1; unsigned int pending_p2ps_group:1; unsigned int pending_group_iface_for_p2ps:1; unsigned int pending_p2ps_group_freq; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; #define MAX_WFD_SUBELEMS 12 struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; #endif /* CONFIG_WIFI_DISPLAY */ struct psk_list_entry *add_psk; /* From group formation */ }; /** * struct wpa_radio - Internal data for per-radio information * * This structure is used to share data about configured interfaces * (struct wpa_supplicant) that share the same physical radio, e.g., to allow * better coordination of offchannel operations. */ struct wpa_radio { char name[16]; /* from driver_ops get_radio_name() or empty if not * available */ /** NULL if no external scan running. */ struct wpa_supplicant *external_scan_req_interface; unsigned int num_active_works; struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ struct dl_list work; /* struct wpa_radio_work::list entries */ }; /** * Checks whether an external scan is running on a given radio. * @radio: Pointer to radio struct * Returns: true if an external scan is running, false otherwise. */ static inline bool external_scan_running(struct wpa_radio *radio) { return radio && radio->external_scan_req_interface; } #define MAX_ACTIVE_WORKS 2 /** * struct wpa_radio_work - Radio work item */ struct wpa_radio_work { struct dl_list list; unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */ const char *type; struct wpa_supplicant *wpa_s; void (*cb)(struct wpa_radio_work *work, int deinit); void *ctx; unsigned int started:1; struct os_reltime time; unsigned int bands; }; int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, const char *type, int next, void (*cb)(struct wpa_radio_work *work, int deinit), void *ctx); void radio_work_done(struct wpa_radio_work *work); void radio_remove_works(struct wpa_supplicant *wpa_s, const char *type, int remove_all); void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx); void radio_work_check_next(struct wpa_supplicant *wpa_s); struct wpa_radio_work * radio_work_pending(struct wpa_supplicant *wpa_s, const char *type); struct wpa_connect_work { unsigned int sme:1; unsigned int bss_removed:1; struct wpa_bss *bss; struct wpa_ssid *ssid; }; int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, struct wpa_ssid *test_ssid); void wpas_connect_work_free(struct wpa_connect_work *cwork); void wpas_connect_work_done(struct wpa_supplicant *wpa_s); struct wpa_external_work { unsigned int id; char type[100]; unsigned int timeout; }; enum wpa_radio_work_band wpas_freq_to_band(int freq); unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs); /** * offchannel_send_action_result - Result of offchannel send Action frame */ enum offchannel_send_action_result { OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */, OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged */, OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure */ }; struct wps_ap_info { u8 bssid[ETH_ALEN]; enum wps_ap_info_type { WPS_AP_NOT_SEL_REG, WPS_AP_SEL_REG, WPS_AP_SEL_REG_OUR } type; unsigned int tries; struct os_reltime last_attempt; unsigned int pbc_active; u8 uuid[WPS_UUID_LEN]; }; #define WPA_FREQ_USED_BY_INFRA_STATION BIT(0) #define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1) struct wpa_used_freq_data { int freq; unsigned int flags; }; #define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */ /* * struct rrm_data - Data used for managing RRM features */ struct rrm_data { /* rrm_used - indication regarding the current connection */ unsigned int rrm_used:1; /* * notify_neighbor_rep - Callback for notifying report requester */ void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep); /* * neighbor_rep_cb_ctx - Callback context * Received in the callback registration, and sent to the callback * function as a parameter. */ void *neighbor_rep_cb_ctx; /* next_neighbor_rep_token - Next request's dialog token */ u8 next_neighbor_rep_token; /* token - Dialog token of the current radio measurement */ u8 token; /* destination address of the current radio measurement request */ u8 dst_addr[ETH_ALEN]; }; enum wpa_supplicant_test_failure { WPAS_TEST_FAILURE_NONE, WPAS_TEST_FAILURE_SCAN_TRIGGER, }; struct icon_entry { struct dl_list list; u8 bssid[ETH_ALEN]; u8 dialog_token; char *file_name; u8 *image; size_t image_len; }; struct wpa_bss_tmp_disallowed { struct dl_list list; u8 bssid[ETH_ALEN]; int rssi_threshold; }; struct beacon_rep_data { u8 token; u8 last_indication; struct wpa_driver_scan_params scan_params; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 bssid[ETH_ALEN]; enum beacon_report_detail report_detail; struct bitfield *eids; }; struct external_pmksa_cache { struct dl_list list; void *pmksa_cache; }; struct fils_hlp_req { struct dl_list list; u8 dst[ETH_ALEN]; struct wpabuf *pkt; }; struct driver_signal_override { struct dl_list list; u8 bssid[ETH_ALEN]; int si_current_signal; int si_avg_signal; int si_avg_beacon_signal; int si_current_noise; int scan_level; }; struct robust_av_data { u8 dialog_token; enum scs_request_type request_type; u8 up_bitmap; u8 up_limit; u32 stream_timeout; u8 frame_classifier[48]; size_t frame_classifier_len; bool valid_config; }; #ifdef CONFIG_PASN struct pasn_fils { u8 nonce[FILS_NONCE_LEN]; u8 anonce[FILS_NONCE_LEN]; u8 session[FILS_SESSION_LEN]; u8 erp_pmkid[PMKID_LEN]; bool completed; }; struct wpas_pasn { int akmp; int cipher; u16 group; int freq; size_t kdk_len; u8 trans_seq; u8 status; u8 bssid[ETH_ALEN]; size_t pmk_len; u8 pmk[PMK_LEN_MAX]; bool using_pmksa; u8 hash[SHA384_MAC_LEN]; struct wpabuf *beacon_rsne_rsnxe; struct wpa_ptk ptk; struct crypto_ecdh *ecdh; struct wpabuf *comeback; u16 comeback_after; #ifdef CONFIG_SAE struct sae_data sae; #endif /* CONFIG_SAE */ struct wpa_ssid *ssid; #ifdef CONFIG_FILS struct pasn_fils fils; #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R u8 pmk_r1[PMK_LEN_MAX]; size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; #endif /* CONFIG_IEEE80211R */ }; #endif /* CONFIG_PASN */ /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * * This structure contains the internal data for core wpa_supplicant code. This * should be only used directly from the core code. However, a pointer to this * data is used from other files as an arbitrary context pointer in calls to * core functions. */ struct wpa_supplicant { struct wpa_global *global; struct wpa_radio *radio; /* shared radio context */ struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */ struct wpa_supplicant *parent; struct wpa_supplicant *p2pdev; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; struct os_reltime roam_start; struct os_reltime roam_time; struct os_reltime session_start; struct os_reltime session_length; unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; #ifdef CONFIG_MATCH_IFACE int matched; #endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; char *dbus_groupobj_path; #ifdef CONFIG_AP char *preq_notify_peer; #endif /* CONFIG_AP */ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #ifdef CONFIG_CTRL_IFACE_BINDER const void *binder_object_key; #endif /* CONFIG_CTRL_IFACE_BINDER */ char bridge_ifname[16]; char *confname; char *confanother; struct wpa_config *conf; int countermeasures; struct os_reltime last_michael_mic_error; u8 bssid[ETH_ALEN]; u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ int reassociate; /* reassociation requested */ bool roam_in_progress; /* roam in progress */ unsigned int reassoc_same_bss:1; /* reassociating to the same BSS */ unsigned int reassoc_same_ess:1; /* reassociating to the same ESS */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; struct wpa_ssid *last_ssid; struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; u8 *last_con_fail_realm; size_t last_con_fail_realm_len; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ int pairwise_cipher; int deny_ptk0_rekey; int group_cipher; int key_mgmt; int wpa_proto; int mgmt_group_cipher; void *drv_priv; /* private data used by driver_ops */ void *global_drv_priv; u8 *bssid_filter; size_t bssid_filter_count; u8 *disallow_aps_bssid; size_t disallow_aps_bssid_count; struct wpa_ssid_value *disallow_aps_ssid; size_t disallow_aps_ssid_count; u32 setband_mask; /* Preferred network for the next connection attempt */ struct wpa_ssid *next_ssid; /* previous scan was wildcard when interleaving between * wildcard scans and specific SSID scan when max_ssids=1 */ int prev_scan_wildcard; struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; * NULL = not yet initialized (start * with wildcard SSID) * WILDCARD_SSID_SCAN = wildcard * SSID was used in the previous scan */ #define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1) struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */ int sched_scan_timeout; int first_sched_scan; int sched_scan_timed_out; struct sched_scan_plan *sched_scan_plans; size_t sched_scan_plans_num; void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); void (*scan_res_fail_handler)(struct wpa_supplicant *wpa_s); struct dl_list bss; /* struct wpa_bss::list */ struct dl_list bss_id; /* struct wpa_bss::list_id */ size_t num_bss; unsigned int bss_update_idx; unsigned int bss_next_id; /* * Pointers to BSS entries in the order they were in the last scan * results. */ struct wpa_bss **last_scan_res; size_t last_scan_res_used; size_t last_scan_res_size; struct os_reltime last_scan; const struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ struct wpa_sm *wpa; struct ptksa_cache *ptksa; struct eapol_sm *eapol; struct ctrl_iface_priv *ctrl_iface; enum wpa_states wpa_state; struct wpa_radio_work *scan_work; int scanning; int sched_scanning; unsigned int sched_scan_stop_req:1; int new_connection; int eapol_received; /* number of EAPOL packets received after the * previous association event */ u8 rsnxe[20]; size_t rsnxe_len; struct scard_data *scard; char imsi[20]; int mnc_len; unsigned char last_eapol_src[ETH_ALEN]; unsigned int keys_cleared; /* bitfield of key indexes that the driver is * known not to be configured with a key */ struct wpa_bssid_ignore *bssid_ignore; /* Number of connection failures since last successful connection */ unsigned int consecutive_conn_failures; /** * scan_req - Type of the scan request */ enum scan_req_type { /** * NORMAL_SCAN_REQ - Normal scan request * * This is used for scans initiated by wpa_supplicant to find an * AP for a connection. */ NORMAL_SCAN_REQ, /** * INITIAL_SCAN_REQ - Initial scan request * * This is used for the first scan on an interface to force at * least one scan to be run even if the configuration does not * include any enabled networks. */ INITIAL_SCAN_REQ, /** * MANUAL_SCAN_REQ - Manual scan request * * This is used for scans where the user request a scan or * a specific wpa_supplicant operation (e.g., WPS) requires scan * to be run. */ MANUAL_SCAN_REQ } scan_req, last_scan_req; enum wpa_states scan_prev_wpa_state; struct os_reltime scan_trigger_time, scan_start_time; /* Minimum freshness requirement for connection purposes */ struct os_reltime scan_min_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; int *select_network_scan_freqs; int *manual_scan_freqs; int *manual_sched_scan_freqs; unsigned int manual_scan_passive:1; unsigned int manual_scan_use_id:1; unsigned int manual_scan_only_new:1; unsigned int own_scan_requested:1; unsigned int own_scan_running:1; unsigned int clear_driver_scan_cache:1; unsigned int manual_scan_id; int scan_interval; /* time in sec between scans to find suitable AP */ int normal_scans; /* normal scans run before sched_scan */ int scan_for_connection; /* whether the scan request was triggered for * finding a connection */ /* * A unique cookie representing the vendor scan request. This cookie is * returned from the driver interface. 0 indicates that there is no * pending vendor scan request. */ u64 curr_scan_cookie; #define MAX_SCAN_ID 16 int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; u8 next_scan_bssid[ETH_ALEN]; unsigned int next_scan_bssid_wildcard_ssid:1; struct wpa_ssid_value *ssids_from_scan_req; unsigned int num_ssids_from_scan_req; int *last_scan_freqs; unsigned int num_last_scan_freqs; unsigned int suitable_network; unsigned int no_suitable_network; u64 drv_flags; u64 drv_flags2; unsigned int drv_enc; unsigned int drv_rrm_flags; /* * A bitmap of supported protocols for probe response offload. See * struct wpa_driver_capa in driver.h */ unsigned int probe_resp_offloads; /* extended capabilities supported by the driver */ const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; int max_scan_ssids; int max_sched_scan_ssids; unsigned int max_sched_scan_plans; unsigned int max_sched_scan_plan_interval; unsigned int max_sched_scan_plan_iterations; int sched_scan_supported; unsigned int max_match_sets; unsigned int max_remain_on_chan; unsigned int max_stations; int pending_mic_error_report; int pending_mic_error_pairwise; int mic_errors_seen; /* Michael MIC errors with the current PTK */ struct wps_context *wps; int wps_success; /* WPS success event received */ struct wps_er *wps_er; unsigned int wps_run; struct os_reltime wps_pin_start_time; bool bssid_ignore_cleared; struct wpabuf *pending_eapol_rx; struct os_reltime pending_eapol_rx_time; u8 pending_eapol_rx_src[ETH_ALEN]; unsigned int last_eapol_matches_bssid:1; unsigned int eap_expected_failure:1; unsigned int reattach:1; /* reassociation to the same BSS requested */ unsigned int mac_addr_changed:1; unsigned int added_vif:1; unsigned int wnmsleep_used:1; unsigned int owe_transition_select:1; unsigned int owe_transition_search:1; unsigned int connection_set:1; unsigned int connection_ht:1; unsigned int connection_vht:1; unsigned int connection_he:1; unsigned int disable_mbo_oce:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; struct ibss_rsn *ibss_rsn; int set_sta_uapsd; int sta_uapsd; int set_ap_uapsd; int ap_uapsd; int auth_alg; u16 last_owe_group; #ifdef CONFIG_SME struct { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int freq; u8 assoc_req_ie[1500]; size_t assoc_req_ie_len; int mfp; int ft_used; u8 mobility_domain[2]; u8 *ft_ies; size_t ft_ies_len; u8 prev_bssid[ETH_ALEN]; int prev_bssid_set; int auth_alg; int proto; int sa_query_count; /* number of pending SA Query requests; * 0 = no SA Query in progress */ int sa_query_timed_out; u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * * sa_query_count octets of pending * SA Query transaction identifiers */ struct os_reltime sa_query_start; struct os_reltime last_unprot_disconnect; enum { HT_SEC_CHAN_UNKNOWN, HT_SEC_CHAN_ABOVE, HT_SEC_CHAN_BELOW } ht_sec_chan; u8 sched_obss_scan; u16 obss_scan_int; u16 bss_max_idle_period; #ifdef CONFIG_SAE struct sae_data sae; struct wpabuf *sae_token; int sae_group_index; unsigned int sae_pmksa_caching:1; u16 seq_num; u8 ext_auth_bssid[ETH_ALEN]; u8 ext_auth_ssid[SSID_MAX_LEN]; size_t ext_auth_ssid_len; int *sae_rejected_groups; #endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ #ifdef CONFIG_AP struct hostapd_iface *ap_iface; void (*ap_configured_cb)(void *ctx, void *data); void *ap_configured_cb_ctx; void *ap_configured_cb_data; #endif /* CONFIG_AP */ struct hostapd_iface *ifmsh; #ifdef CONFIG_MESH struct mesh_rsn *mesh_rsn; int mesh_if_idx; unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; unsigned int mesh_vht_enabled:1; unsigned int mesh_he_enabled:1; struct wpa_driver_mesh_join_params *mesh_params; #ifdef CONFIG_PMKSA_CACHE_EXTERNAL /* struct external_pmksa_cache::list */ struct dl_list mesh_external_pmksa_cache; #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ #endif /* CONFIG_MESH */ unsigned int off_channel_freq; struct wpabuf *pending_action_tx; u8 pending_action_src[ETH_ALEN]; u8 pending_action_dst[ETH_ALEN]; u8 pending_action_bssid[ETH_ALEN]; unsigned int pending_action_freq; int pending_action_no_cck; int pending_action_without_roc; unsigned int pending_action_tx_done:1; void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result); unsigned int roc_waiting_drv_freq; int action_tx_wait_time; int action_tx_wait_time_used; int p2p_mgmt; #ifdef CONFIG_P2P struct p2p_go_neg_results *go_params; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; char pending_interface_name[100]; int pending_interface_type; int p2p_group_idx; unsigned int pending_listen_freq; unsigned int pending_listen_duration; enum { NOT_P2P_GROUP_INTERFACE, P2P_GROUP_INTERFACE_PENDING, P2P_GROUP_INTERFACE_GO, P2P_GROUP_INTERFACE_CLIENT } p2p_group_interface; struct p2p_group *p2p_group; char p2p_pin[10]; int p2p_wps_method; u8 p2p_auth_invite[ETH_ALEN]; int p2p_sd_over_ctrl_iface; int p2p_in_provisioning; int p2p_in_invitation; int p2p_invite_go_freq; int pending_invite_ssid_id; int show_group_started; u8 go_dev_addr[ETH_ALEN]; int pending_pd_before_join; u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; int pending_join_wps_method; u8 p2p_join_ssid[SSID_MAX_LEN]; size_t p2p_join_ssid_len; int p2p_join_scan_count; int auto_pd_scan_retry; int force_long_sd; u16 pending_pd_config_methods; enum { NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP } pending_pd_use; /* * Whether cross connection is disallowed by the AP to which this * interface is associated (only valid if there is an association). */ int cross_connect_disallowed; /* * Whether this P2P group is configured to use cross connection (only * valid if this is P2P GO interface). The actual cross connect packet * forwarding may not be configured depending on the uplink status. */ int cross_connect_enabled; /* Whether cross connection forwarding is in use at the moment. */ int cross_connect_in_use; /* * Uplink interface name for cross connection */ char cross_connect_uplink[100]; unsigned int p2p_auto_join:1; unsigned int p2p_auto_pd:1; unsigned int p2p_go_do_acs:1; unsigned int p2p_persistent_group:1; unsigned int p2p_fallback_to_go_neg:1; unsigned int p2p_pd_before_go_neg:1; unsigned int p2p_go_ht40:1; unsigned int p2p_go_vht:1; unsigned int p2p_go_edmg:1; unsigned int p2p_go_he:1; unsigned int user_initiated_pd:1; unsigned int p2p_go_group_formation_completed:1; unsigned int group_formation_reported:1; unsigned int waiting_presence_resp; int p2p_first_connection_timeout; unsigned int p2p_nfc_tag_enabled:1; unsigned int p2p_peer_oob_pk_hash_known:1; unsigned int p2p_disable_ip_addr_req:1; unsigned int p2ps_method_config_any:1; unsigned int p2p_cli_probe:1; enum hostapd_hw_mode p2p_go_acs_band; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; int p2p_connect_freq; struct os_reltime p2p_auto_started; struct wpa_ssid *p2p_last_4way_hs_fail; struct wpa_radio_work *p2p_scan_work; struct wpa_radio_work *p2p_listen_work; struct wpa_radio_work *p2p_send_action_work; u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */ struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group * formation */ u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; u8 p2p_ip_addr_info[3 * 4]; /* group common frequencies */ int *p2p_group_common_freqs; unsigned int p2p_group_common_freqs_num; u8 p2ps_join_addr[ETH_ALEN]; unsigned int p2p_go_max_oper_chwidth; unsigned int p2p_go_vht_center_freq2; int p2p_lo_started; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; const struct bgscan_ops *bgscan; void *bgscan_priv; const struct autoscan_ops *autoscan; struct wpa_driver_scan_params *autoscan_params; void *autoscan_priv; struct wpa_ssid *connect_without_scan; struct wps_ap_info *wps_ap; size_t num_wps_ap; int wps_ap_iter; int after_wps; int known_wps_freq; unsigned int wps_freq; int wps_fragment_size; int auto_reconnect_disabled; /* Channel preferences for AP/P2P GO use */ int best_24_freq; int best_5_freq; int best_overall_freq; struct gas_query *gas; struct gas_server *gas_server; #ifdef CONFIG_INTERWORKING unsigned int fetch_anqp_in_progress:1; unsigned int network_select:1; unsigned int auto_select:1; unsigned int auto_network_select:1; unsigned int interworking_fast_assoc_tried:1; unsigned int fetch_all_anqp:1; unsigned int fetch_osu_info:1; unsigned int fetch_osu_waiting_scan:1; unsigned int fetch_osu_icon_in_progress:1; struct wpa_bss *interworking_gas_bss; unsigned int osu_icon_id; struct dl_list icon_head; /* struct icon_entry */ struct osu_provider *osu_prov; size_t osu_prov_count; struct os_reltime osu_icon_fetch_start; unsigned int num_osu_scans; unsigned int num_prov_found; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; struct { struct hostapd_hw_modes *modes; u16 num_modes; u16 flags; } hw; enum local_hw_capab { CAPAB_NO_HT_VHT, CAPAB_HT, CAPAB_HT40, CAPAB_VHT, } hw_capab; #ifdef CONFIG_MACSEC struct ieee802_1x_kay *kay; #endif /* CONFIG_MACSEC */ int pno; int pno_sched_pending; /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; /* WLAN_STATUS_* status codes from last received Authentication frame * from the AP. */ u16 auth_status_code; /* WLAN_STATUS_* status codes from (Re)Association Response frame. */ u16 assoc_status_code; struct ext_password_data *ext_pw; struct wpabuf *last_gas_resp, *prev_gas_resp; u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN]; u8 last_gas_dialog_token, prev_gas_dialog_token; unsigned int no_keep_alive:1; unsigned int ext_mgmt_frame_handling:1; unsigned int ext_eapol_frame_io:1; unsigned int wmm_ac_supported:1; unsigned int ext_work_in_progress:1; unsigned int own_disconnect_req:1; unsigned int own_reconnect_req:1; unsigned int ignore_post_flush_scan_res:1; #define MAC_ADDR_RAND_SCAN BIT(0) #define MAC_ADDR_RAND_SCHED_SCAN BIT(1) #define MAC_ADDR_RAND_PNO BIT(2) #define MAC_ADDR_RAND_ALL (MAC_ADDR_RAND_SCAN | \ MAC_ADDR_RAND_SCHED_SCAN | \ MAC_ADDR_RAND_PNO) unsigned int mac_addr_rand_supported; unsigned int mac_addr_rand_enable; /* MAC Address followed by mask (2 * ETH_ALEN) */ u8 *mac_addr_scan; u8 *mac_addr_sched_scan; u8 *mac_addr_pno; #ifdef CONFIG_WNM u8 wnm_dialog_token; u8 wnm_reply; u8 wnm_num_neighbor_report; u8 wnm_mode; u16 wnm_dissoc_timer; u8 wnm_bss_termination_duration[12]; struct neighbor_report *wnm_neighbor_report_elements; struct os_reltime wnm_cand_valid_until; u8 wnm_cand_from_bss[ETH_ALEN]; enum bss_trans_mgmt_status_code bss_tm_status; struct wpabuf *coloc_intf_elems; u8 coloc_intf_dialog_token; u8 coloc_intf_auto_report; u8 coloc_intf_timeout; #ifdef CONFIG_MBO unsigned int wnm_mbo_trans_reason_present:1; u8 wnm_mbo_transition_reason; #endif /* CONFIG_MBO */ #endif /* CONFIG_WNM */ #ifdef CONFIG_TESTING_GET_GTK u8 last_gtk[32]; size_t last_gtk_len; #endif /* CONFIG_TESTING_GET_GTK */ unsigned int num_multichan_concurrent; struct wpa_radio_work *connect_work; unsigned int ext_work_id; struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES]; #ifdef CONFIG_TESTING_OPTIONS struct l2_packet_data *l2_test; unsigned int extra_roc_dur; enum wpa_supplicant_test_failure test_failure; char *get_pref_freq_list_override; unsigned int reject_btm_req_reason; unsigned int p2p_go_csa_on_inv:1; unsigned int ignore_auth_resp:1; unsigned int ignore_assoc_disallow:1; unsigned int disable_sa_query:1; unsigned int testing_resend_assoc:1; unsigned int ignore_sae_h2e_only:1; int ft_rsnxe_used; struct wpabuf *sae_commit_override; enum wpa_alg last_tk_alg; u8 last_tk_addr[ETH_ALEN]; int last_tk_key_idx; u8 last_tk[WPA_TK_MAX_LEN]; size_t last_tk_len; struct wpabuf *last_assoc_req_wpa_ie; int *extra_sae_rejected_groups; struct wpabuf *rsne_override_eapol; struct wpabuf *rsnxe_override_assoc; struct wpabuf *rsnxe_override_eapol; struct dl_list drv_signal_override; unsigned int oci_freq_override_eapol; unsigned int oci_freq_override_saquery_req; unsigned int oci_freq_override_saquery_resp; unsigned int oci_freq_override_eapol_g2; unsigned int oci_freq_override_ft_assoc; unsigned int oci_freq_override_fils_assoc; unsigned int oci_freq_override_wnm_sleep; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT]; struct wmm_ac_addts_request *addts_request; u8 wmm_ac_last_dialog_token; struct wmm_tspec_element *last_tspecs; u8 last_tspecs_count; struct rrm_data rrm; struct beacon_rep_data beacon_rep_data; #ifdef CONFIG_FST struct fst_iface *fst; const struct wpabuf *fst_ies; struct wpabuf *received_mb_ies; #endif /* CONFIG_FST */ #ifdef CONFIG_MBO /* Multiband operation non-preferred channel */ struct wpa_mbo_non_pref_channel { enum mbo_non_pref_chan_reason reason; u8 oper_class; u8 chan; u8 preference; } *non_pref_chan; size_t non_pref_chan_num; u8 mbo_wnm_token; /** * enable_oce - Enable OCE if it is enabled by user and device also * supports OCE. * User can enable OCE with wpa_config's 'oce' parameter as follows - * - Set BIT(0) to enable OCE in non-AP STA mode. * - Set BIT(1) to enable OCE in STA-CFON mode. */ u8 enable_oce; #endif /* CONFIG_MBO */ /* * This should be under CONFIG_MBO, but it is left out to allow using * the bss_temp_disallowed list for other purposes as well. */ struct dl_list bss_tmp_disallowed; /* * Content of a measurement report element with type 8 (LCI), * own location. */ struct wpabuf *lci; struct os_reltime lci_time; struct os_reltime beacon_rep_scan; /* FILS HLP requests (struct fils_hlp_req) */ struct dl_list fils_hlp_req; struct sched_scan_relative_params { /** * relative_rssi_set - Enable relatively preferred BSS reporting * * 0 = Disable reporting relatively preferred BSSs * 1 = Enable reporting relatively preferred BSSs */ int relative_rssi_set; /** * relative_rssi - Relative RSSI for reporting better BSSs * * Amount of RSSI by which a BSS should be better than the * current connected BSS so that the new BSS can be reported * to user space. This applies to sched_scan operations. */ int relative_rssi; /** * relative_adjust_band - Band in which RSSI is to be adjusted */ enum set_band relative_adjust_band; /** * relative_adjust_rssi - RSSI adjustment * * An amount of relative_adjust_rssi should be added to the * BSSs that belong to the relative_adjust_band while comparing * with other bands for BSS reporting. */ int relative_adjust_rssi; } srp; /* RIC elements for FT protocol */ struct wpabuf *ric_ies; int last_auth_timeout_sec; #ifdef CONFIG_DPP struct dpp_global *dpp; struct dpp_authentication *dpp_auth; struct wpa_radio_work *dpp_listen_work; unsigned int dpp_pending_listen_freq; unsigned int dpp_listen_freq; struct os_reltime dpp_listen_end; u8 dpp_allowed_roles; int dpp_qr_mutual; int dpp_netrole; int dpp_auth_ok_on_ack; int dpp_in_response_listen; int dpp_gas_client; int dpp_gas_dialog_token; u8 dpp_intro_bssid[ETH_ALEN]; void *dpp_intro_network; struct dpp_pkex *dpp_pkex; struct dpp_bootstrap_info *dpp_pkex_bi; char *dpp_pkex_code; char *dpp_pkex_identifier; char *dpp_pkex_auth_cmd; char *dpp_configurator_params; struct os_reltime dpp_last_init; struct os_reltime dpp_init_iter_start; unsigned int dpp_init_max_tries; unsigned int dpp_init_retry_time; unsigned int dpp_resp_wait_time; unsigned int dpp_resp_max_tries; unsigned int dpp_resp_retry_time; u8 dpp_last_ssid[SSID_MAX_LEN]; size_t dpp_last_ssid_len; bool dpp_conf_backup_received; #ifdef CONFIG_DPP2 struct dpp_pfs *dpp_pfs; int dpp_pfs_fallback; struct wpabuf *dpp_presence_announcement; struct dpp_bootstrap_info *dpp_chirp_bi; int dpp_chirp_freq; int *dpp_chirp_freqs; int dpp_chirp_iter; int dpp_chirp_round; int dpp_chirp_scan_done; int dpp_chirp_listen; struct wpa_ssid *dpp_reconfig_ssid; int dpp_reconfig_ssid_id; struct dpp_reconfig_id *dpp_reconfig_id; #endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override; char *dpp_discovery_override; char *dpp_groups_override; unsigned int dpp_ignore_netaccesskey_mismatch:1; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_DPP */ #ifdef CONFIG_FILS unsigned int disable_fils:1; #endif /* CONFIG_FILS */ unsigned int ieee80211ac:1; unsigned int enabled_4addr_mode:1; unsigned int multi_bss_support:1; unsigned int drv_authorized_port:1; unsigned int multi_ap_ie:1; unsigned int multi_ap_backhaul:1; unsigned int multi_ap_fronthaul:1; struct robust_av_data robust_av; bool mscs_setup_done; #ifdef CONFIG_PASN struct wpas_pasn pasn; struct wpa_radio_work *pasn_auth_work; #endif /* CONFIG_PASN */ }; /* wpa_supplicant.c */ void wpa_supplicant_apply_ht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params); void wpa_supplicant_apply_vht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params); void wpa_supplicant_apply_he_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params); int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); const char * wpa_supplicant_state_txt(enum wpa_states state); int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s); int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s); int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s, const char *bridge_ifname); int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, u8 *wpa_ie, size_t *wpa_ie_len); int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s); void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid); void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s); void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec); void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff); void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s); void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state); struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason_code); void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s); struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s); int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id); int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s); void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, const char *pkcs11_engine_path, const char *pkcs11_module_path); int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan); int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, unsigned int expire_age); int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, unsigned int expire_count); int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, int scan_interval); int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int debug_timestamp, int debug_show_keys); void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, const char *ifname); struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface, struct wpa_supplicant *parent); int wpa_supplicant_remove_iface(struct wpa_global *global, struct wpa_supplicant *wpa_s, int terminate); struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, const char *ifname); struct wpa_global * wpa_supplicant_init(struct wpa_params *params); int wpa_supplicant_run(struct wpa_global *global); void wpa_supplicant_deinit(struct wpa_global *global); int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_terminate_proc(struct wpa_global *global); void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s); void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); void fils_connection_failure(struct wpa_supplicant *wpa_s); +void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s); int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason); void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int clear_failures); int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(struct wpa_supplicant *wpa_s); void wpas_request_disconnection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen); int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style); int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); void add_freq(int *freqs, int *num_freqs, int freq); int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, u8 *op_class, u8 *chan, u8 *phy_type); int wpas_twt_send_setup(struct wpa_supplicant *wpa_s, u8 dtok, int exponent, int mantissa, u8 min_twt, int setup_cmd, u64 twt, bool requestor, bool trigger, bool implicit, bool flow_type, u8 flow_id, bool protection, u8 twt_channel, u8 control); int wpas_twt_send_teardown(struct wpa_supplicant *wpa_s, u8 flags); void wpas_rrm_reset(struct wpa_supplicant *wpa_s); void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, const u8 *report, size_t report_len); int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, const struct wpa_ssid_value *ssid, int lci, int civic, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx); void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *dst, const u8 *frame, size_t len); void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, int rssi); void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s); int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res, struct scan_info *info); void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s); void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s); void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx); void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s); /* MBO functions */ int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, int add_oce_capa); const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr); const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr); void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid); const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len, enum mbo_attr_id attr); int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, const char *non_pref_chan); void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie); void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie, size_t len); size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, size_t len, enum mbo_transition_reject_reason reason); void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa); struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u32 mbo_subtypes); void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, const u8 *data, size_t slen); void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s); /* op_classes.c */ enum chan_allowed { NOT_ALLOWED, NO_IR, ALLOWED }; enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class, u8 channel, u8 bw); size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss, u8 *pos, size_t len); int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s); int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s, unsigned int type, const u8 *addr, const u8 *mask); int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s, unsigned int type); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response * @wpa_s: Pointer to wpa_supplicant data * @ssid: Pointer to the network block the reply is for * @field: field the response is a reply for * @value: value (ie, password, etc) for @field * Returns: 0 on success, non-zero on error * * Helper function to handle replies to control interface requests. */ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const char *field, const char *value); void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq); /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid); void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx); void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx); void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid); int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, struct channel_list_changed *info); int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss, struct wpa_bss *seleceted); /* eap_register.c */ int eap_register_methods(void); /** * Utility method to tell if a given network is for persistent group storage * @ssid: Network object * Returns: 1 if network is a persistent group, 0 otherwise */ static inline int network_is_persistent_group(struct wpa_ssid *ssid) { return ssid->disabled == 2 && ssid->p2p_persistent_group; } static inline int wpas_mode_to_ieee80211_mode(enum wpas_mode mode) { switch (mode) { default: case WPAS_MODE_INFRA: return IEEE80211_MODE_INFRA; case WPAS_MODE_IBSS: return IEEE80211_MODE_IBSS; case WPAS_MODE_AP: case WPAS_MODE_P2P_GO: case WPAS_MODE_P2P_GROUP_FORMATION: return IEEE80211_MODE_AP; case WPAS_MODE_MESH: return IEEE80211_MODE_MESH; } } int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, struct wpa_used_freq_data *freqs_data, unsigned int len); int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs_data, unsigned int len); int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, int *freq_array, unsigned int len); void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx); void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s); struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame); int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, const u8 *elem, size_t len); #ifdef CONFIG_FST struct fst_wpa_obj; void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, struct fst_wpa_obj *iface_obj); #endif /* CONFIG_FST */ int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd); struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, u16 num_modes, enum hostapd_hw_mode mode, bool is_6ghz); struct hostapd_hw_modes * get_mode_with_freq(struct hostapd_hw_modes *modes, u16 num_modes, int freq); void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, unsigned int sec, int rssi_threshold); int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s); struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, int only_first_ssid, int debug_print); int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type if_type, unsigned int *num, unsigned int *freq_list); int wpa_is_fils_supported(struct wpa_supplicant *wpa_s); int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s); void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s); int wpas_send_mscs_req(struct wpa_supplicant *wpa_s); void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av, struct wpabuf *buf); void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len); void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ies, size_t ies_len); int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid, int akmp, int cipher, u16 group, int network_id, const u8 *comeback, size_t comeback_len); void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s); int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, size_t data_len, u8 acked); int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len); int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid); #endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 029349b08d20..5633f3d1ecaf 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -1,3008 +1,3013 @@ /* * wpa_supplicant / WPS integration * Copyright (c) 2008-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" #include "common.h" #include "eloop.h" #include "uuid.h" #include "crypto/random.h" #include "crypto/dh_group5.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_common.h" #include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "wps/wps_attr_parse.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "notify.h" #include "bssid_ignore.h" #include "bss.h" #include "scan.h" #include "ap.h" #include "p2p/p2p.h" #include "p2p_supplicant.h" #include "wps_supplicant.h" #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ /* * The minimum time in seconds before trying to associate to a WPS PIN AP that * does not have Selected Registrar TRUE. */ #ifndef WPS_PIN_TIME_IGNORE_SEL_REG #define WPS_PIN_TIME_IGNORE_SEL_REG 5 #endif /* WPS_PIN_TIME_IGNORE_SEL_REG */ static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s) { os_free(wpa_s->wps_ap); wpa_s->wps_ap = NULL; wpa_s->num_wps_ap = 0; wpa_s->wps_ap_iter = 0; } static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; int use_fast_assoc = timeout_ctx != NULL; wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb"); if (!use_fast_assoc || wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); } static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0); eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1); } int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { if (wpas_p2p_wps_eapol_cb(wpa_s) > 0) return 1; if (!wpa_s->wps_success && wpa_s->current_ssid && eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { const u8 *bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR " did not succeed - continue trying to find " "suitable AP", MAC2STR(bssid)); wpa_bssid_ignore_add(wpa_s, bssid); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, wpa_s->bssid_ignore_cleared ? 5 : 0, 0); wpa_s->bssid_ignore_cleared = false; return 1; } wpas_wps_clear_ap_info(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success) wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid && !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { int disabled = wpa_s->current_ssid->disabled; unsigned int freq = wpa_s->assoc_freq; struct wpa_bss *bss; struct wpa_ssid *ssid = NULL; int use_fast_assoc = 0; wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " "try to associate with the received credential " "(freq=%u)", freq); wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); if (disabled) { wpa_printf(MSG_DEBUG, "WPS: Current network is " "disabled - wait for user to enable"); return 1; } wpa_s->after_wps = 5; wpa_s->wps_freq = freq; wpa_s->normal_scans = 0; wpa_s->reassociate = 1; wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association " "without a new scan can be used"); bss = wpa_supplicant_pick_network(wpa_s, &ssid); if (bss) { struct wpabuf *wps; struct wps_parse_attr attr; wps = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps && wps_parse_msg(wps, &attr) == 0 && attr.wps_state && *attr.wps_state == WPS_STATE_CONFIGURED) use_fast_assoc = 1; wpabuf_free(wps); } /* * Complete the next step from an eloop timeout to allow pending * driver events related to the disconnection to be processed * first. This makes it less likely for disconnection event to * cause problems with the following connection. */ wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout"); wpas_wps_assoc_with_cred_cancel(wpa_s); eloop_register_timeout(0, 10000, wpas_wps_assoc_with_cred, wpa_s, use_fast_assoc ? (void *) 1 : (void *) 0); return 1; } if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) { wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting " "for external credential processing"); wpas_clear_wps(wpa_s); wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); return 1; } return 0; } static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const struct wps_credential *cred) { struct wpa_driver_capa capa; struct wpa_bss *bss; const u8 *ie; struct wpa_ie_data adv; int wpa2 = 0, ccmp = 0; enum wpa_driver_if_type iftype; /* * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in * case they are configured for mixed mode operation (WPA+WPA2 and * TKIP+CCMP). Try to use scan results to figure out whether the AP * actually supports stronger security and select that if the client * has support for it, too. */ if (wpa_drv_get_capa(wpa_s, &capa)) return; /* Unknown what driver supports */ if (ssid->ssid == NULL) return; bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len); if (!bss) bss = wpa_bss_get(wpa_s, wpa_s->bssid, ssid->ssid, ssid->ssid_len); if (bss == NULL) { wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS " "table - use credential as-is"); return; } wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table"); ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) { wpa2 = 1; if (adv.pairwise_cipher & WPA_CIPHER_CCMP) ccmp = 1; } else { ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 && adv.pairwise_cipher & WPA_CIPHER_CCMP) ccmp = 1; } if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) && (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) { /* * TODO: This could be the initial AP configuration and the * Beacon contents could change shortly. Should request a new * scan and delay addition of the network until the updated * scan results are available. */ wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA " "support - use credential as-is"); return; } iftype = ssid->p2p_group ? WPA_IF_P2P_CLIENT : WPA_IF_STATION; if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && (ssid->pairwise_cipher & WPA_CIPHER_TKIP) && (capa.key_mgmt_iftype[iftype] & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential " "based on scan results"); if (wpa_s->conf->ap_scan == 1) ssid->pairwise_cipher |= WPA_CIPHER_CCMP; else ssid->pairwise_cipher = WPA_CIPHER_CCMP; } if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) && (ssid->proto & WPA_PROTO_WPA) && (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) { wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential " "based on scan results"); if (wpa_s->conf->ap_scan == 1) ssid->proto |= WPA_PROTO_RSN; else ssid->proto = WPA_PROTO_RSN; } } static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *new_ssid) { struct wpa_ssid *ssid, *next; for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid; ssid = next, next = ssid ? ssid->next : NULL) { /* * new_ssid has already been added to the list in * wpas_wps_add_network(), so skip it. */ if (ssid == new_ssid) continue; if (ssid->bssid_set || new_ssid->bssid_set) { if (ssid->bssid_set != new_ssid->bssid_set) continue; if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) != 0) continue; } /* compare SSID */ if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len) continue; if (ssid->ssid && new_ssid->ssid) { if (os_memcmp(ssid->ssid, new_ssid->ssid, ssid->ssid_len) != 0) continue; } else if (ssid->ssid || new_ssid->ssid) continue; /* compare security parameters */ if (ssid->auth_alg != new_ssid->auth_alg || ssid->key_mgmt != new_ssid->key_mgmt || (ssid->group_cipher != new_ssid->group_cipher && !(ssid->group_cipher & new_ssid->group_cipher & WPA_CIPHER_CCMP))) continue; /* * Some existing WPS APs will send two creds in case they are * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP). * Try to merge these two creds if they are received in the same * M8 message. */ if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run && wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { if (new_ssid->passphrase && ssid->passphrase && os_strcmp(new_ssid->passphrase, ssid->passphrase) != 0) { wpa_printf(MSG_DEBUG, "WPS: M8 Creds with different passphrase - do not merge"); continue; } if (new_ssid->psk_set && (!ssid->psk_set || os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) { wpa_printf(MSG_DEBUG, "WPS: M8 Creds with different PSK - do not merge"); continue; } if ((new_ssid->passphrase && !ssid->passphrase) || (!new_ssid->passphrase && ssid->passphrase)) { wpa_printf(MSG_DEBUG, "WPS: M8 Creds with different passphrase/PSK type - do not merge"); continue; } wpa_printf(MSG_DEBUG, "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message"); new_ssid->proto |= ssid->proto; new_ssid->pairwise_cipher |= ssid->pairwise_cipher; } else { /* * proto and pairwise_cipher difference matter for * non-mixed-mode creds. */ if (ssid->proto != new_ssid->proto || ssid->pairwise_cipher != new_ssid->pairwise_cipher) continue; } /* Remove the duplicated older network entry. */ wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id); wpas_notify_network_removed(wpa_s, ssid); if (wpa_s->current_ssid == ssid) wpa_s->current_ssid = NULL; wpa_config_remove_network(wpa_s->conf, ssid->id); } } static int wpa_supplicant_wps_cred(void *ctx, const struct wps_credential *cred) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid = wpa_s->current_ssid; u16 auth_type; #ifdef CONFIG_WPS_REG_DISABLE_OPEN int registrar = 0; #endif /* CONFIG_WPS_REG_DISABLE_OPEN */ + bool add_sae; if ((wpa_s->conf->wps_cred_processing == 1 || wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) { size_t blen = cred->cred_attr_len * 2 + 1; char *buf = os_malloc(blen); if (buf) { wpa_snprintf_hex(buf, blen, cred->cred_attr, cred->cred_attr_len); wpa_msg(wpa_s, MSG_INFO, "%s%s", WPS_EVENT_CRED_RECEIVED, buf); os_free(buf); } wpas_notify_wps_credential(wpa_s, cred); } else wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED); wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", cred->cred_attr, cred->cred_attr_len); if (wpa_s->conf->wps_cred_processing == 1) return 0; wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", cred->auth_type); wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", cred->key, cred->key_len); wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(cred->mac_addr)); auth_type = cred->auth_type; if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode " "auth_type into WPA2PSK"); auth_type = WPS_AUTH_WPA2PSK; } if (auth_type != WPS_AUTH_OPEN && auth_type != WPS_AUTH_WPAPSK && auth_type != WPS_AUTH_WPA2PSK) { wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for " "unsupported authentication type 0x%x", auth_type); return 0; } if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) { if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) { wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with " "invalid Network Key length %lu", (unsigned long) cred->key_len); return -1; } } if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " "on the received credential"); #ifdef CONFIG_WPS_REG_DISABLE_OPEN if (ssid->eap.identity && ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN && os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) registrar = 1; #endif /* CONFIG_WPS_REG_DISABLE_OPEN */ os_free(ssid->eap.identity); ssid->eap.identity = NULL; ssid->eap.identity_len = 0; os_free(ssid->eap.phase1); ssid->eap.phase1 = NULL; os_free(ssid->eap.eap_methods); ssid->eap.eap_methods = NULL; if (!ssid->p2p_group) { ssid->temporary = 0; ssid->bssid_set = 0; } ssid->disabled_until.sec = 0; ssid->disabled_until.usec = 0; ssid->auth_failures = 0; } else { wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " "received credential"); ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; if (wpa_s->current_ssid) { /* * Should the GO issue multiple credentials for some * reason, each credential should be marked as a * temporary P2P group similarly to the one that gets * marked as such based on the pre-configured values * used for the WPS network block. */ ssid->p2p_group = wpa_s->current_ssid->p2p_group; ssid->temporary = wpa_s->current_ssid->temporary; } wpas_notify_network_added(wpa_s, ssid); } wpa_config_set_network_defaults(ssid); ssid->wps_run = wpa_s->wps_run; os_free(ssid->ssid); ssid->ssid = os_malloc(cred->ssid_len); if (ssid->ssid) { os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len); ssid->ssid_len = cred->ssid_len; } switch (cred->encr_type) { case WPS_ENCR_NONE: break; case WPS_ENCR_TKIP: ssid->pairwise_cipher = WPA_CIPHER_TKIP | WPA_CIPHER_CCMP; break; case WPS_ENCR_AES: ssid->pairwise_cipher = WPA_CIPHER_CCMP; if (wpa_s->drv_capa_known && (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) { ssid->pairwise_cipher |= WPA_CIPHER_GCMP; ssid->group_cipher |= WPA_CIPHER_GCMP; } if (wpa_s->drv_capa_known && (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) { ssid->pairwise_cipher |= WPA_CIPHER_GCMP_256; ssid->group_cipher |= WPA_CIPHER_GCMP_256; } if (wpa_s->drv_capa_known && (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256)) { ssid->pairwise_cipher |= WPA_CIPHER_CCMP_256; ssid->group_cipher |= WPA_CIPHER_CCMP_256; } break; } switch (auth_type) { case WPS_AUTH_OPEN: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_NONE; ssid->proto = 0; #ifdef CONFIG_WPS_REG_DISABLE_OPEN if (registrar) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK "id=%d - Credentials for an open " "network disabled by default - use " "'select_network %d' to enable", ssid->id, ssid->id); ssid->disabled = 1; } #endif /* CONFIG_WPS_REG_DISABLE_OPEN */ break; case WPS_AUTH_WPAPSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN; break; case WPS_AUTH_WPA2PSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; - if (wpa_s->conf->wps_cred_add_sae && - cred->key_len != 2 * PMK_LEN) { + add_sae = wpa_s->conf->wps_cred_add_sae; +#ifdef CONFIG_P2P + if (ssid->p2p_group && is_p2p_6ghz_capable(wpa_s->global->p2p)) + add_sae = true; +#endif /* CONFIG_P2P */ + if (add_sae && cred->key_len != 2 * PMK_LEN) { ssid->auth_alg = 0; ssid->key_mgmt |= WPA_KEY_MGMT_SAE; ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; } ssid->proto = WPA_PROTO_RSN; break; } if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { if (cred->key_len == 2 * PMK_LEN) { if (hexstr2bin((const char *) cred->key, ssid->psk, PMK_LEN)) { wpa_printf(MSG_ERROR, "WPS: Invalid Network " "Key"); return -1; } ssid->psk_set = 1; ssid->export_keys = 1; } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) { os_free(ssid->passphrase); ssid->passphrase = os_malloc(cred->key_len + 1); if (ssid->passphrase == NULL) return -1; os_memcpy(ssid->passphrase, cred->key, cred->key_len); ssid->passphrase[cred->key_len] = '\0'; wpa_config_update_psk(ssid); ssid->export_keys = 1; } else { wpa_printf(MSG_ERROR, "WPS: Invalid Network Key " "length %lu", (unsigned long) cred->key_len); return -1; } } ssid->priority = wpa_s->conf->wps_priority; wpas_wps_security_workaround(wpa_s, ssid, cred); wpas_wps_remove_dup_network(wpa_s, ssid); #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration"); return -1; } #endif /* CONFIG_NO_CONFIG_WRITE */ if (ssid->priority) wpa_config_update_prio_list(wpa_s->conf); /* * Optimize the post-WPS scan based on the channel used during * the provisioning in case EAP-Failure is not received. */ wpa_s->after_wps = 5; wpa_s->wps_freq = wpa_s->assoc_freq; return 0; } static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, struct wps_event_m2d *m2d) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D "dev_password_id=%d config_error=%d", m2d->dev_password_id, m2d->config_error); wpas_notify_wps_event_m2d(wpa_s, m2d); #ifdef CONFIG_P2P if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) { wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D "dev_password_id=%d config_error=%d", m2d->dev_password_id, m2d->config_error); } if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) { /* * Notify P2P from eloop timeout to avoid issues with the * interface getting removed while processing a message. */ eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s, NULL); } #endif /* CONFIG_P2P */ } static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout"); wpas_clear_wps(wpa_s); } static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { if (fail->error_indication > 0 && fail->error_indication < NUM_WPS_EI_VALUES) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); } else { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } /* * Need to allow WPS processing to complete, e.g., by sending WSC_NACK. */ wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network"); eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL); wpas_notify_wps_event_fail(wpa_s, fail); wpas_p2p_wps_failed(wpa_s, fail); } static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx); static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; int changed = 0; eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid->disabled_for_connect && ssid->disabled) { ssid->disabled_for_connect = 0; ssid->disabled = 0; wpas_notify_network_enabled_changed(wpa_s, ssid); changed++; } } if (changed) { #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { wpa_printf(MSG_DEBUG, "WPS: Failed to update " "configuration"); } #endif /* CONFIG_NO_CONFIG_WRITE */ } } static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; /* Enable the networks disabled during wpas_wps_reassoc */ wpas_wps_reenable_networks(wpa_s); } int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) { return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb, wpa_s, NULL); } static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); wpa_s->wps_success = 1; wpas_notify_wps_event_success(wpa_s); if (wpa_s->current_ssid) wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1); wpa_s->consecutive_conn_failures = 0; /* * Enable the networks disabled during wpas_wps_reassoc after 10 * seconds. The 10 seconds timer is to allow the data connection to be * formed before allowing other networks to be selected. */ eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, NULL); wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); } static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s, struct wps_event_er_ap *ap) { char uuid_str[100]; char dev_type[WPS_DEV_TYPE_BUFSIZE]; uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str)); if (ap->pri_dev_type) wps_dev_type_bin2str(ap->pri_dev_type, dev_type, sizeof(dev_type)); else dev_type[0] = '\0'; wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR " pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|", uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state, ap->friendly_name ? ap->friendly_name : "", ap->manufacturer ? ap->manufacturer : "", ap->model_description ? ap->model_description : "", ap->model_name ? ap->model_name : "", ap->manufacturer_url ? ap->manufacturer_url : "", ap->model_url ? ap->model_url : ""); } static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s, struct wps_event_er_ap *ap) { char uuid_str[100]; uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str)); wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str); } static void wpa_supplicant_wps_event_er_enrollee_add( struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee) { char uuid_str[100]; char dev_type[WPS_DEV_TYPE_BUFSIZE]; uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str)); if (enrollee->pri_dev_type) wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type, sizeof(dev_type)); else dev_type[0] = '\0'; wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR " M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s " "|%s|%s|%s|%s|%s|", uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received, enrollee->config_methods, enrollee->dev_passwd_id, dev_type, enrollee->dev_name ? enrollee->dev_name : "", enrollee->manufacturer ? enrollee->manufacturer : "", enrollee->model_name ? enrollee->model_name : "", enrollee->model_number ? enrollee->model_number : "", enrollee->serial_number ? enrollee->serial_number : ""); } static void wpa_supplicant_wps_event_er_enrollee_remove( struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee) { char uuid_str[100]; uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str)); wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR, uuid_str, MAC2STR(enrollee->mac_addr)); } static void wpa_supplicant_wps_event_er_ap_settings( struct wpa_supplicant *wpa_s, struct wps_event_er_ap_settings *ap_settings) { char uuid_str[100]; char key_str[65]; const struct wps_credential *cred = ap_settings->cred; key_str[0] = '\0'; if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { if (cred->key_len >= 8 && cred->key_len <= 64) { os_memcpy(key_str, cred->key, cred->key_len); key_str[cred->key_len] = '\0'; } } uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str)); /* Use wpa_msg_ctrl to avoid showing the key in debug log */ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x " "key=%s", uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len), cred->auth_type, cred->encr_type, key_str); } static void wpa_supplicant_wps_event_er_set_sel_reg( struct wpa_supplicant *wpa_s, struct wps_event_er_set_selected_registrar *ev) { char uuid_str[100]; uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str)); switch (ev->state) { case WPS_ER_SET_SEL_REG_START: wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG "uuid=%s state=START sel_reg=%d dev_passwd_id=%u " "sel_reg_config_methods=0x%x", uuid_str, ev->sel_reg, ev->dev_passwd_id, ev->sel_reg_config_methods); break; case WPS_ER_SET_SEL_REG_DONE: wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG "uuid=%s state=DONE", uuid_str); break; case WPS_ER_SET_SEL_REG_FAILED: wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG "uuid=%s state=FAILED", uuid_str); break; } } static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, union wps_event_data *data) { struct wpa_supplicant *wpa_s = ctx; switch (event) { case WPS_EV_M2D: wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d); break; case WPS_EV_FAIL: wpa_supplicant_wps_event_fail(wpa_s, &data->fail); break; case WPS_EV_SUCCESS: wpa_supplicant_wps_event_success(wpa_s); break; case WPS_EV_PWD_AUTH_FAIL: #ifdef CONFIG_AP if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee) wpa_supplicant_ap_pwd_auth_fail(wpa_s); #endif /* CONFIG_AP */ break; case WPS_EV_PBC_OVERLAP: break; case WPS_EV_PBC_TIMEOUT: break; case WPS_EV_PBC_ACTIVE: wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE); break; case WPS_EV_PBC_DISABLE: wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE); break; case WPS_EV_ER_AP_ADD: wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap); break; case WPS_EV_ER_AP_REMOVE: wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap); break; case WPS_EV_ER_ENROLLEE_ADD: wpa_supplicant_wps_event_er_enrollee_add(wpa_s, &data->enrollee); break; case WPS_EV_ER_ENROLLEE_REMOVE: wpa_supplicant_wps_event_er_enrollee_remove(wpa_s, &data->enrollee); break; case WPS_EV_ER_AP_SETTINGS: wpa_supplicant_wps_event_er_ap_settings(wpa_s, &data->ap_settings); break; case WPS_EV_ER_SET_SELECTED_REGISTRAR: wpa_supplicant_wps_event_er_set_sel_reg(wpa_s, &data->set_sel_reg); break; case WPS_EV_AP_PIN_SUCCESS: break; } } static int wpa_supplicant_wps_rf_band(void *ctx) { struct wpa_supplicant *wpa_s = ctx; if (!wpa_s->current_ssid || !wpa_s->assoc_freq) return 0; return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ : (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; } enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) { if (eap_is_wps_pbc_enrollee(&ssid->eap) || eap_is_wps_pin_enrollee(&ssid->eap)) return WPS_REQ_ENROLLEE; else return WPS_REQ_REGISTRAR; } static void wpas_clear_wps(struct wpa_supplicant *wpa_s) { int id; struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current; wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; prev_current = wpa_s->current_ssid; /* Enable the networks disabled during wpas_wps_reassoc */ wpas_wps_reenable_networks(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); /* Remove any existing WPS network from configuration */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { if (ssid == wpa_s->current_ssid) { wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } id = ssid->id; remove_ssid = ssid; } else id = -1; ssid = ssid->next; if (id >= 0) { if (prev_current == remove_ssid) { wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); } wpas_notify_network_removed(wpa_s, remove_ssid); wpa_config_remove_network(wpa_s->conf, id); } } wpas_wps_clear_ap_info(wpa_s); } static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; union wps_event_data data; wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " "out"); os_memset(&data, 0, sizeof(data)); data.fail.config_error = WPS_CFG_MSG_TIMEOUT; data.fail.error_indication = WPS_EI_NO_ERROR; /* * Call wpas_notify_wps_event_fail() directly instead of through * wpa_supplicant_wps_event() which would end up registering unnecessary * timeouts (those are only for the case where the failure happens * during an EAP-WSC exchange). */ wpas_notify_wps_event_fail(wpa_s, &data.fail); wpas_clear_wps(wpa_s); } static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, int registrar, const u8 *dev_addr, const u8 *bssid) { struct wpa_ssid *ssid; ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return NULL; wpas_notify_network_added(wpa_s, ssid); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 || wpa_config_set(ssid, "eap", "WSC", 0) < 0 || wpa_config_set(ssid, "identity", registrar ? "\"" WSC_ID_REGISTRAR "\"" : "\"" WSC_ID_ENROLLEE "\"", 0) < 0) { wpas_notify_network_removed(wpa_s, ssid); wpa_config_remove_network(wpa_s->conf, ssid->id); return NULL; } #ifdef CONFIG_P2P if (dev_addr) os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN); #endif /* CONFIG_P2P */ if (bssid) { #ifndef CONFIG_P2P struct wpa_bss *bss; int count = 0; #endif /* CONFIG_P2P */ os_memcpy(ssid->bssid, bssid, ETH_ALEN); ssid->bssid_set = 1; /* * Note: With P2P, the SSID may change at the time the WPS * provisioning is started, so better not filter the AP based * on the current SSID in the scan results. */ #ifndef CONFIG_P2P dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0) continue; os_free(ssid->ssid); ssid->ssid = os_memdup(bss->ssid, bss->ssid_len); if (ssid->ssid == NULL) break; ssid->ssid_len = bss->ssid_len; wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from " "scan results", ssid->ssid, ssid->ssid_len); count++; } if (count > 1) { wpa_printf(MSG_DEBUG, "WPS: More than one SSID found " "for the AP; use wildcard"); os_free(ssid->ssid); ssid->ssid = NULL; ssid->ssid_len = 0; } #endif /* CONFIG_P2P */ } return ssid; } static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s, struct wpa_ssid *selected) { struct wpa_ssid *ssid; if (wpa_s->current_ssid) { wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } /* Mark all other networks disabled and trigger reassociation */ ssid = wpa_s->conf->ssid; while (ssid) { int was_disabled = ssid->disabled; ssid->disabled_for_connect = 0; /* * In case the network object corresponds to a persistent group * then do not send out network disabled signal. In addition, * do not change disabled status of persistent network objects * from 2 to 1 should we connect to another network. */ if (was_disabled != 2) { ssid->disabled = ssid != selected; if (was_disabled != ssid->disabled) { if (ssid->disabled) ssid->disabled_for_connect = 1; wpas_notify_network_enabled_changed(wpa_s, ssid); } } ssid = ssid->next; } } static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, struct wpa_ssid *selected, const u8 *bssid, int freq) { struct wpa_bss *bss; wpa_s->wps_run++; if (wpa_s->wps_run == 0) wpa_s->wps_run++; wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; if (freq) { wpa_s->after_wps = 5; wpa_s->wps_freq = freq; } else if (bssid) { bss = wpa_bss_get_bssid_latest(wpa_s, bssid); if (bss && bss->freq > 0) { wpa_s->known_wps_freq = 1; wpa_s->wps_freq = bss->freq; } } wpas_wps_temp_disable(wpa_s, selected); wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->scan_runs = 0; wpa_s->normal_scans = 0; wpa_s->wps_success = 0; wpa_s->bssid_ignore_cleared = false; wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, int p2p_group, int multi_ap_backhaul_sta) { struct wpa_ssid *ssid; char phase1[32]; #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_printf(MSG_DEBUG, "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); return -1; } #endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; ssid->p2p_group = p2p_group; /* * When starting a regular WPS process (not P2P group formation) * the registrar/final station can be either AP or PCP * so use a "don't care" value for the pbss flag. */ if (!p2p_group) ssid->pbss = 2; #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); if (ssid->ssid) { ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); if (wpa_s->go_params->freq > 56160) { /* P2P in 60 GHz uses PBSS */ ssid->pbss = 1; } if (wpa_s->go_params->edmg && wpas_p2p_try_edmg_channel(wpa_s, wpa_s->go_params) == 0) ssid->enable_edmg = 1; wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } } #endif /* CONFIG_P2P */ os_snprintf(phase1, sizeof(phase1), "pbc=1%s", multi_ap_backhaul_sta ? " multi_ap=1" : ""); if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0) return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; if (multi_ap_backhaul_sta) ssid->multi_ap_backhaul_sta = 1; wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *dev_addr, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id, const u8 *peer_pubkey_hash, const u8 *ssid_val, size_t ssid_len, int freq) { struct wpa_ssid *ssid; char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN]; unsigned int rpin = 0; char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10]; #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_printf(MSG_DEBUG, "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); return -1; } #endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); if (bssid && is_zero_ether_addr(bssid)) bssid = NULL; ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "WPS: Could not add network"); return -1; } ssid->temporary = 1; ssid->p2p_group = p2p_group; /* * When starting a regular WPS process (not P2P group formation) * the registrar/final station can be either AP or PCP * so use a "don't care" value for the pbss flag. */ if (!p2p_group) ssid->pbss = 2; if (ssid_val) { ssid->ssid = os_malloc(ssid_len); if (ssid->ssid) { os_memcpy(ssid->ssid, ssid_val, ssid_len); ssid->ssid_len = ssid_len; } } if (peer_pubkey_hash) { os_memcpy(hash, " pkhash=", 8); wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8, peer_pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); } else { hash[0] = '\0'; } #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { os_free(ssid->ssid); ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); if (ssid->ssid) { ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); if (wpa_s->go_params->freq > 56160) { /* P2P in 60 GHz uses PBSS */ ssid->pbss = 1; } if (wpa_s->go_params->edmg && wpas_p2p_try_edmg_channel(wpa_s, wpa_s->go_params) == 0) ssid->enable_edmg = 1; wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } } #endif /* CONFIG_P2P */ if (pin) os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"", pin, dev_pw_id, hash); else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", dev_pw_id, hash); } else { if (wps_generate_pin(&rpin) < 0) { wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN"); return -1; } os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"", rpin, dev_pw_id, hash); } if (wpa_config_set(ssid, "phase1", val, 0) < 0) { wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val); return -1; } if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_PIN_ACTIVE); if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpa_s->wps_ap_iter = 1; wpas_wps_reassoc(wpa_s, ssid, bssid, freq); return rpin; } int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id) { os_get_reltime(&wpa_s->wps_pin_start_time); return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group, dev_pw_id, NULL, NULL, 0, 0); } void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s) { union wps_event_data data; os_memset(&data, 0, sizeof(data)); data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; data.fail.error_indication = WPS_EI_NO_ERROR; /* * Call wpas_notify_wps_event_fail() directly instead of through * wpa_supplicant_wps_event() which would end up registering unnecessary * timeouts (those are only for the case where the failure happens * during an EAP-WSC exchange). */ wpas_notify_wps_event_fail(wpa_s, &data.fail); } /* Cancel the wps pbc/pin requests */ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode"); return wpa_supplicant_ap_wps_cancel(wpa_s); } #endif /* CONFIG_AP */ if (wpa_s->wpa_state == WPA_SCANNING || wpa_s->wpa_state == WPA_DISCONNECTED) { wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan"); wpa_supplicant_cancel_scan(wpa_s); wpas_clear_wps(wpa_s); } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) { wpa_printf(MSG_DEBUG, "WPS: Cancel operation - " "deauthenticate"); wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpas_clear_wps(wpa_s); } else { wpas_wps_reenable_networks(wpa_s); wpas_wps_clear_ap_info(wpa_s); if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) > 0) wpas_clear_wps(wpa_s); } wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CANCEL); wpa_s->after_wps = 0; return 0; } int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, struct wps_new_ap_settings *settings) { struct wpa_ssid *ssid; char val[200]; char *pos, *end; int res; #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_printf(MSG_DEBUG, "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); return -1; } #endif /* CONFIG_AP */ if (!pin) return -1; wpas_clear_wps(wpa_s); ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; pos = val; end = pos + sizeof(val); res = os_snprintf(pos, end - pos, "\"pin=%s", pin); if (os_snprintf_error(end - pos, res)) return -1; pos += res; if (settings) { res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s " "new_encr=%s new_key=%s", settings->ssid_hex, settings->auth, settings->encr, settings->key_hex); if (os_snprintf_error(end - pos, res)) return -1; pos += res; } res = os_snprintf(pos, end - pos, "\""); if (os_snprintf_error(end - pos, res)) return -1; if (wpa_config_set(ssid, "phase1", val, 0) < 0) return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { if (is_zero_ether_addr(p2p_dev_addr)) { wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, MAC2STR(mac_addr)); } else { wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " MACSTR " P2P Device Addr " MACSTR, MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); /* TODO */ return 0; } static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev) { char uuid[40], txt[400]; int len; char devtype[WPS_DEV_TYPE_BUFSIZE]; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid); len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR " [%s|%s|%s|%s|%s|%s]", uuid, MAC2STR(dev->mac_addr), dev->device_name, dev->manufacturer, dev->model_name, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); if (!os_snprintf_error(sizeof(txt), len)) wpa_printf(MSG_INFO, "%s", txt); } static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods) { #ifdef CONFIG_WPS_ER struct wpa_supplicant *wpa_s = ctx; if (wpa_s->wps_er == NULL) return; wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d " "dev_password_id=%u sel_reg_config_methods=0x%x", sel_reg, dev_passwd_id, sel_reg_config_methods); wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id, sel_reg_config_methods); #endif /* CONFIG_WPS_ER */ } static u16 wps_fix_config_methods(u16 config_methods) { if ((config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { wpa_printf(MSG_INFO, "WPS: Converting display to " "virtual_display for WPS 2.0 compliance"); config_methods |= WPS_CONFIG_VIRT_DISPLAY; } if ((config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { wpa_printf(MSG_INFO, "WPS: Converting push_button to " "virtual_push_button for WPS 2.0 compliance"); config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; } return config_methods; } static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s, struct wps_context *wps) { char buf[50]; const char *src; if (is_nil_uuid(wpa_s->conf->uuid)) { struct wpa_supplicant *first; first = wpa_s->global->ifaces; while (first && first->next) first = first->next; if (first && first != wpa_s) { if (wps != wpa_s->global->ifaces->wps) os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid, WPS_UUID_LEN); src = "from the first interface"; } else if (wpa_s->conf->auto_uuid == 1) { uuid_random(wps->uuid); src = "based on random data"; } else { uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); src = "based on MAC address"; } } else { os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); src = "based on configuration"; } uuid_bin2str(wps->uuid, buf, sizeof(buf)); wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf); } static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s, struct wps_context *wps) { wpabuf_free(wps->dev.vendor_ext_m1); wps->dev.vendor_ext_m1 = NULL; if (wpa_s->conf->wps_vendor_ext_m1) { wps->dev.vendor_ext_m1 = wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1); if (!wps->dev.vendor_ext_m1) { wpa_printf(MSG_ERROR, "WPS: Cannot " "allocate memory for vendor_ext_m1"); } } } int wpas_wps_init(struct wpa_supplicant *wpa_s) { struct wps_context *wps; struct wps_registrar_config rcfg; struct hostapd_hw_modes *modes; u16 m; wps = os_zalloc(sizeof(*wps)); if (wps == NULL) return -1; wps->cred_cb = wpa_supplicant_wps_cred; wps->event_cb = wpa_supplicant_wps_event; wps->rf_band_cb = wpa_supplicant_wps_rf_band; wps->cb_ctx = wpa_s; wps->dev.device_name = wpa_s->conf->device_name; wps->dev.manufacturer = wpa_s->conf->manufacturer; wps->dev.model_name = wpa_s->conf->model_name; wps->dev.model_number = wpa_s->conf->model_number; wps->dev.serial_number = wpa_s->conf->serial_number; wps->config_methods = wps_config_methods_str2bin(wpa_s->conf->config_methods); if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) == (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) { wpa_printf(MSG_ERROR, "WPS: Both Label and Display config " "methods are not allowed at the same time"); os_free(wps); return -1; } wps->config_methods = wps_fix_config_methods(wps->config_methods); wps->dev.config_methods = wps->config_methods; os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types); wpas_wps_set_vendor_ext_m1(wpa_s, wps); wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); modes = wpa_s->hw.modes; if (modes) { for (m = 0; m < wpa_s->hw.num_modes; m++) { if (modes[m].mode == HOSTAPD_MODE_IEEE80211B || modes[m].mode == HOSTAPD_MODE_IEEE80211G) wps->dev.rf_bands |= WPS_RF_24GHZ; else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A) wps->dev.rf_bands |= WPS_RF_50GHZ; else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD) wps->dev.rf_bands |= WPS_RF_60GHZ; } } if (wps->dev.rf_bands == 0) { /* * Default to claiming support for both bands if the driver * does not provide support for fetching supported bands. */ wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; } os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN); wpas_wps_set_uuid(wpa_s, wps); #ifdef CONFIG_NO_TKIP wps->auth_types = WPS_AUTH_WPA2PSK; wps->encr_types = WPS_ENCR_AES; #else /* CONFIG_NO_TKIP */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; #endif /* CONFIG_NO_TKIP */ os_memset(&rcfg, 0, sizeof(rcfg)); rcfg.new_psk_cb = wpas_wps_new_psk_cb; rcfg.pin_needed_cb = wpas_wps_pin_needed_cb; rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb; rcfg.cb_ctx = wpa_s; wps->registrar = wps_registrar_init(wps, &rcfg); if (wps->registrar == NULL) { wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar"); os_free(wps); return -1; } wpa_s->wps = wps; return 0; } #ifdef CONFIG_WPS_ER static void wpas_wps_nfc_clear(struct wps_context *wps) { wps->ap_nfc_dev_pw_id = 0; wpabuf_free(wps->ap_nfc_dh_pubkey); wps->ap_nfc_dh_pubkey = NULL; wpabuf_free(wps->ap_nfc_dh_privkey); wps->ap_nfc_dh_privkey = NULL; wpabuf_free(wps->ap_nfc_dev_pw); wps->ap_nfc_dev_pw = NULL; } #endif /* CONFIG_WPS_ER */ void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { wpas_wps_assoc_with_cred_cancel(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); wpas_wps_clear_ap_info(wpa_s); #ifdef CONFIG_P2P eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL); #endif /* CONFIG_P2P */ if (wpa_s->wps == NULL) return; #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; wpas_wps_nfc_clear(wpa_s->wps); #endif /* CONFIG_WPS_ER */ wps_registrar_deinit(wpa_s->wps->registrar); wpabuf_free(wpa_s->wps->dh_pubkey); wpabuf_free(wpa_s->wps->dh_privkey); wpabuf_free(wpa_s->wps->dev.vendor_ext_m1); os_free(wpa_s->wps->network_key); os_free(wpa_s->wps); wpa_s->wps = NULL; } int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss) { struct wpabuf *wps_ie; if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) return -1; wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (eap_is_wps_pbc_enrollee(&ssid->eap)) { if (!wps_ie) { wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); return 0; } if (!wps_is_selected_pbc_registrar(wps_ie)) { wpa_printf(MSG_DEBUG, " skip - WPS AP " "without active PBC Registrar"); wpabuf_free(wps_ie); return 0; } /* TODO: overlap detection */ wpa_printf(MSG_DEBUG, " selected based on WPS IE " "(Active PBC)"); wpabuf_free(wps_ie); return 1; } if (eap_is_wps_pin_enrollee(&ssid->eap)) { if (!wps_ie) { wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); return 0; } /* * Start with WPS APs that advertise our address as an * authorized MAC (v2.0) or active PIN Registrar (v1.0) and * allow any WPS AP after couple of scans since some APs do not * set Selected Registrar attribute properly when using * external Registrar. */ if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { struct os_reltime age; os_reltime_age(&wpa_s->wps_pin_start_time, &age); if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG || age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) { wpa_printf(MSG_DEBUG, " skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)", wpa_s->scan_runs, (int) age.sec); wpabuf_free(wps_ie); return 0; } wpa_printf(MSG_DEBUG, " selected based on WPS IE"); } else { wpa_printf(MSG_DEBUG, " selected based on WPS IE " "(Authorized MAC or Active PIN)"); } wpabuf_free(wps_ie); return 1; } if (wps_ie) { wpa_printf(MSG_DEBUG, " selected based on WPS IE"); wpabuf_free(wps_ie); return 1; } return -1; } int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss) { struct wpabuf *wps_ie = NULL; int ret = 0; if (eap_is_wps_pbc_enrollee(&ssid->eap)) { wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { /* allow wildcard SSID for WPS PBC */ ret = 1; } } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) || wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { /* allow wildcard SSID for WPS PIN */ ret = 1; } } if (!ret && ssid->bssid_set && os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) { /* allow wildcard SSID due to hardcoded BSSID match */ ret = 1; } #ifdef CONFIG_WPS_STRICT if (wps_ie) { if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len > 0, bss->bssid) < 0) ret = 0; if (bss->beacon_ie_len) { struct wpabuf *bcn_wps; bcn_wps = wpa_bss_get_vendor_ie_multi_beacon( bss, WPS_IE_VENDOR_TYPE); if (bcn_wps == NULL) { wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE " "missing from AP Beacon"); ret = 0; } else { if (wps_validate_beacon(wps_ie) < 0) ret = 0; wpabuf_free(bcn_wps); } } } #endif /* CONFIG_WPS_STRICT */ wpabuf_free(wps_ie); return ret; } int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { const u8 *sel_uuid; struct wpabuf *wps_ie; int ret = 0; size_t i; if (!eap_is_wps_pbc_enrollee(&ssid->eap)) return 0; wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is " "present in scan results; selected BSSID " MACSTR, MAC2STR(selected->bssid)); if (!is_zero_ether_addr(ssid->bssid)) wpa_printf(MSG_DEBUG, "WPS: Network profile limited to accept only a single BSSID " MACSTR, MAC2STR(ssid->bssid)); /* Make sure that only one AP is in active PBC mode */ wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE); if (wps_ie) { sel_uuid = wps_get_uuid_e(wps_ie); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS", sel_uuid, UUID_LEN); } else { wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include " "WPS IE?!"); sel_uuid = NULL; } for (i = 0; i < wpa_s->num_wps_ap; i++) { struct wps_ap_info *ap = &wpa_s->wps_ap[i]; if (!ap->pbc_active || os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0) continue; if (!is_zero_ether_addr(ssid->bssid) && os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR " in active PBC mode due to local BSSID limitation", MAC2STR(ap->bssid)); continue; } wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " MACSTR, MAC2STR(ap->bssid)); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", ap->uuid, UUID_LEN); if (sel_uuid == NULL || os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: " MACSTR " and " MACSTR, MAC2STR(selected->bssid), MAC2STR(ap->bssid)); break; } /* TODO: verify that this is reasonable dual-band situation */ } wpabuf_free(wps_ie); return ret; } void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; unsigned int pbc = 0, auth = 0, pin = 0, wps = 0; if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED) return; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { struct wpabuf *ie; ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (!ie) continue; if (wps_is_selected_pbc_registrar(ie)) pbc++; else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0)) auth++; else if (wps_is_selected_pin_registrar(ie)) pin++; else wps++; wpabuf_free(ie); } if (pbc) wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC); else if (auth) wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH); else if (pin) wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN); else if (wps) wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE); } int wpas_wps_searching(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled) return 1; } return 0; } int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) { struct wpabuf *wps_ie; int ret; wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA); if (wps_ie == NULL) return 0; ret = wps_attr_text(wps_ie, buf, end); wpabuf_free(wps_ie); return ret; } int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter) { #ifdef CONFIG_WPS_ER if (wpa_s->wps_er) { wps_er_refresh(wpa_s->wps_er); return 0; } wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter); if (wpa_s->wps_er == NULL) return -1; return 0; #else /* CONFIG_WPS_ER */ return 0; #endif /* CONFIG_WPS_ER */ } void wpas_wps_er_stop(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; #endif /* CONFIG_WPS_ER */ } #ifdef CONFIG_WPS_ER int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, const char *uuid, const char *pin) { u8 u[UUID_LEN]; const u8 *use_uuid = NULL; u8 addr_buf[ETH_ALEN]; if (os_strcmp(uuid, "any") == 0) { } else if (uuid_str2bin(uuid, u) == 0) { use_uuid = u; } else if (hwaddr_aton(uuid, addr_buf) == 0) { use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf); if (use_uuid == NULL) return -1; } else return -1; return wps_registrar_add_pin(wpa_s->wps->registrar, addr, use_uuid, (const u8 *) pin, os_strlen(pin), 300); } int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid) { u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; else if (hwaddr_aton(uuid, addr) == 0) use_addr = addr; else return -1; return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr); } int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin) { u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; else if (hwaddr_aton(uuid, addr) == 0) use_addr = addr; else return -1; return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin, os_strlen(pin)); } static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, struct wps_credential *cred) { os_memset(cred, 0, sizeof(*cred)); if (ssid->ssid_len > SSID_MAX_LEN) return -1; os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len); cred->ssid_len = ssid->ssid_len; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ? WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK; if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) cred->encr_type = WPS_ENCR_AES; else cred->encr_type = WPS_ENCR_TKIP; if (ssid->passphrase) { cred->key_len = os_strlen(ssid->passphrase); if (cred->key_len >= 64) return -1; os_memcpy(cred->key, ssid->passphrase, cred->key_len); } else if (ssid->psk_set) { cred->key_len = 32; os_memcpy(cred->key, ssid->psk, 32); } else return -1; } else { cred->auth_type = WPS_AUTH_OPEN; cred->encr_type = WPS_ENCR_NONE; } return 0; } int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, int id) { u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; struct wpa_ssid *ssid; struct wps_credential cred; int ret; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; else if (hwaddr_aton(uuid, addr) == 0) use_addr = addr; else return -1; ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->ssid == NULL) return -1; if (wpas_wps_network_to_cred(ssid, &cred) < 0) return -1; ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); os_memset(&cred, 0, sizeof(cred)); return ret; } int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin, struct wps_new_ap_settings *settings) { u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; struct wps_credential cred; size_t len; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; else if (hwaddr_aton(uuid, addr) == 0) use_addr = addr; else return -1; if (settings->ssid_hex == NULL || settings->auth == NULL || settings->encr == NULL || settings->key_hex == NULL) return -1; os_memset(&cred, 0, sizeof(cred)); len = os_strlen(settings->ssid_hex); if ((len & 1) || len > 2 * sizeof(cred.ssid) || hexstr2bin(settings->ssid_hex, cred.ssid, len / 2)) return -1; cred.ssid_len = len / 2; len = os_strlen(settings->key_hex); if ((len & 1) || len > 2 * sizeof(cred.key) || hexstr2bin(settings->key_hex, cred.key, len / 2)) return -1; cred.key_len = len / 2; if (os_strcmp(settings->auth, "OPEN") == 0) cred.auth_type = WPS_AUTH_OPEN; else if (os_strcmp(settings->auth, "WPAPSK") == 0) cred.auth_type = WPS_AUTH_WPAPSK; else if (os_strcmp(settings->auth, "WPA2PSK") == 0) cred.auth_type = WPS_AUTH_WPA2PSK; else return -1; if (os_strcmp(settings->encr, "NONE") == 0) cred.encr_type = WPS_ENCR_NONE; #ifdef CONFIG_TESTING_OPTIONS else if (os_strcmp(settings->encr, "WEP") == 0) cred.encr_type = WPS_ENCR_WEP; #endif /* CONFIG_TESTING_OPTIONS */ else if (os_strcmp(settings->encr, "TKIP") == 0) cred.encr_type = WPS_ENCR_TKIP; else if (os_strcmp(settings->encr, "CCMP") == 0) cred.encr_type = WPS_ENCR_AES; else return -1; return wps_er_config(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin, os_strlen(pin), &cred); } #ifdef CONFIG_WPS_NFC struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef, const char *uuid) { struct wpabuf *ret; u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; if (!wpa_s->wps_er) return NULL; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; else if (hwaddr_aton(uuid, addr) == 0) use_addr = addr; else return NULL; ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } #endif /* CONFIG_WPS_NFC */ static int callbacks_pending = 0; static void wpas_wps_terminate_cb(void *ctx) { wpa_printf(MSG_DEBUG, "WPS ER: Terminated"); if (--callbacks_pending <= 0) eloop_terminate(); } #endif /* CONFIG_WPS_ER */ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS_ER if (wpa_s->wps_er) { callbacks_pending++; wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s); wpa_s->wps_er = NULL; return 1; } #endif /* CONFIG_WPS_ER */ return 0; } void wpas_wps_update_config(struct wpa_supplicant *wpa_s) { struct wps_context *wps = wpa_s->wps; if (wps == NULL) return; if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) { wps->config_methods = wps_config_methods_str2bin( wpa_s->conf->config_methods); if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) == (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) { wpa_printf(MSG_ERROR, "WPS: Both Label and Display " "config methods are not allowed at the " "same time"); wps->config_methods &= ~WPS_CONFIG_LABEL; } } wps->config_methods = wps_fix_config_methods(wps->config_methods); wps->dev.config_methods = wps->config_methods; if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) { wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN); } if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) wpas_wps_set_vendor_ext_m1(wpa_s, wps); if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION) wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID) wpas_wps_set_uuid(wpa_s, wps); if (wpa_s->conf->changed_parameters & (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) { /* Update pointers to make sure they refer current values */ wps->dev.device_name = wpa_s->conf->device_name; wps->dev.manufacturer = wpa_s->conf->manufacturer; wps->dev.model_name = wpa_s->conf->model_name; wps->dev.model_number = wpa_s->conf->model_number; wps->dev.serial_number = wpa_s->conf->serial_number; } } void wpas_wps_update_mac_addr(struct wpa_supplicant *wpa_s) { struct wps_context *wps; wps = wpa_s->wps; if (wps) os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN); } #ifdef CONFIG_WPS_NFC #ifdef CONFIG_WPS_ER static struct wpabuf * wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef, struct wpa_ssid *ssid) { struct wpabuf *ret; struct wps_credential cred; if (wpas_wps_network_to_cred(ssid, &cred) < 0) return NULL; ret = wps_er_config_token_from_cred(wpa_s->wps, &cred); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } #endif /* CONFIG_WPS_ER */ struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef, const char *id_str) { #ifdef CONFIG_WPS_ER if (id_str) { int id; char *end = NULL; struct wpa_ssid *ssid; id = strtol(id_str, &end, 10); if (end && *end) return NULL; ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) return NULL; return wpas_wps_network_config_token(wpa_s, ndef, ssid); } #endif /* CONFIG_WPS_ER */ #ifdef CONFIG_AP if (wpa_s->ap_iface) return wpas_ap_wps_nfc_config_token(wpa_s, ndef); #endif /* CONFIG_AP */ return NULL; } struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) { if (wpa_s->conf->wps_nfc_pw_from_config) { return wps_nfc_token_build(ndef, wpa_s->conf->wps_nfc_dev_pw_id, wpa_s->conf->wps_nfc_dh_pubkey, wpa_s->conf->wps_nfc_dev_pw); } return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id, &wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey, &wpa_s->conf->wps_nfc_dev_pw); } int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr, const u8 *bssid, const struct wpabuf *dev_pw, u16 dev_pw_id, int p2p_group, const u8 *peer_pubkey_hash, const u8 *ssid, size_t ssid_len, int freq) { struct wps_context *wps = wpa_s->wps; char pw[32 * 2 + 1]; if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { dev_pw = wpa_s->conf->wps_nfc_dev_pw; dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; } if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || wpa_s->conf->wps_nfc_dh_privkey == NULL) { wpa_printf(MSG_DEBUG, "WPS: Missing DH params - " "cannot start NFC-triggered connection"); return -1; } if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - " "cannot start NFC-triggered connection", dev_pw_id); return -1; } dh5_free(wps->dh_ctx); wpabuf_free(wps->dh_pubkey); wpabuf_free(wps->dh_privkey); wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { wps->dh_ctx = NULL; wpabuf_free(wps->dh_pubkey); wps->dh_pubkey = NULL; wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key"); return -1; } wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); if (wps->dh_ctx == NULL) { wpabuf_free(wps->dh_pubkey); wps->dh_pubkey = NULL; wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context"); return -1; } if (dev_pw) { wpa_snprintf_hex_uppercase(pw, sizeof(pw), wpabuf_head(dev_pw), wpabuf_len(dev_pw)); } return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid, dev_pw ? pw : NULL, p2p_group, dev_pw_id, peer_pubkey_hash, ssid, ssid_len, freq); } static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, struct wps_parse_attr *attr) { /* * Disable existing networks temporarily to allow the newly learned * credential to be preferred. Enable the temporarily disabled networks * after 10 seconds. */ wpas_wps_temp_disable(wpa_s, NULL); eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, NULL); if (wps_oob_use_cred(wpa_s->wps, attr) < 0) return -1; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return 0; if (attr->ap_channel) { u16 chan = WPA_GET_BE16(attr->ap_channel); int freq = 0; if (chan >= 1 && chan <= 13) freq = 2407 + 5 * chan; else if (chan == 14) freq = 2484; else if (chan >= 30) freq = 5000 + 5 * chan; if (freq) { wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz", chan, freq); wpa_s->after_wps = 5; wpa_s->wps_freq = freq; } } wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " "based on the received credential added"); wpa_s->normal_scans = 0; wpa_supplicant_reinit_autoscan(wpa_s); wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; } #ifdef CONFIG_WPS_ER static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s, struct wps_parse_attr *attr) { return wps_registrar_add_nfc_password_token( wpa_s->wps->registrar, attr->oob_dev_password, attr->oob_dev_password_len); } #endif /* CONFIG_WPS_ER */ static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s, const struct wpabuf *wps) { struct wps_parse_attr attr; wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); if (wps_parse_msg(wps, &attr)) { wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); return -1; } if (attr.num_cred) return wpas_wps_use_cred(wpa_s, &attr); #ifdef CONFIG_WPS_ER if (attr.oob_dev_password) return wpas_wps_add_nfc_password_token(wpa_s, &attr); #endif /* CONFIG_WPS_ER */ wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); return -1; } int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, const struct wpabuf *data, int forced_freq) { const struct wpabuf *wps = data; struct wpabuf *tmp = NULL; int ret; if (wpabuf_len(data) < 4) return -1; if (*wpabuf_head_u8(data) != 0x10) { /* Assume this contains full NDEF record */ tmp = ndef_parse_wifi(data); if (tmp == NULL) { #ifdef CONFIG_P2P tmp = ndef_parse_p2p(data); if (tmp) { ret = wpas_p2p_nfc_tag_process(wpa_s, tmp, forced_freq); wpabuf_free(tmp); return ret; } #endif /* CONFIG_P2P */ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); return -1; } wps = tmp; } ret = wpas_wps_nfc_tag_process(wpa_s, wps); wpabuf_free(tmp); return ret; } struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int ndef) { struct wpabuf *ret; if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey) < 0) return NULL; ret = wps_build_nfc_handover_req(wpa_s->wps, wpa_s->conf->wps_nfc_dh_pubkey); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; } #ifdef CONFIG_WPS_NFC static struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, const char *uuid) { #ifdef CONFIG_WPS_ER struct wpabuf *ret; u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; struct wps_context *wps = wpa_s->wps; if (wps == NULL) return NULL; if (uuid == NULL) return NULL; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; else if (hwaddr_aton(uuid, addr) == 0) use_addr = addr; else return NULL; if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) { if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey) < 0) return NULL; } wpas_wps_nfc_clear(wps); wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { wpas_wps_nfc_clear(wps); return NULL; } ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid, use_addr, wpa_s->conf->wps_nfc_dh_pubkey); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); wpabuf_free(ret); if (tmp == NULL) return NULL; ret = tmp; } return ret; #else /* CONFIG_WPS_ER */ return NULL; #endif /* CONFIG_WPS_ER */ } #endif /* CONFIG_WPS_NFC */ struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, int cr, const char *uuid) { struct wpabuf *ret; if (!cr) return NULL; ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef); if (ret) return ret; return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid); } static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, const struct wpabuf *data) { struct wpabuf *wps; int ret = -1; u16 wsc_len; const u8 *pos; struct wpabuf msg; struct wps_parse_attr attr; u16 dev_pw_id; const u8 *bssid = NULL; int freq = 0; wps = ndef_parse_wifi(data); if (wps == NULL) return -1; wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " "payload from NFC connection handover"); wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); if (wpabuf_len(wps) < 2) { wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select " "Message"); goto out; } pos = wpabuf_head(wps); wsc_len = WPA_GET_BE16(pos); if (wsc_len > wpabuf_len(wps) - 2) { wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " "in Wi-Fi Handover Select Message", wsc_len); goto out; } pos += 2; wpa_hexdump(MSG_DEBUG, "WPS: WSC attributes in Wi-Fi Handover Select Message", pos, wsc_len); if (wsc_len < wpabuf_len(wps) - 2) { wpa_hexdump(MSG_DEBUG, "WPS: Ignore extra data after WSC attributes", pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); } wpabuf_set(&msg, pos, wsc_len); ret = wps_parse_msg(&msg, &attr); if (ret < 0) { wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " "Wi-Fi Handover Select Message"); goto out; } if (attr.oob_dev_password == NULL || attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " "included in Wi-Fi Handover Select Message"); ret = -1; goto out; } if (attr.ssid == NULL) { wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover " "Select Message"); ret = -1; goto out; } wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len); if (attr.mac_addr) { bssid = attr.mac_addr; wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR, MAC2STR(bssid)); } if (attr.rf_bands) wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands); if (attr.ap_channel) { u16 chan = WPA_GET_BE16(attr.ap_channel); wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan); if (chan >= 1 && chan <= 13 && (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ)) freq = 2407 + 5 * chan; else if (chan == 14 && (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ)) freq = 2484; else if (chan >= 30 && (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_50GHZ)) freq = 5000 + 5 * chan; else if (chan >= 1 && chan <= 6 && (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_60GHZ)) freq = 56160 + 2160 * chan; if (freq) { wpa_printf(MSG_DEBUG, "WPS: AP indicated channel %u -> %u MHz", chan, freq); } } wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", attr.oob_dev_password, attr.oob_dev_password_len); dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + WPS_OOB_PUBKEY_HASH_LEN); if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " "%u in Wi-Fi Handover Select Message", dev_pw_id); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash", attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0, attr.oob_dev_password, attr.ssid, attr.ssid_len, freq); out: wpabuf_free(wps); return ret; } int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, const struct wpabuf *req, const struct wpabuf *sel) { wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported"); wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req); wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel); return wpas_wps_nfc_rx_handover_sel(wpa_s, sel); } int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, const struct wpabuf *req, const struct wpabuf *sel) { struct wpabuf *wps; int ret = -1; u16 wsc_len; const u8 *pos; struct wpabuf msg; struct wps_parse_attr attr; u16 dev_pw_id; /* * Enrollee/station is always initiator of the NFC connection handover, * so use the request message here to find Enrollee public key hash. */ wps = ndef_parse_wifi(req); if (wps == NULL) return -1; wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " "payload from NFC connection handover"); wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); if (wpabuf_len(wps) < 2) { wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " "Message"); goto out; } pos = wpabuf_head(wps); wsc_len = WPA_GET_BE16(pos); if (wsc_len > wpabuf_len(wps) - 2) { wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " "in rt Wi-Fi Handover Request Message", wsc_len); goto out; } pos += 2; wpa_hexdump(MSG_DEBUG, "WPS: WSC attributes in Wi-Fi Handover Request Message", pos, wsc_len); if (wsc_len < wpabuf_len(wps) - 2) { wpa_hexdump(MSG_DEBUG, "WPS: Ignore extra data after WSC attributes", pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); } wpabuf_set(&msg, pos, wsc_len); ret = wps_parse_msg(&msg, &attr); if (ret < 0) { wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " "Wi-Fi Handover Request Message"); goto out; } if (attr.oob_dev_password == NULL || attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " "included in Wi-Fi Handover Request Message"); ret = -1; goto out; } if (attr.uuid_e == NULL) { wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " "Handover Request Message"); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", attr.oob_dev_password, attr.oob_dev_password_len); dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + WPS_OOB_PUBKEY_HASH_LEN); if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " "%u in Wi-Fi Handover Request Message", dev_pw_id); ret = -1; goto out; } wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar, attr.oob_dev_password, DEV_PW_NFC_CONNECTION_HANDOVER, NULL, 0, 1); out: wpabuf_free(wps); return ret; } #endif /* CONFIG_WPS_NFC */ static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) { size_t i; struct os_reltime now; if (wpa_debug_level > MSG_DEBUG) return; if (wpa_s->wps_ap == NULL) return; os_get_reltime(&now); for (i = 0; i < wpa_s->num_wps_ap; i++) { struct wps_ap_info *ap = &wpa_s->wps_ap[i]; struct wpa_bssid_ignore *e = wpa_bssid_ignore_get(wpa_s, ap->bssid); wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d " "tries=%d last_attempt=%d sec ago bssid_ignore=%d", (int) i, MAC2STR(ap->bssid), ap->type, ap->tries, ap->last_attempt.sec > 0 ? (int) now.sec - (int) ap->last_attempt.sec : -1, e ? e->count : 0); } } static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s, const u8 *bssid) { size_t i; if (wpa_s->wps_ap == NULL) return NULL; for (i = 0; i < wpa_s->num_wps_ap; i++) { struct wps_ap_info *ap = &wpa_s->wps_ap[i]; if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0) return ap; } return NULL; } static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res) { struct wpabuf *wps; enum wps_ap_info_type type; struct wps_ap_info *ap; int r, pbc_active; const u8 *uuid; if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) return; wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); if (wps == NULL) return; r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1); if (r == 2) type = WPS_AP_SEL_REG_OUR; else if (r == 1) type = WPS_AP_SEL_REG; else type = WPS_AP_NOT_SEL_REG; uuid = wps_get_uuid_e(wps); pbc_active = wps_is_selected_pbc_registrar(wps); ap = wpas_wps_get_ap_info(wpa_s, res->bssid); if (ap) { if (ap->type != type) { wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " changed type %d -> %d", MAC2STR(res->bssid), ap->type, type); ap->type = type; if (type != WPS_AP_NOT_SEL_REG) wpa_bssid_ignore_del(wpa_s, ap->bssid); } ap->pbc_active = pbc_active; if (uuid) os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); goto out; } ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, sizeof(struct wps_ap_info)); if (ap == NULL) goto out; wpa_s->wps_ap = ap; ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; wpa_s->num_wps_ap++; os_memset(ap, 0, sizeof(*ap)); os_memcpy(ap->bssid, res->bssid, ETH_ALEN); ap->type = type; ap->pbc_active = pbc_active; if (uuid) os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", MAC2STR(ap->bssid), ap->type); out: wpabuf_free(wps); } void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { size_t i; for (i = 0; i < scan_res->num; i++) wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]); wpas_wps_dump_ap_info(wpa_s); } void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wps_ap_info *ap; wpa_s->after_wps = 0; if (!wpa_s->wps_ap_iter) return; ap = wpas_wps_get_ap_info(wpa_s, bssid); if (ap == NULL) return; ap->tries++; os_get_reltime(&ap->last_attempt); }