net: l2: wifi: Add support for W-Fi mode setting and selection

This change brings in support for setting various Wi-Fi modes and
enables a specific Wi-Fi interface to be also placed into a sniffer
operation via monitor mode and promiscuous mode. A raw TX- packet
Injection mode is also introduced

Signed-off-by: Vivekananda Uppunda <vivekananda.uppunda@nordicsemi.no>
This commit is contained in:
Vivekananda Uppunda 2023-09-01 15:56:38 +05:30 committed by Carles Cufí
commit 450dbb15ef
4 changed files with 556 additions and 1 deletions

View file

@ -148,6 +148,7 @@ static inline const char *wifi_band_txt(enum wifi_frequency_bands band)
#define WIFI_SAE_PSWD_MAX_LEN 128
#define WIFI_MAC_ADDR_LEN 6
#define WIFI_CHANNEL_MIN 1
#define WIFI_CHANNEL_MAX 233
#define WIFI_CHANNEL_ANY 255
@ -355,6 +356,38 @@ static const char * const wifi_ps_mode2str[] = {
};
/** @endcond */
/* Interface index Min and Max values */
#define WIFI_INTERFACE_INDEX_MIN 1
#define WIFI_INTERFACE_INDEX_MAX 255
/** Wifi operational mode */
enum wifi_operational_modes {
/** STA mode setting enable */
WIFI_STA_MODE = BIT(0),
/** Monitor mode setting enable */
WIFI_MONITOR_MODE = BIT(1),
/** TX injection mode setting enable */
WIFI_TX_INJECTION_MODE = BIT(2),
/** Promiscuous mode setting enable */
WIFI_PROMISCUOUS_MODE = BIT(3),
/** AP mode setting enable */
WIFI_AP_MODE = BIT(4),
/** Softap mode setting enable */
WIFI_SOFTAP_MODE = BIT(5),
};
/** Mode filter settings */
enum wifi_filter {
/** Support management, data and control packet sniffing */
WIFI_PACKET_FILTER_ALL = BIT(0),
/** Support only sniffing of management packets */
WIFI_PACKET_FILTER_MGMT = BIT(1),
/** Support only sniffing of data packets */
WIFI_PACKET_FILTER_DATA = BIT(2),
/** Support only sniffing of control packets */
WIFI_PACKET_FILTER_CTRL = BIT(3),
};
/** Wi-Fi Target Wake Time (TWT) operations. */
enum wifi_twt_operation {
/** TWT setup operation */

View file

@ -69,6 +69,12 @@ enum net_request_wifi_cmd {
NET_REQUEST_WIFI_CMD_REG_DOMAIN,
/** Set power save timeout */
NET_REQUEST_WIFI_CMD_PS_TIMEOUT,
/** Set or get Mode of operation */
NET_REQUEST_WIFI_CMD_MODE,
/** Set or get packet filter setting for current mode */
NET_REQUEST_WIFI_CMD_PACKET_FILTER,
/** Set or get Wi-Fi channel for Monitor or TX-Injection mode */
NET_REQUEST_WIFI_CMD_CHANNEL,
NET_REQUEST_WIFI_CMD_MAX
};
@ -131,6 +137,21 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_REG_DOMAIN);
NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_TIMEOUT);
#define NET_REQUEST_WIFI_MODE \
(_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_MODE)
NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_MODE);
#define NET_REQUEST_WIFI_PACKET_FILTER \
(_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PACKET_FILTER)
NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PACKET_FILTER);
#define NET_REQUEST_WIFI_CHANNEL \
(_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_CHANNEL)
NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_CHANNEL);
/** Wi-Fi management events */
enum net_event_wifi_cmd {
/** Scan results available */
@ -475,6 +496,37 @@ union wifi_mgmt_events {
struct wifi_twt_params twt_params;
};
/** Wi-Fi mode setup */
struct wifi_mode_info {
/** Mode setting for a specific mode of operation */
uint8_t mode;
/** Interface index */
uint8_t if_index;
/** Get or set operation */
enum wifi_mgmt_op oper;
};
/** Wi-Fi filter setting for monitor, prmoiscuous, TX-injection modes */
struct wifi_filter_info {
/** Filter setting */
uint8_t filter;
/** Interface index */
uint8_t if_index;
/** Filter buffer size */
uint16_t buffer_size;
/** Get or set operation */
enum wifi_mgmt_op oper;
};
/** Wi-Fi channel setting for monitor and TX-injection modes */
struct wifi_channel_info {
/** Channel value to set */
uint16_t channel;
/** Interface index */
uint8_t if_index;
/** Get or set operation */
enum wifi_mgmt_op oper;
};
#include <zephyr/net/net_if.h>
@ -596,6 +648,30 @@ struct wifi_mgmt_ops {
* @return 0 if ok, < 0 if error
*/
int (*reg_domain)(const struct device *dev, struct wifi_reg_domain *reg_domain);
/** Set or get packet filter settings for monitor and promiscuous modes
*
* @param dev Pointer to the device structure for the driver instance.
* @param packet filter settings
*
* @return 0 if ok, < 0 if error
*/
int (*filter)(const struct device *dev, struct wifi_filter_info *filter);
/** Set or get mode of operation
*
* @param dev Pointer to the device structure for the driver instance.
* @param mode settings
*
* @return 0 if ok, < 0 if error
*/
int (*mode)(const struct device *dev, struct wifi_mode_info *mode);
/** Set or get current channel of operation
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel settings
*
* @return 0 if ok, < 0 if error
*/
int (*channel)(const struct device *dev, struct wifi_channel_info *channel);
};
/** Wi-Fi management offload API */

View file

@ -446,6 +446,66 @@ void wifi_mgmt_raise_twt_sleep_state(struct net_if *iface,
sizeof(twt_sleep_state));
}
static int wifi_mode(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
const struct device *dev = net_if_get_device(iface);
const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface);
struct wifi_mode_info *mode_info = data;
if (dev == NULL) {
return -ENODEV;
}
if (wifi_mgmt_api == NULL || wifi_mgmt_api->mode == NULL) {
return -ENOTSUP;
}
return wifi_mgmt_api->mode(dev, mode_info);
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_MODE, wifi_mode);
static int wifi_packet_filter(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
const struct device *dev = net_if_get_device(iface);
const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface);
struct wifi_filter_info *filter_info = data;
if (dev == NULL) {
return -ENODEV;
}
if (wifi_mgmt_api == NULL || wifi_mgmt_api->filter == NULL) {
return -ENOTSUP;
}
return wifi_mgmt_api->filter(dev, filter_info);
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_PACKET_FILTER, wifi_packet_filter);
static int wifi_channel(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
const struct device *dev = net_if_get_device(iface);
const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface);
struct wifi_channel_info *channel_info = data;
if (dev == NULL) {
return -ENODEV;
}
if (wifi_mgmt_api == NULL || wifi_mgmt_api->channel == NULL) {
return -ENOTSUP;
}
return wifi_mgmt_api->channel(dev, channel_info);
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_CHANNEL, wifi_channel);
#ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS
void wifi_mgmt_raise_raw_scan_result_event(struct net_if *iface,
struct wifi_raw_scan_result *raw_scan_result)

