Merge branch 'feature/add_dpp_support' into 'master'

Add dpp support

Closes WIFI-1054 and WIFI-2627

See merge request espressif/esp-idf!8167
This commit is contained in:
Jiang Jiang Jian
2021-01-25 20:09:31 +08:00
30 changed files with 3101 additions and 131 deletions

View File

@@ -44,6 +44,9 @@
#if __has_include("esp_spi_flash.h")
#include "esp_spi_flash.h"
#endif
#if __has_include("esp_supplicant/esp_dpp.h")
#include "esp_supplicant/esp_dpp.h"
#endif
#if __has_include("esp_supplicant/esp_wps.h")
#include "esp_supplicant/esp_wps.h"
#endif
@@ -407,6 +410,16 @@ static const esp_err_msg_t esp_err_msg_table[] = {
# endif
# ifdef ESP_ERR_ESPNOW_IF
ERR_TBL_IT(ESP_ERR_ESPNOW_IF), /* 12396 0x306c Interface error */
# endif
// components/wpa_supplicant/include/esp_supplicant/esp_dpp.h
# ifdef ESP_ERR_DPP_FAILURE
ERR_TBL_IT(ESP_ERR_DPP_FAILURE), /* 12439 0x3097 Generic failure during DPP Operation */
# endif
# ifdef ESP_ERR_DPP_TX_FAILURE
ERR_TBL_IT(ESP_ERR_DPP_TX_FAILURE), /* 12440 0x3098 DPP Frame Tx failed OR not Acked */
# endif
# ifdef ESP_ERR_DPP_INVALID_ATTR
ERR_TBL_IT(ESP_ERR_DPP_INVALID_ATTR), /* 12441 0x3099 Encountered invalid DPP Attribute */
# endif
// components/esp_common/include/esp_err.h
# ifdef ESP_ERR_MESH_BASE

View File

@@ -85,6 +85,12 @@ static system_event_id_t esp_event_legacy_wifi_event_id(int32_t event_id)
case WIFI_EVENT_AP_PROBEREQRECVED:
return SYSTEM_EVENT_AP_PROBEREQRECVED;
case WIFI_EVENT_ACTION_TX_STATUS:
return SYSTEM_EVENT_ACTION_TX_STATUS;
case WIFI_EVENT_ROC_DONE:
return SYSTEM_EVENT_ROC_DONE;
default:
ESP_LOGE(TAG, "invalid wifi event id %d", event_id);
return SYSTEM_EVENT_MAX;

View File

@@ -48,6 +48,8 @@ typedef enum {
SYSTEM_EVENT_AP_STADISCONNECTED, /*!< a station disconnected from ESP32 soft-AP */
SYSTEM_EVENT_AP_STAIPASSIGNED, /*!< ESP32 soft-AP assign an IP to a connected station */
SYSTEM_EVENT_AP_PROBEREQRECVED, /*!< Receive probe request packet in soft-AP interface */
SYSTEM_EVENT_ACTION_TX_STATUS, /*!< Receive status of Action frame transmitted */
SYSTEM_EVENT_ROC_DONE, /*!< Indicates the completion of Remain-on-Channel operation status */
SYSTEM_EVENT_GOT_IP6, /*!< ESP32 station or ap or ethernet interface v6IP addr is preferred */
SYSTEM_EVENT_ETH_START, /*!< ESP32 ethernet start */
SYSTEM_EVENT_ETH_STOP, /*!< ESP32 ethernet stop */

View File

@@ -35,6 +35,12 @@ typedef enum {
WIFI_IF_AP = ESP_IF_WIFI_AP,
} wifi_interface_t;
#define WIFI_OFFCHAN_TX_REQ 1
#define WIFI_OFFCHAN_TX_CANCEL 0
#define WIFI_ROC_REQ 1
#define WIFI_ROC_CANCEL 0
typedef enum {
WIFI_COUNTRY_POLICY_AUTO, /**< Country policy is auto, use the country info of AP to which the station is connected */
WIFI_COUNTRY_POLICY_MANUAL, /**< Country policy is manual, always use the configured country info */
@@ -483,6 +489,32 @@ typedef struct {
enabled_ant1: 4; /**< Index (in antenna GPIO configuration) of enabled WIFI_ANT_MODE_ANT1 */
} wifi_ant_config_t;
/**
* @brief The Rx callback function of Action Tx operations
*
* @param hdr pointer to the IEEE 802.11 Header structure
* @param payload pointer to the Payload following 802.11 Header
* @param len length of the Payload
* @param channel channel number the frame is received on
*
*/
typedef int (* wifi_action_rx_cb_t)(uint8_t *hdr, uint8_t *payload,
size_t len, uint8_t channel);
/**
* @brief Action Frame Tx Request
*
*
*/
typedef struct {
wifi_interface_t ifx; /**< WiFi interface to send request to */
uint8_t dest_mac[6]; /**< Destination MAC address */
bool no_ack; /**< Indicates no ack required */
wifi_action_rx_cb_t rx_cb; /**< Rx Callback to receive any response */
uint32_t data_len; /**< Length of the appended Data */
uint8_t data[0]; /**< Appended Data payload */
} wifi_action_tx_req_t;
/**
* @brief WiFi PHY rate encodings
*
@@ -551,6 +583,8 @@ typedef enum {
/* Add next events after this only */
WIFI_EVENT_STA_BSS_RSSI_LOW, /**< AP's RSSI crossed configured threshold */
WIFI_EVENT_ACTION_TX_STATUS, /**< Status indication of Action Tx operation */
WIFI_EVENT_ROC_DONE, /**< Remain-on-Channel operation complete */
WIFI_EVENT_MAX, /**< Invalid WiFi event ID */
} wifi_event_t;
@@ -645,6 +679,19 @@ typedef struct {
#define WIFI_STATIS_PS (1<<4)
#define WIFI_STATIS_ALL (-1)
/** Argument structure for WIFI_EVENT_ACTION_TX_STATUS event */
typedef struct {
wifi_interface_t ifx; /**< WiFi interface to send request to */
uint32_t context; /**< Context to identify the request */
uint8_t da[6]; /**< Destination MAC address */
uint8_t status; /**< Status of the operation */
} wifi_event_action_tx_status_t;
/** Argument structure for WIFI_EVENT_ROC_DONE event */
typedef struct {
uint32_t context; /**< Context to identify the request */
} wifi_event_roc_done_t;
#ifdef __cplusplus
}
#endif

View File

@@ -18,7 +18,7 @@ set(srcs "port/os_xtensa.c"
"src/crypto/aes-omac1.c"
"src/crypto/aes-unwrap.c"
"src/crypto/aes-wrap.c"
"src/crypto/aes-omac1.c"
"src/crypto/sha256-tlsprf.c"
"src/crypto/bignum.c"
"src/crypto/ccmp.c"
"src/crypto/crypto_mbedtls.c"
@@ -63,6 +63,7 @@ set(srcs "port/os_xtensa.c"
"src/esp_supplicant/esp_wpas_glue.c"
"src/esp_supplicant/esp_wps.c"
"src/esp_supplicant/esp_wpa3.c"
"src/esp_supplicant/esp_dpp.c"
"src/rsn_supp/pmksa_cache.c"
"src/rsn_supp/wpa.c"
"src/rsn_supp/wpa_ie.c"

View File

@@ -0,0 +1,116 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_DPP_H
#define ESP_DPP_H
#include <stdbool.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_ERR_DPP_FAILURE (ESP_ERR_WIFI_BASE + 151) /*!< Generic failure during DPP Operation */
#define ESP_ERR_DPP_TX_FAILURE (ESP_ERR_WIFI_BASE + 152) /*!< DPP Frame Tx failed OR not Acked */
#define ESP_ERR_DPP_INVALID_ATTR (ESP_ERR_WIFI_BASE + 153) /*!< Encountered invalid DPP Attribute */
/** @brief Types of Bootstrap Methods for DPP. */
typedef enum dpp_bootstrap_type {
DPP_BOOTSTRAP_QR_CODE, /**< QR Code Method */
DPP_BOOTSTRAP_PKEX, /**< Proof of Knowledge Method */
DPP_BOOTSTRAP_NFC_URI, /**< NFC URI record Method */
} esp_supp_dpp_bootstrap_t;
/** @brief Types of Callback Events received from DPP Supplicant. */
typedef enum {
ESP_SUPP_DPP_URI_READY, /**< URI is ready through Bootstrapping */
ESP_SUPP_DPP_CFG_RECVD, /**< Config received via DPP Authentication */
ESP_SUPP_DPP_FAIL, /**< DPP Authentication failure */
} esp_supp_dpp_event_t;
/**
* @brief Callback function for receiving DPP Events from Supplicant.
*
* Callback function will be called with DPP related information.
*
* @param evt DPP event ID
* @param data Event data payload
*/
typedef void (*esp_supp_dpp_event_cb_t)(esp_supp_dpp_event_t evt, void *data);
/**
* @brief Initialize DPP Supplicant
*
* Starts DPP Supplicant and initializes related Data Structures.
*
* @param evt_cb Callback function to receive DPP related events
*
* return
* - ESP_OK: Success
* - ESP_FAIL: Failure
*/
esp_err_t esp_supp_dpp_init(esp_supp_dpp_event_cb_t evt_cb);
/**
* @brief De-initalize DPP Supplicant
*
* Frees memory from DPP Supplicant Data Structures.
*/
void esp_supp_dpp_deinit(void);
/**
* @brief Generates Bootstrap Information as an Enrollee.
*
* Generates Out Of Band Bootstrap information as an Enrollee which can be
* used by a DPP Configurator to provision the Enrollee.
*
* @param chan_list List of channels device will be available on for listening
* @param type Bootstrap method type, only QR Code method is supported for now.
* @param key (Optional) Private Key used to generate a Bootstrapping Public Key
* @param info (Optional) Ancilliary Device Information like Serial Number
*
* @return
* - ESP_OK: Success
* - ESP_FAIL: Failure
*/
esp_err_t
esp_supp_dpp_bootstrap_gen(const char *chan_list, esp_supp_dpp_bootstrap_t type,
const char *key, const char *info);
/**
* @brief Start listening on Channels provided during esp_supp_dpp_bootstrap_gen.
*
* Listens on every Channel from Channel List for a pre-defined wait time.
*
* @return
* - ESP_OK: Success
* - ESP_FAIL: Generic Failure
* - ESP_ERR_INVALID_STATE: ROC attempted before WiFi is started
* - ESP_ERR_NO_MEM: Memory allocation failed while posting ROC request
*/
esp_err_t esp_supp_dpp_start_listen(void);
/**
* @brief Stop listening on Channels.
*
* Stops listening on Channels and cancels ongoing listen operation.
*/
void esp_supp_dpp_stop_listen(void);
#ifdef __cplusplus
}
#endif
#endif /* ESP_DPP_H */

View File

@@ -2397,7 +2397,7 @@ struct dpp_authentication *
dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
unsigned int freq, const u8 *hdr, const u8 *attr_start,
unsigned int curr_chan, const u8 *hdr, const u8 *attr_start,
size_t attr_len)
{
struct crypto_key *pi = NULL;
@@ -2406,10 +2406,16 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
*channel;
u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
i_bootstrap_len, channel_len;
const u8 *wrapped_data;
const u8 *i_proto;
const u8 *i_nonce;
const u8 *i_capab;
const u8 *i_bootstrap;
u16 wrapped_data_len;
u16 i_proto_len;
u16 i_nonce_len;
u16 i_capab_len;
u16 i_bootstrap_len;
struct dpp_authentication *auth = NULL;
#ifdef CONFIG_WPA_TESTING_OPTIONS
@@ -2438,10 +2444,11 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
auth->peer_bi = peer_bi;
auth->own_bi = own_bi;
auth->curve = own_bi->curve;
auth->curr_freq = freq;
auth->curr_chan = curr_chan;
auth->peer_version = 1; /* default to the first version */
#if 0
channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
&channel_len);
if (channel) {
@@ -2452,7 +2459,6 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
goto fail;
}
#ifndef ESP_SUPPLICANT
neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
wpa_printf(MSG_DEBUG,
"DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
@@ -2469,10 +2475,10 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
freq, neg_freq);
auth->curr_freq = neg_freq;
}
#endif
/* rename it to chan */
auth->curr_freq = *channel;
auth->curr_chan = *channel;
}
#endif
i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
&i_proto_len);
@@ -5386,7 +5392,7 @@ fail:
int dpp_conf_resp_rx(struct dpp_authentication *auth,
const struct wpabuf *resp)
const uint8_t *resp, uint32_t resp_len)
{
const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
@@ -5398,12 +5404,12 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
auth->conf_resp_status = 255;
if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
if (dpp_check_attrs(resp, resp_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in config response");
return -1;
}
wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
wrapped_data = dpp_get_attr(resp, resp_len,
DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
@@ -5419,8 +5425,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
if (!unwrapped)
return -1;
addr[0] = wpabuf_head(resp);
len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
addr[0] = resp;
len[0] = wrapped_data - 4 - resp;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
@@ -5451,7 +5457,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
goto fail;
}
status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
status = dpp_get_attr(resp, resp_len,
DPP_ATTR_STATUS, &status_len);
if (!status || status_len < 1) {
dpp_auth_fail(auth,
@@ -6083,9 +6089,6 @@ int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
int ret = -1;
struct dpp_bootstrap_info *bi;
if (!dpp)
return -1;
bi = os_zalloc(sizeof(*bi));
if (!bi)
goto fail;
@@ -6143,6 +6146,7 @@ int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
info ? "I:" : "", info ? info : "", info ? ";" : "",
pk);
bi->id = dpp_next_id(dpp);
dl_list_add(&dpp->bootstrap, &bi->list);
ret = bi->id;

View File

@@ -13,8 +13,11 @@
#ifdef CONFIG_DPP
#include "utils/list.h"
#include "common/wpa_common.h"
#include "crypto/sha256.h"
#include "utils/includes.h"
#include "utils/common.h"
#include "esp_err.h"
#include "esp_dpp.h"
struct crypto_ecdh;
struct hostapd_ip_addr;
@@ -147,12 +150,6 @@ struct dpp_curve_params {
const char *jws_alg;
};
enum dpp_bootstrap_type {
DPP_BOOTSTRAP_QR_CODE,
DPP_BOOTSTRAP_PKEX,
DPP_BOOTSTRAP_NFC_URI,
};
struct dpp_bootstrap_info {
struct dl_list list;
unsigned int id;
@@ -258,6 +255,7 @@ struct dpp_authentication {
* Authentication exchange */
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
unsigned int num_freq, freq_idx;
unsigned int curr_chan;
unsigned int curr_freq;
unsigned int neg_freq;
unsigned int num_freq_iters;
@@ -488,8 +486,8 @@ void dpp_auth_deinit(struct dpp_authentication *auth);
struct wpabuf *
dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
size_t attr_len);
int dpp_conf_resp_rx(struct dpp_authentication *auth,
const struct wpabuf *resp);
int dpp_conf_resp_rx(struct dpp_authentication *auth, const u8 *resp,
u32 resp_len);
enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
const u8 *hdr,
const u8 *attr_start, size_t attr_len);

View File

@@ -263,6 +263,13 @@
#define WLAN_ACTION_UNPROTECTED_WNM 11
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
#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
/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
#define WLAN_SA_QUERY_REQUEST 0
#define WLAN_SA_QUERY_RESPONSE 1
@@ -274,6 +281,8 @@
#define WLAN_TIMEOUT_KEY_LIFETIME 2
#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
#define OUI_WFA 0x506f9a
#define DPP_OUI_TYPE 0x1A
#ifdef _MSC_VER
#pragma pack(push, 1)
@@ -343,110 +352,37 @@ enum lci_req_subelem {
LCI_REQ_SUBELEM_MAX_AGE = 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[0];
} STRUCT_PACKED auth;
struct {
le16 reason_code;
} STRUCT_PACKED deauth;
struct {
le16 capab_info;
le16 listen_interval;
/* followed by SSID and Supported rates */
u8 variable[0];
} STRUCT_PACKED assoc_req;
struct {
le16 capab_info;
le16 status_code;
le16 aid;
/* followed by Supported rates */
u8 variable[0];
} 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[0];
} STRUCT_PACKED reassoc_req;
struct {
le16 reason_code;
} 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[0];
} STRUCT_PACKED beacon;
struct {
/* only variable items: SSID, Supported rates */
u8 variable[0];
} STRUCT_PACKED probe_req;
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[0];
} STRUCT_PACKED probe_resp;
struct {
u8 category;
union {
struct {
u8 action_code;
u8 dialog_token;
u8 status_code;
u8 variable[0];
} 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[0]; /* 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[0]; /* FT Request */
} STRUCT_PACKED ft_action_resp;
struct {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} STRUCT_PACKED sa_query_req;
struct {
u8 action; /* */
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} STRUCT_PACKED sa_query_resp;
} u;
} STRUCT_PACKED action;
} u;
#ifdef ESP_SUPPLICANT
struct ieee80211_pa_vendor {
u8 oui[3];
u8 wfa_stype;
u8 vendor_data[];
} STRUCT_PACKED;
struct ieee80211_gas_resp {
u8 diag_token;
u16 status_code;
u16 comeback_delay;
u8 type;
u8 length;
u8 data[];
} STRUCT_PACKED;
struct ieee80211_public_action {
u8 action;
union {
struct ieee80211_pa_vendor pa_vendor_spec;
struct ieee80211_gas_resp pa_gas_resp;
} v;
} STRUCT_PACKED;
struct ieee80211_action {
u8 category;
union {
struct ieee80211_public_action public_action;
} u;
} STRUCT_PACKED;
#endif /* ESP_SUPPLICANT */
#define IEEE80211_MAX_MMPDU_SIZE 2304
struct ieee80211_ht_capabilities {

View File

@@ -0,0 +1,648 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_dpp_i.h"
#include "esp_dpp.h"
#include "esp_wpa.h"
#include "esp_timer.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "common/ieee802_11_defs.h"
static void *s_dpp_task_hdl = NULL;
static void *s_dpp_evt_queue = NULL;
static void *s_dpp_api_lock = NULL;
static bool s_dpp_stop_listening;
static int s_dpp_auth_retries;
struct esp_dpp_context_t s_dpp_ctx;
static wifi_action_rx_cb_t s_action_rx_cb = esp_supp_rx_action;
#define DPP_API_LOCK() xSemaphoreTakeRecursive(s_dpp_api_lock, portMAX_DELAY)
#define DPP_API_UNLOCK() xSemaphoreGiveRecursive(s_dpp_api_lock)
struct action_rx_param {
u8 sa[ETH_ALEN];
u32 channel;
u32 frm_len;
u32 vendor_data_len;
struct ieee80211_action *action_frm;
};
static int esp_dpp_post_evt(uint32_t evt_id, uint32_t data)
{
DPP_API_LOCK();
dpp_event_t *evt = os_zalloc(sizeof(dpp_event_t));
if (evt == NULL) {
DPP_API_UNLOCK();
return ESP_ERR_NO_MEM;
}
evt->id = evt_id;
evt->data = data;
if ( xQueueSend(s_dpp_evt_queue, &evt, 10 / portTICK_PERIOD_MS ) != pdPASS) {
DPP_API_UNLOCK();
os_free(evt);
return ESP_ERR_DPP_FAILURE;
}
DPP_API_UNLOCK();
return ESP_OK;
}
static void esp_dpp_call_cb(esp_supp_dpp_event_t evt, void *data)
{
s_dpp_ctx.dpp_event_cb(evt, data);
}
void esp_send_action_frame(uint8_t *dest_mac, const uint8_t *buf, uint32_t len,
uint8_t channel, uint32_t wait_time_ms)
{
wifi_action_tx_req_t *req = os_zalloc(sizeof(*req) + len);;
if (!req) {
return;
}
req->ifx = ESP_IF_WIFI_STA;
memcpy(req->dest_mac, dest_mac, ETH_ALEN);
req->no_ack = false;
req->data_len = len;
req->rx_cb = s_action_rx_cb;
memcpy(req->data, buf, req->data_len);
wpa_printf(MSG_DEBUG, "DPP: Mgmt Tx - MAC:" MACSTR ", Channel-%d, WaitT-%d",
MAC2STR(dest_mac), channel, wait_time_ms);
if (ESP_OK != esp_wifi_action_tx_req(WIFI_OFFCHAN_TX_REQ, channel,
wait_time_ms, req)) {
wpa_printf(MSG_ERROR, "DPP: Failed to perfrm offchannel operation");
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_TX_FAILURE);
os_free(req);
return;
}
os_free(req);
}
static void esp_dpp_rx_auth_req(struct action_rx_param *rx_param, uint8_t *dpp_data)
{
size_t len = rx_param->vendor_data_len - 2;
const u8 *r_bootstrap, *i_bootstrap;
u16 r_bootstrap_len, i_bootstrap_len;
struct dpp_bootstrap_info *own_bi;
int rc;
wpa_printf(MSG_INFO, "DPP: Authentication Request from " MACSTR, MAC2STR(rx_param->sa));
r_bootstrap = dpp_get_attr(dpp_data, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
wpa_printf(MSG_INFO, "DPP: Missing or invalid Responder Bootstrapping Key Hash attribute");
rc = ESP_ERR_DPP_INVALID_ATTR;
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", r_bootstrap, r_bootstrap_len);
i_bootstrap = dpp_get_attr(dpp_data, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
wpa_printf(MSG_INFO, "DPP: Missing or invalid Initiator Bootstrapping Key Hash attribute");
rc = ESP_ERR_DPP_INVALID_ATTR;
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", i_bootstrap, i_bootstrap_len);
own_bi = dpp_bootstrap_get_id(s_dpp_ctx.dpp_global, s_dpp_ctx.id);
/* Try to find own and peer bootstrapping key matches based on the
* received hash values */
if (os_memcmp(own_bi->pubkey_hash, r_bootstrap, SHA256_MAC_LEN)) {
wpa_printf(MSG_INFO, "DPP: No matching own bootstrapping key found as responder - ignore message");
rc = ESP_ERR_DPP_INVALID_ATTR;
goto fail;
}
s_dpp_ctx.dpp_auth = dpp_auth_req_rx(NULL, DPP_CAPAB_ENROLLEE, 0, NULL,
own_bi, rx_param->channel,
(const u8 *)&rx_param->action_frm->u.public_action.v, dpp_data, len);
os_memcpy(s_dpp_ctx.dpp_auth->peer_mac_addr, rx_param->sa, ETH_ALEN);
esp_send_action_frame(rx_param->sa, wpabuf_head(s_dpp_ctx.dpp_auth->resp_msg),
wpabuf_len(s_dpp_ctx.dpp_auth->resp_msg),
rx_param->channel, OFFCHAN_TX_WAIT_TIME);
return;
fail:
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)rc);
}
static void gas_query_req_tx(struct dpp_authentication *auth)
{
struct wpabuf *buf;
int supp_op_classes[] = {81, 0};
buf = dpp_build_conf_req_helper(auth, NULL, 0, NULL,
supp_op_classes);
if (!buf) {
wpa_printf(MSG_DEBUG, "DPP: No configuration request data available");
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_FAILURE);
return;
}
wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (chan %u)",
MAC2STR(auth->peer_mac_addr), auth->curr_chan);
esp_send_action_frame(auth->peer_mac_addr, wpabuf_head(buf), wpabuf_len(buf),
auth->curr_chan, OFFCHAN_TX_WAIT_TIME);
}
static int esp_dpp_handle_config_obj(struct dpp_authentication *auth,
struct dpp_config_obj *conf)
{
wifi_config_t *wifi_cfg = &s_dpp_ctx.wifi_cfg;
if (conf->ssid_len) {
os_memcpy(wifi_cfg->sta.ssid, conf->ssid, conf->ssid_len);
}
if (dpp_akm_legacy(conf->akm)) {
if (conf->passphrase[0])
os_memcpy(wifi_cfg->sta.password, conf->passphrase,
sizeof(wifi_cfg->sta.password));
if (conf->akm == DPP_AKM_PSK_SAE) {
wifi_cfg->sta.pmf_cfg.capable = true;
wifi_cfg->sta.pmf_cfg.required = true;
}
}
if (conf->connector) {
/* TODO: Save the Connector and consider using a command
* to fetch the value instead of sending an event with
* it. The Connector could end up being larger than what
* most clients are ready to receive as an event
* message. */
wpa_printf(MSG_INFO, DPP_EVENT_CONNECTOR "%s",
conf->connector);
}
s_dpp_stop_listening = false;
esp_wifi_action_tx_req(WIFI_OFFCHAN_TX_CANCEL, 0, 0, NULL);
esp_dpp_call_cb(ESP_SUPP_DPP_CFG_RECVD, wifi_cfg);
return 0;
}
static void esp_dpp_rx_auth_conf(struct action_rx_param *rx_param, uint8_t *dpp_data)
{
struct dpp_authentication *auth = s_dpp_ctx.dpp_auth;
struct ieee80211_public_action *public_action =
&rx_param->action_frm->u.public_action;
size_t len = rx_param->vendor_data_len - 2;
int rc;
wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
MAC2STR(rx_param->sa));
if (!auth) {
wpa_printf(MSG_DEBUG, "DPP: No DPP Authentication in progress - drop");
rc = ESP_ERR_DPP_FAILURE;
goto fail;
}
if (os_memcmp(rx_param->sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
rc = ESP_ERR_DPP_FAILURE;
goto fail;
}
if (dpp_auth_conf_rx(auth, (const u8 *)&public_action->v,
dpp_data, len) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
rc = ESP_ERR_DPP_FAILURE;
goto fail;
}
/* Send GAS Query Req */
gas_query_req_tx(auth);
return;
fail:
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)rc);
}
static void esp_dpp_rx_auth(struct action_rx_param *rx_param)
{
uint8_t crypto_suit, type;
uint8_t *tmp;
tmp = rx_param->action_frm->u.public_action.v.pa_vendor_spec.vendor_data;
crypto_suit = tmp[0];
type = tmp[1];
if (crypto_suit != 1) {
wpa_printf(MSG_ERROR, "DPP: Unsupported crypto suit");
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_NOT_SUPPORTED);
return;
}
switch (type) {
case DPP_PA_AUTHENTICATION_REQ:
esp_dpp_rx_auth_req(rx_param, &tmp[2]);
break;
case DPP_PA_AUTHENTICATION_CONF:
esp_dpp_rx_auth_conf(rx_param, &tmp[2]);
break;
}
}
static void gas_query_resp_rx(struct action_rx_param *rx_param)
{
struct dpp_authentication *auth = s_dpp_ctx.dpp_auth;
uint8_t *pos = rx_param->action_frm->u.public_action.v.pa_gas_resp.data;
uint8_t *resp = &pos[10];
int i, res;
if (pos[1] == WLAN_EID_VENDOR_SPECIFIC && pos[2] == 5 &&
WPA_GET_BE24(&pos[3]) == OUI_WFA && pos[6] == 0x1a && pos[7] == 1) {
if (dpp_conf_resp_rx(auth, resp, rx_param->vendor_data_len - 2) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
goto fail;
}
for (i = 0; i < auth->num_conf_obj; i++) {
res = esp_dpp_handle_config_obj(auth, &auth->conf_obj[i]);
if (res < 0) {
goto fail;
}
}
}
return;
fail:
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_FAILURE);
}
static void esp_dpp_rx_action(struct action_rx_param *rx_param)
{
if (rx_param->action_frm->category == WLAN_ACTION_PUBLIC) {
struct ieee80211_public_action *public_action =
&rx_param->action_frm->u.public_action;
wpa_printf(MSG_DEBUG, "DPP: Rx Public Action frame: action - %d",
public_action->action);
if (public_action->action == WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(public_action->v.pa_vendor_spec.oui) == OUI_WFA &&
public_action->v.pa_vendor_spec.wfa_stype == DPP_OUI_TYPE) {
rx_param->vendor_data_len = rx_param->frm_len -
(size_t)(public_action->v.pa_vendor_spec.vendor_data -
(u8 *)rx_param->action_frm);
if (!s_dpp_stop_listening) {
esp_supp_dpp_stop_listen();
}
esp_dpp_rx_auth(rx_param);
} else if (public_action->action == WLAN_PA_GAS_INITIAL_RESP &&
public_action->v.pa_gas_resp.type == WLAN_EID_ADV_PROTO &&
public_action->v.pa_gas_resp.length == 8 &&
public_action->v.pa_gas_resp.status_code == 0) {
rx_param->vendor_data_len = rx_param->frm_len -
(size_t)(public_action->v.pa_gas_resp.data +
public_action->v.pa_gas_resp.length -
(u8 *)rx_param->action_frm);
gas_query_resp_rx(rx_param);
}
}
os_free(rx_param->action_frm);
os_free(rx_param);
}
static void esp_dpp_task(void *pvParameters )
{
dpp_event_t *evt;
bool task_del = false;
for (;;) {
if (xQueueReceive(s_dpp_evt_queue, &evt, portMAX_DELAY) == pdTRUE) {
if (evt->id < SIG_DPP_MAX) {
DPP_API_LOCK();
} else {
os_free(evt);
continue;
}
switch (evt->id) {
case SIG_DPP_DEL_TASK:
task_del = true;
break;
case SIG_DPP_BOOTSTRAP_GEN: {
char *command = (char *)evt->data;
const char *uri;
s_dpp_ctx.id = dpp_bootstrap_gen(s_dpp_ctx.dpp_global, command);
uri = dpp_bootstrap_get_uri(s_dpp_ctx.dpp_global, s_dpp_ctx.id);
esp_dpp_call_cb(ESP_SUPP_DPP_URI_READY, (void *)uri);
os_free(command);
}
break;
case SIG_DPP_RX_ACTION: {
esp_dpp_rx_action((struct action_rx_param *)evt->data);
}
break;
case SIG_DPP_LISTEN_NEXT_CHANNEL: {
struct dpp_bootstrap_params_t *p = &s_dpp_ctx.bootstrap_params;
static int counter;
int channel;
channel = p->chan_list[counter++ % p->num_chan];
esp_wifi_remain_on_channel(ESP_IF_WIFI_STA, WIFI_ROC_REQ, channel,
BOOTSTRAP_ROC_WAIT_TIME, s_action_rx_cb);
}
break;
default:
break;
}
os_free(evt);
DPP_API_UNLOCK();
if (task_del) {
break;
}
}
}
vQueueDelete(s_dpp_evt_queue);
s_dpp_evt_queue = NULL;
if (s_dpp_api_lock) {
vSemaphoreDelete(s_dpp_api_lock);
s_dpp_api_lock = NULL;
}
/* At this point, we completed */
vTaskDelete(NULL);
}
int esp_supp_rx_action(uint8_t *hdr, uint8_t *payload, size_t len, uint8_t channel)
{
struct ieee80211_hdr *rx_hdr = (struct ieee80211_hdr *)hdr;
struct action_rx_param *rx_param;
if (WLAN_FC_GET_STYPE(rx_hdr->frame_control) == WLAN_FC_STYPE_ACTION) {
rx_param = os_zalloc(sizeof(struct action_rx_param));
os_memcpy(rx_param->sa, rx_hdr->addr2, ETH_ALEN);
rx_param->channel = channel;
rx_param->action_frm = os_zalloc(len);
rx_param->frm_len = len;
os_memcpy(rx_param->action_frm, payload, len);
if (ESP_OK != esp_dpp_post_evt(SIG_DPP_RX_ACTION, (u32)rx_param)) {
os_free(rx_param->action_frm);
os_free(rx_param);
}
}
return ESP_ERR_NOT_SUPPORTED;
}
static void offchan_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_id == WIFI_EVENT_ACTION_TX_STATUS) {
wifi_event_action_tx_status_t *evt =
(wifi_event_action_tx_status_t *)event_data;
wpa_printf(MSG_DEBUG, "Mgmt Tx Status - %d, Cookie - 0x%x",
evt->status, (uint32_t)evt->context);
if (evt->status) {
esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_TX_FAILURE);
}
} else if (event_id == WIFI_EVENT_ROC_DONE) {
wifi_event_roc_done_t *evt = (wifi_event_roc_done_t *)event_data;
if (!s_dpp_stop_listening && evt->context == (uint32_t)s_action_rx_cb) {
esp_dpp_post_evt(SIG_DPP_LISTEN_NEXT_CHANNEL, 0);
}
}
}
static char *esp_dpp_parse_chan_list(const char *chan_list)
{
struct dpp_bootstrap_params_t *params = &s_dpp_ctx.bootstrap_params;
char *uri_channels = os_zalloc(14 * 6 + 1);
const char *pos = chan_list;
const char *pos2;
char *pos3 = uri_channels;
params->num_chan = 0;
os_memcpy(pos3, " chan=", strlen(" chan="));
pos3 += strlen(" chan=");
while (pos && *pos) {
int channel;
int len = strlen(chan_list);
pos2 = pos;
while (*pos2 >= '0' && *pos2 <= '9') {
pos2++;
}
if (*pos2 == ',' || *pos2 == ' ' || *pos2 == '\0') {
channel = atoi(pos);
if (channel < 1 || channel > 14) {
os_free(uri_channels);
return NULL;
}
params->chan_list[params->num_chan++] = channel;
os_memcpy(pos3, "81/", strlen("81/"));
pos3 += strlen("81/");
os_memcpy(pos3, pos, (pos2 - pos));
pos3 += (pos2 - pos);
*pos3++ = ',';
pos = pos2 + 1;
}
while (*pos == ',' || *pos == ' ' || *pos == '\0') {
pos++;
}
if (((int)(pos - chan_list) >= len)) {
break;
}
}
*(pos3 - 1) = ' ';
return uri_channels;
}
esp_err_t
esp_supp_dpp_bootstrap_gen(const char *chan_list, enum dpp_bootstrap_type type,
const char *key, const char *uri_info)
{
struct dpp_bootstrap_params_t *params = &s_dpp_ctx.bootstrap_params;
char *uri_chan_list = esp_dpp_parse_chan_list(chan_list);
char *command = os_zalloc(1200);
int ret;
if (!uri_chan_list || !command || params->num_chan >= 14 || params->num_chan == 0) {
wpa_printf(MSG_ERROR, "Invalid Channel list - %s", chan_list);
if (command) {
os_free(command);
}
ret = ESP_ERR_DPP_FAILURE;
goto fail;
}
if (type != DPP_BOOTSTRAP_QR_CODE) {
wpa_printf(MSG_INFO, "Bootstrap type %d not supported", type);
os_free(command);
ret = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
params->type = type;
esp_wifi_get_mac(ESP_IF_WIFI_STA, params->mac);
if (uri_info) {
params->info_len = strlen(uri_info);
if (params->info_len) {
params->info = os_zalloc(params->info_len + 1);
if (!params->info) {
os_free(command);
ret = ESP_ERR_NO_MEM;
goto fail;
}
os_memcpy(params->info, uri_info, params->info_len);
}
}
if (key) {
params->key_len = strlen(key);
if (params->key_len) {
char prefix[] = "30310201010420";
char postfix[] = "a00a06082a8648ce3d030107";
params->key = os_zalloc(params->key_len +
sizeof(prefix) + sizeof(postfix));
if (!params->key) {
os_free(command);
ret = ESP_ERR_NO_MEM;
goto fail;
}
sprintf(params->key, "%s%s%s", prefix, key, postfix);
}
}
sprintf(command, "type=qrcode mac=" MACSTR "%s%s%s%s%s",
MAC2STR(params->mac), uri_chan_list,
params->key_len ? "key=" : "",
params->key_len ? params->key : "",
params->info_len ? " info=" : "",
params->info_len ? params->info : "");
ret = esp_dpp_post_evt(SIG_DPP_BOOTSTRAP_GEN, (u32)command);
if (ret != ESP_OK) {
os_free(command);
if (params->info) {
os_free(params->info);
params->info = NULL;
}
if (params->key) {
os_free(params->key);
params->key = NULL;
}
goto fail;
}
ret = ESP_OK;
fail:
if (uri_chan_list) {
os_free(uri_chan_list);
}
return ret;
}
esp_err_t esp_supp_dpp_start_listen(void)
{
if (esp_wifi_get_user_init_flag_internal() == 0) {
wpa_printf(MSG_ERROR, "DPP: ROC not possible before wifi is started");
return ESP_ERR_INVALID_STATE;
}
return esp_dpp_post_evt(SIG_DPP_LISTEN_NEXT_CHANNEL, 0);
}
void esp_supp_dpp_stop_listen(void)
{
s_dpp_stop_listening = true;
esp_wifi_remain_on_channel(ESP_IF_WIFI_STA, WIFI_ROC_CANCEL, 0, 0, NULL);
}
esp_err_t esp_supp_dpp_init(esp_supp_dpp_event_cb_t cb)
{
struct dpp_global_config cfg = {0};
os_bzero(&s_dpp_ctx, sizeof(s_dpp_ctx));
s_dpp_ctx.dpp_event_cb = cb;
cfg.cb_ctx = &s_dpp_ctx;
cfg.msg_ctx = &s_dpp_ctx;
s_dpp_ctx.dpp_global = dpp_global_init(&cfg);
s_dpp_stop_listening = false;
s_dpp_evt_queue = xQueueCreate(3, sizeof(dpp_event_t));
xTaskCreate(esp_dpp_task, "dppT", DPP_TASK_STACK_SIZE, NULL, 2, s_dpp_task_hdl);
s_dpp_api_lock = xSemaphoreCreateRecursiveMutex();
if (!s_dpp_api_lock) {
wpa_printf(MSG_ERROR, "DPP: dpp_init: failed to create DPP API lock");
return ESP_ERR_NO_MEM;
}
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_ACTION_TX_STATUS,
&offchan_event_handler, NULL);
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_ROC_DONE,
&offchan_event_handler, NULL);
wpa_printf(MSG_INFO, "esp_dpp_task prio:%d, stack:%d\n", 2, DPP_TASK_STACK_SIZE);
return ESP_OK;
}
void esp_supp_dpp_deinit(void)
{
struct dpp_bootstrap_params_t *params = &s_dpp_ctx.bootstrap_params;
if (params->info) {
os_free(params->info);
params->info = NULL;
}
if (params->key) {
os_free(params->key);
params->key = NULL;
}
s_dpp_auth_retries = 0;
dpp_global_deinit(s_dpp_ctx.dpp_global);
esp_dpp_post_evt(SIG_DPP_DEL_TASK, 0);
}

