/* * cmd_monitor.c * * Copyright (c) 2025 Umber Networks & Robert McMahon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "esp_log.h" #include "esp_console.h" #include "argtable3/argtable3.h" #include "wifi_controller.h" #include "app_console.h" // --- Subcommand Arguments --- static struct { struct arg_int *channel; struct arg_end *end; } start_args; static struct { struct arg_int *channel; struct arg_end *end; } channel_args; static struct { struct arg_str *enable; struct arg_end *end; } debug_args; static struct { struct arg_str *mac; struct arg_end *end; } filter_args; static void print_monitor_usage(void) { printf("Usage: monitor [args]\n"); printf("Subcommands:\n"); printf(" start [-c ] Start Monitor Mode (optional: set channel)\n"); printf(" stop Stop Monitor Mode\n"); printf(" status Show current status\n"); printf(" channel Switch channel (while running)\n"); printf(" debug [on|off] Enable/disable debug logging (default: show status)\n"); printf(" filter [mac] Set MAC address filter for debug (e.g., 80:84:89:93:c4:b6), or 'clear' to disable\n"); printf(" save Save current config to NVS\n"); printf(" reload Reload config from NVS\n"); printf(" clear Clear NVS config\n"); } // --- Subcommand Handlers --- static int do_monitor_start(int argc, char **argv) { start_args.channel = arg_int0("c", "channel", "", "Channel (1-13)"); start_args.end = arg_end(1); int nerrors = arg_parse(argc, argv, (void **)&start_args); if (nerrors > 0) { arg_print_errors(stderr, start_args.end, argv[0]); return 1; } int ch = 0; if (start_args.channel->count > 0) { ch = start_args.channel->ival[0]; } printf("Starting Monitor Mode%s...\n", ch ? " on specific channel" : ""); wifi_ctl_monitor_start(ch); return 0; } static int do_monitor_channel(int argc, char **argv) { channel_args.channel = arg_int1(NULL, NULL, "", "Channel (1-13)"); channel_args.end = arg_end(1); int nerrors = arg_parse(argc, argv, (void **)&channel_args); if (nerrors > 0) { arg_print_errors(stderr, channel_args.end, argv[0]); return 1; } int ch = channel_args.channel->ival[0]; printf("Switching to Channel %d...\n", ch); wifi_ctl_set_channel(ch); return 0; } static int do_monitor_debug(int argc, char **argv) { debug_args.enable = arg_str0(NULL, NULL, "", "Enable (on) or disable (off) debug logging"); debug_args.end = arg_end(1); int nerrors = arg_parse(argc, argv, (void **)&debug_args); if (nerrors > 0) { arg_print_errors(stderr, debug_args.end, argv[0]); return 1; } if (debug_args.enable->count > 0) { const char *value = debug_args.enable->sval[0]; bool enable = false; if (strcmp(value, "on") == 0 || strcmp(value, "1") == 0 || strcmp(value, "true") == 0) { enable = true; } else if (strcmp(value, "off") == 0 || strcmp(value, "0") == 0 || strcmp(value, "false") == 0) { enable = false; } else { printf("Invalid value '%s'. Use 'on' or 'off'.\n", value); return 1; } wifi_ctl_set_monitor_debug(enable); printf("Debug mode %s\n", enable ? "enabled" : "disabled"); } else { /* No argument: show current status */ bool enabled = wifi_ctl_get_monitor_debug(); printf("Debug mode: %s\n", enabled ? "enabled" : "disabled"); } return 0; } static int do_monitor_filter(int argc, char **argv) { filter_args.mac = arg_str0(NULL, NULL, "", "MAC address (XX:XX:XX:XX:XX:XX) or 'clear' to disable filter"); filter_args.end = arg_end(1); int nerrors = arg_parse(argc, argv, (void **)&filter_args); if (nerrors > 0) { arg_print_errors(stderr, filter_args.end, argv[0]); return 1; } if (filter_args.mac->count > 0) { const char *mac_str = filter_args.mac->sval[0]; if (strcmp(mac_str, "clear") == 0) { wifi_ctl_set_monitor_debug_filter(NULL); printf("Debug filter cleared\n"); } else { /* Parse MAC address string (format: XX:XX:XX:XX:XX:XX) */ uint8_t mac[6]; int values[6]; int count = sscanf(mac_str, "%x:%x:%x:%x:%x:%x", &values[0], &values[1], &values[2], &values[3], &values[4], &values[5]); if (count == 6) { /* Validate values are in range */ bool valid = true; for (int i = 0; i < 6; i++) { if (values[i] < 0 || values[i] > 255) { valid = false; break; } mac[i] = (uint8_t)values[i]; } if (valid) { if (wifi_ctl_set_monitor_debug_filter(mac) == ESP_OK) { printf("Debug filter set to: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } else { printf("Failed to set debug filter\n"); return 1; } } else { printf("Invalid MAC address: values must be 0-255\n"); return 1; } } else { printf("Invalid MAC address format. Use XX:XX:XX:XX:XX:XX or 'clear'\n"); return 1; } } } else { /* No argument: show current filter */ uint8_t mac[6]; bool enabled = wifi_ctl_get_monitor_debug_filter(mac); if (enabled) { printf("Debug filter: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } else { printf("Debug filter: disabled\n"); } } return 0; } static int cmd_monitor(int argc, char **argv) { if (argc < 2) { print_monitor_usage(); return 0; } if (strcmp(argv[1], "start") == 0) return do_monitor_start(argc - 1, &argv[1]); if (strcmp(argv[1], "stop") == 0) { wifi_ctl_stop(); return 0; } if (strcmp(argv[1], "status") == 0) { wifi_ctl_status(); return 0; } if (strcmp(argv[1], "save") == 0) { wifi_ctl_param_save(NULL); printf("Saved.\n"); return 0; } if (strcmp(argv[1], "reload") == 0) { wifi_ctl_param_init(); printf("Reloaded.\n"); return 0; } if (strcmp(argv[1], "clear") == 0) { wifi_ctl_param_clear(); printf("Cleared.\n"); return 0; } if (strcmp(argv[1], "channel") == 0) return do_monitor_channel(argc - 1, &argv[1]); if (strcmp(argv[1], "debug") == 0) return do_monitor_debug(argc - 1, &argv[1]); if (strcmp(argv[1], "filter") == 0) return do_monitor_filter(argc - 1, &argv[1]); if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0) { print_monitor_usage(); return 0; } printf("Unknown subcommand '%s'.\n", argv[1]); print_monitor_usage(); return 1; } void register_monitor_cmd(void) { start_args.channel = arg_int0("c", "channel", "", "Channel"); start_args.end = arg_end(1); channel_args.channel = arg_int1(NULL, NULL, "", "Channel"); channel_args.end = arg_end(1); debug_args.enable = arg_str0(NULL, NULL, "", "Enable or disable debug logging"); debug_args.end = arg_end(1); filter_args.mac = arg_str0(NULL, NULL, "", "MAC address filter or 'clear'"); filter_args.end = arg_end(1); const esp_console_cmd_t cmd = { .command = "monitor", .help = "Monitor Mode: start, stop, channel, status", .hint = "", .func = &cmd_monitor, }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); }