diff --git a/include/zephyr/net/wifi.h b/include/zephyr/net/wifi.h index aa3096fc5ac..55bd8d71a3a 100644 --- a/include/zephyr/net/wifi.h +++ b/include/zephyr/net/wifi.h @@ -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 */ diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index c7a17e930d6..ab3225e1408 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -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 @@ -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 */ diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index 22d54b926ab..7f0f30dc8c1 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -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) diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index 0b8c6a74983..21b1f551448 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -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, " - PS inactivity timer(in ms)",