View File

@@ -0,0 +1,68 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_DPP_I_H
#define ESP_DPP_I_H
#include "esp_err.h"
#include "utils/includes.h"
#include "utils/common.h"
#include "common/dpp.h"
#include "esp_dpp.h"
#include "esp_wifi_driver.h"
#define DPP_TASK_STACK_SIZE (6144 + TASK_STACK_SIZE_ADD)
enum SIG_DPP {
SIG_DPP_RESET = 0,
SIG_DPP_BOOTSTRAP_GEN,
SIG_DPP_RX_ACTION,
SIG_DPP_LISTEN_NEXT_CHANNEL,
SIG_DPP_DEL_TASK,
SIG_DPP_MAX,
};
typedef struct {
uint32_t id;
uint32_t data;
} dpp_event_t;
#define BOOTSTRAP_ROC_WAIT_TIME 500
#define OFFCHAN_TX_WAIT_TIME 500
struct dpp_bootstrap_params_t {
enum dpp_bootstrap_type type;
uint8_t chan_list[14];
uint8_t num_chan;
uint8_t mac[6];
uint32_t key_len;
char *key;
uint32_t info_len;
char *info;
};
struct esp_dpp_context_t {
struct dpp_bootstrap_params_t bootstrap_params;
struct dpp_authentication *dpp_auth;
int gas_dialog_token;
esp_supp_dpp_event_cb_t dpp_event_cb;
struct dpp_global *dpp_global;
wifi_config_t wifi_cfg;
int id;
};
int esp_supp_rx_action(uint8_t *hdr, uint8_t *payload, size_t len, uint8_t channel);
#endif /* ESP_DPP_I_H */

