diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e0917a..88bc5fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ if (PICO_CYW43_SUPPORTED) # specific projects in subdirectories add_subdirectory(dump_rom) add_subdirectory(dump_console) + add_subdirectory(monitor_mode) if(NOT DEFINED NO_NEXMON) add_subdirectory(ioctl_test) else() diff --git a/README.md b/README.md index 5780602..6150f3a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ App|Description ---|--- [dump_rom1](dump_rom) | Read ROM content from WLAN SoC and hex dump it to Pico's console. [dump_console1](dump_console) | Read WLAN SoC's ARM core internal console and dump it to Pico's console. +[monitor_mode1](monitor_mode) | Enable monitor mode and provide a callback for sniffing packets [ioctl_test2](ioctl_test) | Write string via IOCTL to SoC's internal console, read back internal console, and dump it to Pico's console. 1 Works with unmodified firmware provided by [cyw43-driver](https://github.com/georgerobotics/cyw43-driver). diff --git a/monitor_mode/CMakeLists.txt b/monitor_mode/CMakeLists.txt new file mode 100644 index 0000000..80bccbd --- /dev/null +++ b/monitor_mode/CMakeLists.txt @@ -0,0 +1,21 @@ +add_executable(picow_monitor_mode + picow_monitor_mode.c + ) +target_include_directories(picow_monitor_mode PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_monitor_mode + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + ) + +# Ucomment when using NEXMON driver +# target_compile_definitions(picow_monitor_mode PRIVATE +# CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE="${NEXMON_ROOT}/patches/bcm43439a0/7_95_49_2271bb6/nexmon/w43439A0_7_95_49_00_combined.h" +# CYW43_WIFI_NVRAM_INCLUDE_FILE="${PICO_NEXMON_PATH}/cyw43-driver/firmware/wifi_nvram_43439.h" +# CYW43_ENABLE_BLUETOOTH=0 +# ) + +pico_add_extra_outputs(picow_monitor_mode) + diff --git a/monitor_mode/lwipopts.h b/monitor_mode/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/monitor_mode/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/monitor_mode/picow_monitor_mode.c b/monitor_mode/picow_monitor_mode.c new file mode 100644 index 0000000..e5ddad8 --- /dev/null +++ b/monitor_mode/picow_monitor_mode.c @@ -0,0 +1,77 @@ +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +#define MONITOR_DISABLED 0 +#define MONITOR_IEEE80211 1 +/* RADIOTAP MODE REQUIRES A NEXMON FW! */ +#define MONITOR_RADIOTAP 2 +#define MONITOR_LOG_ONLY 16 + +const char *frame_type_names[3] = { + "Management", + "Control", + "Data" + }; +const char *frame_subtype_names[4][16] = { + { + "Association Request", "Association Response", "Reassociation Request", "Reassociation Response", + "Probe Request", "Probe Response", "Timing Advertisement", "Reserved", + "Beacon", "ATIM", "Disassociation", "Authentication", "Deauthentication", "Action", "Action No Ack (NACK)", "Reserved" + }, + { + "Reserved", "Reserved", "Trigger[3]", "TACK", + "Beamforming Report Poll", "VHT/HE NDP Announcement", "Control Frame Extension", "Control Wrapper", + "Block Ack Request (BAR)", "Block Ack (BA)", "PS-Poll", "RTS", "CTS", "ACK", "CF-End", "CF-End + CF-ACK" + }, + { + "Data", "Reserved", "Reserved", "Reserved", + "Null (no data)", "Reserved", "QoS Data", "QoS Data + CF-ACK", + "QoS Data + CF-Poll", "QoS Data + CF-ACK + CF-Poll", "QoS Null (no data)", "Reserved", "QoS CF-Poll (no data)", "QoS CF-ACK + CF-Poll (no data)", "Reserved", "Reserved" + }, + { + "DMG Beacon", "S1G Beacon", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved" + } + }; + +void monitor_mode_cb(void *data, int itf, size_t len, const uint8_t *buf) { + uint16_t offset_80211 = 0; + if (cyw43_state.is_monitor_mode == MONITOR_RADIOTAP) + offset_80211 = *(uint16_t*)(buf+2); + uint8_t frame_type = buf[offset_80211] >> 2 & 3; + uint8_t frame_subtype = buf[offset_80211] >> 4; + printf("Frame type=%d (%s) subtype=%d (%s) len=%d data=", frame_type, frame_type_names[frame_type], frame_subtype, frame_subtype_names[frame_type][frame_subtype], len); + for (size_t i = 0; i < len; ++i) { + printf("%02x ", buf[i]); + } + printf("\n"); + return; +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + + const char *ap_name = "picow_test"; + const char *password = "password"; + uint32_t channels[] = {1, 6, 11}; + uint8_t chan_idx = 0; + cyw43_arch_enable_ap_mode(ap_name, password, CYW43_AUTH_WPA2_AES_PSK); + cyw43_set_monitor_mode(&cyw43_state, MONITOR_IEEE80211, monitor_mode_cb); + + while(true) { + cyw43_wifi_ap_set_channel(&cyw43_state, channels[chan_idx]); + chan_idx = (chan_idx + chan_idx) % (sizeof(channels)/sizeof(channels[0])); + sleep_ms(200); + } + + cyw43_arch_deinit(); + return 0; +} \ No newline at end of file diff --git a/patches/002_cyw43-add_monitor_mode_support.patch b/patches/002_cyw43-add_monitor_mode_support.patch new file mode 100644 index 0000000..1952eb8 --- /dev/null +++ b/patches/002_cyw43-add_monitor_mode_support.patch @@ -0,0 +1,129 @@ +diff --git a/src/cyw43.h b/src/cyw43.h +index 0900440..97fcd4e 100644 +--- a/src/cyw43.h ++++ b/src/cyw43.h +@@ -149,6 +149,9 @@ typedef struct _cyw43_t { + #if CYW43_ENABLE_BLUETOOTH + bool bt_loaded; + #endif ++ ++ uint8_t is_monitor_mode; ++ void (*monitor_mode_cb)(void *, int, size_t, const uint8_t *); + } cyw43_t; + + extern cyw43_t cyw43_state; +@@ -678,6 +681,29 @@ int cyw43_bluetooth_hci_write(uint8_t *buf, size_t len); + void cyw43_bluetooth_hci_process(void); + #endif + ++/** ++ * @brief Callback function to handle monitor mode data. ++ * ++ * @param cb_data The driver state object. ++ * @param itf The interface identifier. ++ * @param len The length of the received data. ++ * @param buf A pointer to the buffer containing the received data. ++ */ ++void cyw43_cb_monitor_mode(void *cb_data, int itf, size_t len, const uint8_t *buf); ++ ++/** ++ * @brief Set the monitor mode of the CYW43 device. ++ * ++ * @param self the driver state object. This should always be \c &cyw43_state ++ * @param value The value to set monitor mode (1 for enabled, 0 for disabled). ++ * @param cb A callback function to handle monitor mode data. ++ * The callback should have the signature: ++ * `void (*cb)(void *, int, size_t, const uint8_t *)` ++ * ++ * @return 0 on success, an error code on failure. ++ */ ++int cyw43_set_monitor_mode(cyw43_t *self, int value, void (*cb)(void *, int, size_t, const uint8_t *)); ++ + //!\} // cyw43_driver doxygen group + + #endif // CYW43_INCLUDED_CYW43_H +diff --git a/src/cyw43_ctrl.c b/src/cyw43_ctrl.c +index dde5ca7..fb28519 100644 +--- a/src/cyw43_ctrl.c ++++ b/src/cyw43_ctrl.c +@@ -794,3 +794,23 @@ int cyw43_bluetooth_hci_write(uint8_t *buf, size_t len) { + return 0; + } + #endif ++ ++void cyw43_cb_monitor_mode(void *cb_data, int itf, size_t len, const uint8_t *buf) { ++ cyw43_t *self = cb_data; ++ if(self->is_monitor_mode && self->monitor_mode_cb) ++ self->monitor_mode_cb(cb_data, itf, len, buf); ++} ++ ++int cyw43_set_monitor_mode(cyw43_t *self, int value, void (*cb)(void *, int, size_t, const uint8_t *)) { ++ CYW43_THREAD_ENTER; ++ int ret = cyw43_ensure_up(self); ++ if (ret) { ++ CYW43_THREAD_EXIT; ++ return ret; ++ } ++ cyw43_ll_set_monitor_mode(&self->cyw43_ll, value); ++ self->is_monitor_mode = value; ++ self->monitor_mode_cb = cb; ++ CYW43_THREAD_EXIT; ++ return 0; ++} +\ No newline at end of file +diff --git a/src/cyw43_ll.c b/src/cyw43_ll.c +index 11fb696..7e10b8d 100644 +--- a/src/cyw43_ll.c ++++ b/src/cyw43_ll.c +@@ -1167,6 +1167,7 @@ void cyw43_ll_process_packets(cyw43_ll_t *self_in) { + cyw43_cb_process_async_event(self, cyw43_ll_parse_async_event(len, buf)); + } else if (ret == DATA_HEADER) { + cyw43_cb_process_ethernet(self->cb_data, len >> 31, len & 0x7fffffff, buf); ++ cyw43_cb_monitor_mode(self->cb_data, len >> 31, len & 0x7fffffff, buf); + } else if (CYW43_USE_SPI && ret == CYW43_ERROR_WRONG_PAYLOAD_TYPE) { + // Ignore this error when using the SPI interface. It can occur when there + // is a lot of traffic over the SPI (eg sending UDP packets continuously) +@@ -1838,26 +1839,12 @@ static uint32_t cyw43_read_iovar_u32(cyw43_int_t *self, const char *var, uint32_ + return cyw43_get_le32(buf); + } + +-#if 0 + #define WLC_SET_MONITOR (108) +-int cyw43_set_monitor_mode(cyw43_ll_t *self, int value) { +- CYW_THREAD_ENTER; +- int ret = cyw43_ensure_up(self); +- if (ret) { +- CYW_THREAD_EXIT; +- return ret; +- } +- +- CYW_ENTER; +- self->is_monitor_mode = value; +- cyw43_write_iovar_u32(self, "allmulti", value, WWD_STA_INTERFACE); +- cyw43_set_ioctl_u32(self, WLC_SET_MONITOR, value, WWD_STA_INTERFACE); +- CYW_EXIT; +- CYW_THREAD_EXIT; +- +- return 0; ++void cyw43_ll_set_monitor_mode(cyw43_ll_t *self, int value) { ++ //self->is_monitor_mode = value; ++ cyw43_write_iovar_u32(CYW_INT_FROM_LL(self), "allmulti", value, WWD_STA_INTERFACE); ++ cyw43_set_ioctl_u32(CYW_INT_FROM_LL(self), WLC_SET_MONITOR, value, WWD_STA_INTERFACE); + } +-#endif + + // Requires cyw43_ll_bus_init to have been called first + int cyw43_ll_wifi_on(cyw43_ll_t *self_in, uint32_t country) { +diff --git a/src/cyw43_ll.h b/src/cyw43_ll.h +index 2750238..c98fc08 100644 +--- a/src/cyw43_ll.h ++++ b/src/cyw43_ll.h +@@ -277,6 +277,8 @@ void cyw43_ll_process_packets(cyw43_ll_t *self); + int cyw43_ll_ioctl(cyw43_ll_t *self, uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface); + int cyw43_ll_send_ethernet(cyw43_ll_t *self, int itf, size_t len, const void *buf, bool is_pbuf); + ++void cyw43_ll_set_monitor_mode(cyw43_ll_t *self, int value); ++ + int cyw43_ll_wifi_on(cyw43_ll_t *self, uint32_t country); + int cyw43_ll_wifi_pm(cyw43_ll_t *self, uint32_t pm, uint32_t pm_sleep_ret, uint32_t li_bcn, uint32_t li_dtim, uint32_t li_assoc); + int cyw43_ll_wifi_get_pm(cyw43_ll_t *self, uint32_t *pm, uint32_t *pm_sleep_ret, uint32_t *li_bcn, uint32_t *li_dtim, uint32_t *li_assoc); diff --git a/script/patch b/script/patch index f11f0ad..2b83d55 100755 --- a/script/patch +++ b/script/patch @@ -3,6 +3,7 @@ CYW43_DRIVER_DIR="cyw43-driver" PATCH_DIR="patches" PATCH_001="$PATCH_DIR/001_cyw43-driver_expose_backplane_read.patch" +PATCH_002="$PATCH_DIR/002_cyw43-add_monitor_mode_support.patch" # exit on error set -e @@ -25,3 +26,13 @@ fi # apply patch 001 printf "applying patch %s\n" $PATCH_001 cd $CYW43_DRIVER_DIR && git apply ../$PATCH_001 && cd .. + +# check if patch 002 exists +if ! [ -f $PATCH_002 ]; then + printf "Patchfile 2 not found, expected: %s\n" $PATCH_002 + exit 2 +fi + +# apply patch 002 +printf "applying patch %s\n" $PATCH_002 +cd $CYW43_DRIVER_DIR && git apply ../$PATCH_002 && cd .. \ No newline at end of file