View file

@ -75,12 +75,20 @@ static bool parse_number(const struct shell *sh, long *param, char *str, long mi
{
char *endptr;
char *str_tmp = str;
long num = strtol(str_tmp, &endptr, 10);
long num = 0;
if ((str_tmp[0] == '0') && (str_tmp[1] == 'x')) {
/* Hexadecimal numbers take base 0 in strtol */
num = strtol(str_tmp, &endptr, 0);
} else {
num = strtol(str_tmp, &endptr, 10);
}
if (*endptr != '\0') {
print(sh, SHELL_ERROR, "Invalid number: %s", str_tmp);
return false;
}
if ((num) < (min) || (num) > (max)) {
print(sh, SHELL_WARNING, "Value out of range: %s, (%ld-%ld)", str_tmp, min, max);
return false;
@ -1255,6 +1263,335 @@ static int cmd_wifi_ps_wakeup_mode(const struct shell *sh, size_t argc, char *ar
return 0;
}
void parse_mode_args_to_params(const struct shell *sh, int argc,
char *argv[], struct wifi_mode_info *mode,
bool *do_mode_oper)
{
int opt;
int option_index = 0;
static struct option long_options[] = {{"if_index", optional_argument, 0, 'i'},
{"sta", no_argument, 0, 's'},
{"monitor", no_argument, 0, 'm'},
{"TX-injection", no_argument, 0, 't'},
{"promiscuous", no_argument, 0, 'p'},
{"ap", no_argument, 0, 'a'},
{"softap", no_argument, 0, 'k'},
{"get", no_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
while ((opt = getopt_long(argc, argv, "i:smtpakgh", long_options, &option_index)) != -1) {
switch (opt) {
case 's':
mode->mode |= WIFI_STA_MODE;
break;
case 'm':
mode->mode |= WIFI_MONITOR_MODE;
break;
case 't':
mode->mode |= WIFI_TX_INJECTION_MODE;
break;
case 'p':
mode->mode |= WIFI_PROMISCUOUS_MODE;
break;
case 'a':
mode->mode |= WIFI_AP_MODE;
break;
case 'k':
mode->mode |= WIFI_SOFTAP_MODE;
break;
case 'g':
mode->oper = true;
break;
case 'i':
mode->if_index = (uint8_t)atoi(optarg);
break;
case 'h':
shell_help(sh);
*do_mode_oper = false;
break;
case '?':
default:
break;
}
}
}
static int cmd_wifi_mode(const struct shell *sh, size_t argc, char *argv[])
{
struct net_if *iface;
struct wifi_mode_info mode_info = {0};
int ret;
bool do_mode_oper = true;
if (argc > 1) {
mode_info.oper = WIFI_MGMT_SET;
parse_mode_args_to_params(sh, argc, argv, &mode_info, &do_mode_oper);
} else {
shell_fprintf(sh, SHELL_ERROR, "Invalid number of arguments\n");
return -EINVAL;
}
if (do_mode_oper) {
/* Check interface index value. Mode validation must be performed by
* lower layer
*/
if (mode_info.if_index == 0) {
iface = net_if_get_first_wifi();
if (iface == NULL) {
shell_fprintf(sh, SHELL_ERROR,
"Cannot find the default wifi interface\n");
return -ENOEXEC;
}
mode_info.if_index = net_if_get_by_iface(iface);
} else {
iface = net_if_get_by_index(mode_info.if_index);
if (iface == NULL) {
shell_fprintf(sh, SHELL_ERROR,
"Cannot find interface for if_index %d\n",
mode_info.if_index);
return -ENOEXEC;
}
}
ret = net_mgmt(NET_REQUEST_WIFI_MODE, iface, &mode_info, sizeof(mode_info));
if (ret) {
shell_fprintf(sh, SHELL_ERROR, "mode %s operation failed with reason %d\n",
mode_info.oper == WIFI_MGMT_GET ? "get" : "set", ret);
return -ENOEXEC;
}
if (mode_info.oper == WIFI_MGMT_GET) {
shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi current mode is %x\n",
mode_info.mode);
} else {
shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi mode set to %x\n", mode_info.mode);
}
}
return 0;
}
void parse_channel_args_to_params(const struct shell *sh, int argc,
char *argv[], struct wifi_channel_info *channel,
bool *do_channel_oper)
{
int opt;
int option_index = 0;
static struct option long_options[] = {{"if_index", optional_argument, 0, 'i'},
{"channel", required_argument, 0, 'c'},
{"get", no_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
while ((opt = getopt_long(argc, argv, "i:c:gh", long_options, &option_index)) != -1) {
switch (opt) {
case 'c':
channel->channel = (uint16_t)atoi(optarg);
break;
case 'i':
channel->if_index = (uint8_t)atoi(optarg);
break;
case 'g':
channel->oper = WIFI_MGMT_GET;
break;
case 'h':
shell_help(sh);
*do_channel_oper = false;
break;
case '?':
default:
break;
}
}
}
static int cmd_wifi_channel(const struct shell *sh, size_t argc, char *argv[])
{
struct net_if *iface;
struct wifi_channel_info channel_info = {0};
int ret;
bool do_channel_oper = true;
if (argc > 1) {
channel_info.oper = WIFI_MGMT_SET;
parse_channel_args_to_params(sh, argc, argv, &channel_info, &do_channel_oper);
} else {
shell_fprintf(sh, SHELL_ERROR, "Invalid number of arguments\n");
return -EINVAL;
}
if (do_channel_oper) {
/*
* Validate parameters before sending to lower layer.
* Do it here instead of parse_channel_args_to_params
* as this is right before sending the parameters to
* the lower layer.
*/
if (channel_info.if_index == 0) {
iface = net_if_get_first_wifi();
if (iface == NULL) {
shell_fprintf(sh, SHELL_ERROR,
"Cannot find the default wifi interface\n");
return -ENOEXEC;
}
channel_info.if_index = net_if_get_by_iface(iface);
} else {
iface = net_if_get_by_index(channel_info.if_index);
if (iface == NULL) {
shell_fprintf(sh, SHELL_ERROR,
"Cannot find interface for if_index %d\n",
channel_info.if_index);
return -ENOEXEC;
}
}
if (channel_info.oper == WIFI_MGMT_SET) {
if ((channel_info.channel < WIFI_CHANNEL_MIN) ||
(channel_info.channel > WIFI_CHANNEL_MAX)) {
shell_fprintf(sh, SHELL_ERROR,
"Invalid channel number. Range is (1-233)\n");
return -ENOEXEC;
}
}
ret = net_mgmt(NET_REQUEST_WIFI_CHANNEL, iface,
&channel_info, sizeof(channel_info));
if (ret) {
shell_fprintf(sh, SHELL_ERROR,
"channel %s operation failed with reason %d\n",
channel_info.oper == WIFI_MGMT_GET ? "get" : "set", ret);
return -ENOEXEC;
}
if (channel_info.oper == WIFI_MGMT_GET) {
shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi current channel is: %d\n",
channel_info.channel);
} else {
shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi channel set to %d\n",
channel_info.channel);
}
}
return 0;
}
void parse_filter_args_to_params(const struct shell *sh, int argc,
char *argv[], struct wifi_filter_info *filter,
bool *do_filter_oper)
{
int opt;
int option_index = 0;
static struct option long_options[] = {{"if_index", optional_argument, 0, 'i'},
{"capture_len", optional_argument, 0, 'b'},
{"all", no_argument, 0, 'a'},
{"mgmt", no_argument, 0, 'm'},
{"ctrl", no_argument, 0, 'c'},
{"data", no_argument, 0, 'd'},
{"get", no_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
while ((opt = getopt_long(argc, argv, "i:b:amcdgh", long_options, &option_index)) != -1) {
switch (opt) {
case 'a':
filter->filter |= WIFI_PACKET_FILTER_ALL;
break;
case 'm':
filter->filter |= WIFI_PACKET_FILTER_MGMT;
break;
case 'c':
filter->filter |= WIFI_PACKET_FILTER_DATA;
break;
case 'd':
filter->filter |= WIFI_PACKET_FILTER_CTRL;
break;
case 'i':
filter->if_index = (uint8_t)atoi(optarg);
break;
case 'b':
filter->buffer_size = (uint16_t)atoi(optarg);
break;
case 'h':
shell_help(sh);
*do_filter_oper = false;
break;
case 'g':
filter->oper = WIFI_MGMT_GET;
break;
case '?':
default:
break;
}
}
}
static int cmd_wifi_packet_filter(const struct shell *sh, size_t argc, char *argv[])
{
struct net_if *iface;
struct wifi_filter_info packet_filter = {0};
int ret;
bool do_filter_oper = true;
if (argc > 1) {
packet_filter.oper = WIFI_MGMT_SET;
parse_filter_args_to_params(sh, argc, argv, &packet_filter, &do_filter_oper);
} else {
shell_fprintf(sh, SHELL_ERROR, "Invalid number of arguments\n");
return -EINVAL;
}
if (do_filter_oper) {
/*
* Validate parameters before sending to lower layer.
* Do it here instead of parse_filter_args_to_params
* as this is right before sending the parameters to
* the lower layer. filter and packet capture length
* value to be verified by the lower layer.
*/
if (packet_filter.if_index == 0) {
iface = net_if_get_first_wifi();
if (iface == NULL) {
shell_fprintf(sh, SHELL_ERROR,
"Cannot find the default wifi interface\n");
return -ENOEXEC;
}
packet_filter.if_index = net_if_get_by_iface(iface);
} else {
iface = net_if_get_by_index(packet_filter.if_index);
if (iface == NULL) {
shell_fprintf(sh, SHELL_ERROR,
"Cannot find interface for if_index %d\n",
packet_filter.if_index);
return -ENOEXEC;
}
}
ret = net_mgmt(NET_REQUEST_WIFI_PACKET_FILTER, iface,
&packet_filter, sizeof(packet_filter));
if (ret) {
shell_fprintf(sh, SHELL_ERROR,
"Wi-Fi packet filter %s operation failed with reason %d\n",
packet_filter.oper == WIFI_MGMT_GET ? "get" : "set", ret);
return -ENOEXEC;
}
if (packet_filter.oper == WIFI_MGMT_GET) {
shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi current mode packet filter is %d\n",
packet_filter.filter);
} else {
shell_fprintf(sh, SHELL_NORMAL, "Wi-Fi mode packet filter set to %d\n",
packet_filter.filter);
}
}
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(wifi_cmd_ap,
SHELL_CMD(disable, NULL,
"Disable Access Point mode",
@ -1328,6 +1665,55 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands,
"-f: Force to use this regulatory hint over any other regulatory hints\n"
"Note: This may cause regulatory compliance issues, use it at your own risk.",
cmd_wifi_reg_domain),
SHELL_CMD(mode, NULL, "mode operational setting\n"
"This command may be used to set the Wi-Fi device into a specific mode of operation\n"
"parameters:"
"[-i : Interface index - optional argument\n"
"[-s : Station mode.\n"
"[-m : Monitor mode.\n"
"[-p : Promiscuous mode.\n"
"[-t : TX-Injection mode.\n"
"[-a : AP mode.\n"
"[-k : Softap mode.\n"
"[-h : Help.\n"
"[-g : Get current mode for a specific interface index.\n"
"Usage: Get operation example for interface index 1\n"
"wifi mode -g -i1\n"
"Set operation example for interface index 1 - set station+promiscuous\n"
"wifi mode -i1 -sp\n",
cmd_wifi_mode),
SHELL_CMD(packet_filter, NULL, "mode filter setting\n"
"This command is used to set packet filter setting when\n"
"monitor, TX-Injection and promiscuous mode is enabled.\n"
"The different packet filter modes are control, management, data and enable all filters\n"
"parameters:"
"[-i : Interface index - optional argument.\n"
"[-a : Enable all packet filter modes\n"
"[-m : Enable management packets to allowed up the stack.\n"
"[-c : Enable control packets to be allowed up the stack.\n"
"[-d : Enable Data packets to be allowed up the stack.\n"
"[-g : Get current filter settings for a specific interface index.\n"
"<-b : Capture length buffer size for each packet to be captured - optional argument.\n"
"<-h : Help.\n"
"Usage: Get operation example for interface index 1\n"
"wifi packet_filter -g -i1\n"
"Set operation example for interface index 1 - set data+management frame filter\n"
"wifi packet_filter -i1 -md\n",
cmd_wifi_packet_filter),
SHELL_CMD(channel, NULL, "wifi channel setting\n"
"This command is used to set the channel when\n"
"monitor or TX-Injection mode is enabled.\n"
"Currently 20 MHz is only supported and no BW parameter is provided\n"
"parameters:"
"[-i : Interface index - optional argument.\n"
"[-c : Set a specific channel number to the lower layer.\n"
"[-g : Get current set channel number from the lower layer.\n"
"[-h : Help.\n"
"Usage: Get operation example for interface index 1\n"
"wifi channel -g -i1\n"
"Set operation example for interface index 1 (setting channel 5)\n"
"wifi -i1 -c5\n",
cmd_wifi_channel),
SHELL_CMD_ARG(ps_timeout,
NULL,
"<val> - PS inactivity timer(in ms)",