View File

@@ -261,5 +261,9 @@ bool esp_wifi_is_btm_enabled_internal(uint8_t if_index);
esp_err_t esp_wifi_register_mgmt_frame_internal(uint32_t type, uint32_t subtype);
esp_err_t esp_wifi_send_mgmt_frm_internal(const wifi_mgmt_frm_req_t *req);
uint8_t esp_wifi_ap_get_prof_pairwise_cipher_internal(void);
esp_err_t esp_wifi_action_tx_req(uint8_t type, uint8_t channel,
uint32_t wait_time_ms, const wifi_action_tx_req_t *req);
esp_err_t esp_wifi_remain_on_channel(uint8_t ifx, uint8_t type, uint8_t channel,
uint32_t wait_time_ms, wifi_action_rx_cb_t rx_cb);
#endif /* _ESP_WIFI_DRIVER_H_ */

View File

@@ -0,0 +1,245 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "string.h"
#include "esp_system.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi_types.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "../src/esp_supplicant/esp_wifi_driver.h"
#include "esp_log.h"
#include "test_utils.h"
#include "freertos/event_groups.h"
#define WIFI_START_EVENT 0x00000001
#define WIFI_ROC_DONE_EVENT 0x00000002
#define WIFI_ACTION_RX_EVENT 0x00000003
#define WIFI_SCAN_DONE_EVENT 0x00000004
#define TEST_LISTEN_CHANNEL 6
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)
static const char *TAG = "test_offchan";
esp_netif_t *wifi_netif;
static EventGroupHandle_t wifi_event;
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case WIFI_EVENT_STA_START:
ESP_LOGI(TAG, "WIFI Started");
xEventGroupSetBits(wifi_event, WIFI_START_EVENT);
break;
case WIFI_EVENT_ACTION_TX_STATUS: {
wifi_event_action_tx_status_t *evt =
(wifi_event_action_tx_status_t *)event_data;
if (evt->status == 0) {
ESP_LOGI(TAG, "Action Tx Successful");
}
}
break;
case WIFI_EVENT_ROC_DONE:
ESP_LOGI(TAG, "ROC Done");
xEventGroupSetBits(wifi_event, WIFI_ROC_DONE_EVENT);
break;
case WIFI_EVENT_SCAN_DONE:
ESP_LOGI(TAG, "Scan Done");
xEventGroupSetBits(wifi_event, WIFI_SCAN_DONE_EVENT);
break;
default:
break;
}
return;
}
static esp_err_t event_init(void)
{
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
wifi_netif = esp_netif_create_default_wifi_sta();
return ESP_OK;
}
static void start_wifi_as_sta(void)
{
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
cfg.nvs_enable = false;
event_init();
// can't deinit event loop, need to reset leak check
unity_reset_leak_checks();
if (wifi_event == NULL) {
wifi_event = xEventGroupCreate();
} else {
xEventGroupClearBits(wifi_event, 0x00ffffff);
}
TEST_ESP_OK(esp_wifi_init(&cfg));
TEST_ESP_OK(esp_wifi_set_mode(WIFI_MODE_STA));
TEST_ESP_OK(esp_wifi_start());
}
static void stop_wifi(void)
{
esp_event_loop_delete_default();
ESP_LOGI(TAG, "Stop wifi\n");
TEST_ESP_OK(esp_wifi_stop());
TEST_ESP_OK(esp_wifi_deinit());
esp_wifi_clear_default_wifi_driver_and_handlers(wifi_netif);
esp_netif_destroy(wifi_netif);
if (wifi_event) {
vEventGroupDelete(wifi_event);
wifi_event = NULL;
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
int dummy_rx_action(uint8_t *hdr, uint8_t *payload, size_t len, uint8_t channel)
{
return ESP_OK;
}
static const char *frame_data = "This is a test data";
void esp_send_action_frame(uint8_t *dest_mac, const uint8_t *buf, uint32_t len,
uint8_t channel, uint32_t wait_time_ms)
{
wifi_action_tx_req_t *req = os_zalloc(sizeof(*req) + len);;
TEST_ASSERT( req != NULL);
req->ifx = ESP_IF_WIFI_STA;
memcpy(req->dest_mac, dest_mac, ETH_ALEN);
req->no_ack = false;
req->data_len = len;
req->rx_cb = dummy_rx_action;
memcpy(req->data, buf, req->data_len);
ESP_LOGI(TAG, "Action Tx - MAC:" MACSTR ", Channel-%d, WaitT-%d",
MAC2STR(dest_mac), channel, wait_time_ms);
TEST_ESP_OK(esp_wifi_action_tx_req(WIFI_OFFCHAN_TX_REQ, channel, wait_time_ms, req));
os_free(req);
}
/* Test that foreground Scan doesn't pre-empt ROC & vice versa */
TEST_CASE("Test scan and ROC simultaneously", "[Offchan]")
{
wifi_action_rx_cb_t rx_cb = dummy_rx_action;
EventBits_t bits;
test_case_uses_tcpip();
start_wifi_as_sta();
xEventGroupWaitBits(wifi_event, WIFI_START_EVENT, 1, 0, 5000 / portTICK_RATE_MS);
TEST_ESP_OK(esp_wifi_remain_on_channel(ESP_IF_WIFI_STA, WIFI_ROC_REQ, TEST_LISTEN_CHANNEL,
100, rx_cb));
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, false));
bits = xEventGroupWaitBits(wifi_event, WIFI_ROC_DONE_EVENT | WIFI_SCAN_DONE_EVENT,
pdTRUE, pdFALSE, 5000 / portTICK_RATE_MS);
TEST_ASSERT_TRUE(bits == WIFI_ROC_DONE_EVENT);
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, false));
TEST_ESP_OK(esp_wifi_remain_on_channel(ESP_IF_WIFI_STA, WIFI_ROC_REQ, TEST_LISTEN_CHANNEL,
100, rx_cb));
bits = xEventGroupWaitBits(wifi_event, WIFI_ROC_DONE_EVENT | WIFI_SCAN_DONE_EVENT,
pdTRUE, pdFALSE, 5000 / portTICK_RATE_MS);
TEST_ASSERT_TRUE(bits == WIFI_SCAN_DONE_EVENT);
stop_wifi();
}
static void test_wifi_offchan_tx(void)
{
int i;
char mac_str[19];
uint8_t mac[6];
test_case_uses_tcpip();
start_wifi_as_sta();
xEventGroupWaitBits(wifi_event, WIFI_START_EVENT, 1, 0, 5000 / portTICK_RATE_MS);
unity_wait_for_signal_param("Listener mac", mac_str, 19);
TEST_ASSERT_TRUE(unity_util_convert_mac_from_string(mac_str, mac));
for (i = 0; i < 3; i++) {
esp_send_action_frame(mac, (const uint8_t *)frame_data, strlen(frame_data),
TEST_LISTEN_CHANNEL, 500);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
stop_wifi();
}
static int test_rx_action(uint8_t *hdr, uint8_t *payload, size_t len, uint8_t channel)
{
struct ieee80211_hdr *rx_hdr = (struct ieee80211_hdr *)hdr;
ESP_LOGI(TAG, "Rxd Action Frame from " MACSTR " (Seq-%lu)", MAC2STR(rx_hdr->addr2),
WLAN_GET_SEQ_SEQ(rx_hdr->seq_ctrl));
if (!os_memcmp(payload, frame_data, strlen(frame_data))) {
xEventGroupSetBits(wifi_event, WIFI_ACTION_RX_EVENT);
}
return ESP_OK;
}
static void test_wifi_roc(void)
{
wifi_action_rx_cb_t rx_cb = test_rx_action;
char mac_str[19] = {0};
EventBits_t bits;
uint8_t mac[6];
test_case_uses_tcpip();
start_wifi_as_sta();
xEventGroupWaitBits(wifi_event, WIFI_START_EVENT, 1, 0, 5000 / portTICK_RATE_MS);
TEST_ESP_OK(esp_wifi_get_mac(ESP_IF_WIFI_STA, mac));
sprintf(mac_str, MACSTR, MAC2STR(mac));
unity_send_signal_param("Listener mac", mac_str);
TEST_ESP_OK(esp_wifi_remain_on_channel(ESP_IF_WIFI_STA, WIFI_ROC_REQ, TEST_LISTEN_CHANNEL,
10000, rx_cb));
bits = xEventGroupWaitBits(wifi_event, WIFI_ROC_DONE_EVENT | WIFI_ACTION_RX_EVENT,
pdTRUE, pdFALSE, portMAX_DELAY);
/* Confirm that Frame has been received successfully */
if (bits == WIFI_ACTION_RX_EVENT) {
TEST_ESP_OK(esp_wifi_remain_on_channel(ESP_IF_WIFI_STA, WIFI_ROC_CANCEL, 0, 0, NULL));
vTaskDelay(1000 / portTICK_PERIOD_MS);
stop_wifi();
} else {
stop_wifi();
TEST_FAIL();
}
}
TEST_CASE_MULTIPLE_DEVICES("test ROC and Offchannel Action Frame Tx", "[Offchan][test_env=UT_T2_1][timeout=90]", test_wifi_roc, test_wifi_offchan_tx);
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3)