diff --git a/LICENSE b/LICENSE index 6145f231..82a5eca3 100644 --- a/LICENSE +++ b/LICENSE @@ -217,6 +217,11 @@ Arduino-LMIC Library TTN OTAA Example https://github.com/matthijskooijman/arduino-lmic/blob/master/examples/ttn-otaa/ +and it's fork + +LoraWAN-in-C library, adapted to run under the Arduino environment +https://github.com/jpmeijers/arduino-lmic + under this Licence: "License diff --git a/README.md b/README.md index df368dfa..f64c29d0 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,9 @@ For the LoPy/LoPy4 the original Pycom firmware is not needed here, so there is n Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so. -Disclosure: The Paxcounter code stores scanned MAC adresses in the device's RAM, and keeps it in RAM temporary for a configurable scan cycle time (default 240 seconds). After each scan cycle the collected MAC data is erased from RAM. MAC data never is transferred to the LoRaWAN network. No kind of tracking and no persistent storing of MAC data or timestamps on the device and no other kind of analytics than counting is implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are made visible and scanned by this code. The same applies to Bluetooth MACs, if the bluetooth option in the code is enabled. +# Privacy disclosure + +Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 240 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by salting and hashing MAC adresses. The random salt value changes after each scan cycle. Identifiers and MAC adresses are never transferred to the LoRaWAN network. No persistent storing of MAC adresses, identifiers or timestamps and no other kind of analytics than counting are implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are sniffed and processed by this code. If the bluetooth option in the code is enabled, bluetooth MACs are scanned and processed by the included BLE stack, and counted by this code. # Payload format description diff --git a/platformio.ini b/platformio.ini index 88ba9fc3..4c4cb5c4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,9 +10,9 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = heltec_wifi_lora_32 +;env_default = heltec_wifi_lora_32 ;env_default = ttgov1 -;env_default = ttgov2 +env_default = ttgov2 ;env_default = lopy ;env_default = lopy4 ;env_default = lolin32lite_lora @@ -70,7 +70,9 @@ board = esp32dev framework = arduino monitor_baud = 115200 upload_speed = 921600 -lib_deps = U8g2 +lib_deps = + U8g2 + ESP32 BLE Arduino@>=0.4.9 build_flags = ;set log level, we need build_flag for this, otherwise we can't use ESP_LOGx in arduino framework -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE diff --git a/src/blecount.cpp b/src/blecount.cpp index 3872d360..d4076ee9 100644 --- a/src/blecount.cpp +++ b/src/blecount.cpp @@ -27,6 +27,6 @@ void BLECount() { u8x8.clearLine(3); u8x8.setCursor(0,3); blenum=foundDevices.getCount(); - u8x8.printf("BLE#: %4i",blenum); + u8x8.printf("BLE#: %-5i",blenum); } #endif \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index 8d6ed3fa..58948a7d 100644 --- a/src/globals.h +++ b/src/globals.h @@ -12,7 +12,6 @@ #include // Struct holding devices's runtime configuration - typedef struct { int8_t lorasf; // 7-12, lora spreadfactor int8_t txpower; // 2-15, lora tx power @@ -33,9 +32,10 @@ extern configData_t cfg; extern uint8_t mydata[]; extern uint64_t uptimecounter; extern osjob_t sendjob; -extern int macnum, blenum, countermode, screensaver, adrmode, lorasf, txpower, rlim; +extern uint16_t macnum, blenum, salt; +extern int countermode, screensaver, adrmode, lorasf, txpower, rlim; extern bool joinstate; -extern std::set > macs; +extern std::set macs; #ifdef HAS_DISPLAY extern HAS_DISPLAY u8x8; diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 85164280..64eea864 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -79,10 +79,10 @@ void printKeys(void) { #endif // VERBOSE void do_send(osjob_t* j){ - mydata[0] = (macnum & 0x0000ff00) >> 8; - mydata[1] = macnum & 0x000000ff; - mydata[2] = (blenum & 0x0000ff00) >> 8; - mydata[3] = blenum & 0x000000ff; + mydata[0] = (macnum & 0xff00) >> 8; + mydata[1] = macnum & 0x00ff; + mydata[2] = (blenum & 0xff00) >> 8; + mydata[3] = blenum & 0x00ff; // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { diff --git a/src/main.cpp b/src/main.cpp index c1db254a..a54489cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,9 +25,6 @@ Refer to LICENSE.txt file in repository for more details. #include "main.h" #include "globals.h" -// std::set for unified array functions -#include - // OLED driver #include @@ -45,11 +42,11 @@ configData_t cfg; // struct holds current device configuration osjob_t sendjob, initjob; // LMIC // Initialize global variables -int macnum = 0, blenum = 0; +uint16_t macnum = 0, blenum = 0, salt; uint64_t uptimecounter = 0; bool joinstate = false; -std::set > macs; // storage holds MAC frames +std::set macs; // associative container holds filtered MAC adresses // this variable will be changed in the ISR, and read in main loop static volatile bool ButtonTriggered = false; @@ -70,16 +67,29 @@ void eraseConfig(void); void saveConfig(void); void loadConfig(void); - /* begin LMIC specific parts ------------------------------------------------------------ */ +// LMIC enhanced Pin mapping +const lmic_pinmap lmic_pins = { + .mosi = PIN_SPI_MOSI, + .miso = PIN_SPI_MISO, + .sck = PIN_SPI_SCK, + .nss = PIN_SPI_SS, + .rxtx = LMIC_UNUSED_PIN, + .rst = RST, + .dio = {DIO0, DIO1, DIO2} +}; + // defined in lorawan.cpp void gen_lora_deveui(uint8_t * pdeveui); void RevBytes(unsigned char* b, size_t c); - #ifdef VERBOSE void printKeys(void); -#endif // VERBOSE +#endif + +// LMIC functions +void onEvent(ev_t ev); +void do_send(osjob_t* j); // LMIC callback functions void os_getDevKey (u1_t *buf) { @@ -102,21 +112,6 @@ void os_getDevEui (u1_t* buf) { gen_lora_deveui(buf); // generate DEVEUI from device's MAC } -// LMIC enhanced Pin mapping -const lmic_pinmap lmic_pins = { - .mosi = PIN_SPI_MOSI, - .miso = PIN_SPI_MISO, - .sck = PIN_SPI_SCK, - .nss = PIN_SPI_SS, - .rxtx = LMIC_UNUSED_PIN, - .rst = RST, - .dio = {DIO0, DIO1, DIO2} -}; - -// LMIC functions -void onEvent(ev_t ev); -void do_send(osjob_t* j); - // LoRaWAN Initjob static void lora_init (osjob_t* j) { // reset MAC state @@ -193,7 +188,7 @@ void wifi_sniffer_loop(void * pvParameters) { configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check uint8_t channel = 1; int nloop=0, lorawait=0; - + while (true) { nloop++; vTaskDelay(cfg.wifichancycle*10 / portTICK_PERIOD_MS); @@ -208,20 +203,21 @@ void wifi_sniffer_loop(void * pvParameters) { // execute BLE count if BLE function is enabled #ifdef BLECOUNTER - if ( cfg.blescan ) + if (cfg.blescan) BLECount(); #endif // Prepare and execute LoRaWAN data upload u8x8.setCursor(0,4); - u8x8.printf("MAC#: %4i", macnum); + u8x8.printf("MAC#: %-5i", macnum); do_send(&sendjob); // send payload vTaskDelay(500/portTICK_PERIOD_MS); yield(); // clear counter if not in cumulative counter mode - if ( cfg.countermode != 1 ) { - macs.erase(macs.begin(), macs.end()); // clear RAM + if (cfg.countermode != 1) { + macs.clear(); // clear macs container + salt = random(65536); // get new 16bit random for salting hashes macnum = 0; u8x8.clearLine(0); u8x8.clearLine(1); // clear Display counter } @@ -384,11 +380,14 @@ void setup() { antenna_init(); #endif + // initialize salt value using esp_random() called by random in arduino-esp32 core + salt = random(65536); // get new 16bit random for salting hashes + // initialize display init_display(PROGNAME, PROGVERSION); u8x8.setPowerSave(!cfg.screenon); // set display off if disabled u8x8.setCursor(0,5); - u8x8.printf(!cfg.rssilimit ? "RLIM: off" : "RLIM: %4i", cfg.rssilimit); + u8x8.printf(!cfg.rssilimit ? "RLIM: off" : "RLIM: %4i", cfg.rssilimit); u8x8.drawString(0,6,"Join Wait "); // output LoRaWAN keys to console diff --git a/src/main.h b/src/main.h index a32c075b..c01b8e6e 100644 --- a/src/main.h +++ b/src/main.h @@ -1,106 +1,108 @@ -// program version -#define PROGVERSION "1.2.5" // use max 10 chars here! -#define PROGNAME "PAXCNT" - -// Verbose enables serial output -#define VERBOSE 1 // comment out to silence the device, for mute use build option - -// set this to include BLE counting and vendor filter functions -#define VENDORFILTER 1 // comment out if you want to count things, not people -#define BLECOUNTER 1 // comment out if you don't want BLE count - -// BLE scan time -#define BLESCANTIME 30 // [seconds] - -// WiFi Sniffer cycle interval -#define SEND_SECS 120 // [seconds/2] -> 240 sec. - -// WiFi sniffer config -#define WIFI_CHANNEL_MAX 13 -#define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec. - -// Default LoRa Spreadfactor -#define LORASFDEFAULT 9 // 7 ... 12 -#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy -#define RCMDPORT 2 // LoRaWAN Port on which device listenes for remote commands - -// LMIC settings -// define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored -// define hardware specifics settings in platformio.ini as build_flag for hardware environment - -// Select frequency band here according to national regulations -#define CFG_eu868 1 -//#define CFG_us915 1 - -// This is the SX1272/SX1273 radio, which is also used on the HopeRF -// RFM92 boards. -//#define CFG_sx1272_radio 1 -// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on -// the HopeRF RFM95 boards. -//#define CFG_sx1276_radio 1 - -// 16 μs per tick -// LMIC requires ticks to be 15.5μs - 100 μs long -#define US_PER_OSTICK_EXPONENT 4 -#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) -#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) - -// Set this to 1 to enable some basic debug output (using printf) about -// RF settings used during transmission and reception. Set to 2 to -// enable more verbose output. Make sure that printf is actually -// configured (e.g. on AVR it is not by default), otherwise using it can -// cause crashing. -//#define LMIC_DEBUG_LEVEL 1 - -// Enable this to allow using printf() to print to the given serial port -// (or any other Print object). This can be easy for debugging. The -// current implementation only works on AVR, though. -//#define LMIC_PRINTF_TO Serial - -// Any runtime assertion failures are printed to this serial port (or -// any other Print object). If this is unset, any failures just silently -// halt execution. -#define LMIC_FAILURE_TO Serial - -// Uncomment this to disable all code related to joining -//#define DISABLE_JOIN -// Uncomment this to disable all code related to ping -#define DISABLE_PING -// Uncomment this to disable all code related to beacon tracking. -// Requires ping to be disabled too -#define DISABLE_BEACONS - -// Uncomment these to disable the corresponding MAC commands. -// Class A -//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap -//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param -//#define DISABLE_MCMD_SNCH_REQ // set new channel -// Class B -//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING -//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON - -// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the -// same on RX. This ensures that gateways can talk to nodes and vice -// versa, but gateways will not hear other gateways and nodes will not -// hear other nodes. By uncommenting this macro, this inversion is -// disabled and this node can hear other nodes. If two nodes both have -// this macro set, they can talk to each other (but they can no longer -// hear gateways). This should probably only be used when debugging -// and/or when talking to the radio directly (e.g. like in the "raw" -// example). -//#define DISABLE_INVERT_IQ_ON_RX - -// This allows choosing between multiple included AES implementations. -// Make sure exactly one of these is uncommented. -// -// This selects the original AES implementation included LMIC. This -// implementation is optimized for speed on 32-bit processors using -// fairly big lookup tables, but it takes up big amounts of flash on the -// AVR architecture. -// #define USE_ORIGINAL_AES -// -// This selects the AES implementation written by Ideetroon for their -// own LoRaWAN library. It also uses lookup tables, but smaller -// byte-oriented ones, making it use a lot less flash space (but it is -// also about twice as slow as the original). -#define USE_IDEETRON_AES +// program version +#define PROGVERSION "1.2.62" // use max 10 chars here! +#define PROGNAME "PAXCNT" + +// Verbose enables serial output +#define VERBOSE 1 // comment out to silence the device, for mute use build option + +// set this to include BLE counting and vendor filter functions +#define VENDORFILTER 1 // comment out if you want to count things, not people +#define BLECOUNTER 1 // comment out if you don't want BLE count + +// BLE scan time +#define BLESCANTIME 30 // [seconds] + +// WiFi Sniffer cycle interval +#define SEND_SECS 120 // [seconds/2] -> 240 sec. + +// WiFi sniffer config +#define WIFI_CHANNEL_MIN 1 // start channel number where scan begings +#define WIFI_CHANNEL_MAX 13 // total channel number to scan +#define WIFI_MY_COUNTRY "EU" // for Wifi RF settings +#define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec. + +// Default LoRa Spreadfactor +#define LORASFDEFAULT 9 // 7 ... 12 +#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy +#define RCMDPORT 2 // LoRaWAN Port on which device listenes for remote commands + +// LMIC settings +// define hardware independent LMIC settings here, settings of standard library in /lmic/config.h will be ignored +// define hardware specifics settings in platformio.ini as build_flag for hardware environment + +// Select frequency band here according to national regulations +#define CFG_eu868 1 +//#define CFG_us915 1 + +// This is the SX1272/SX1273 radio, which is also used on the HopeRF +// RFM92 boards. +//#define CFG_sx1272_radio 1 +// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on +// the HopeRF RFM95 boards. +//#define CFG_sx1276_radio 1 + +// 16 μs per tick +// LMIC requires ticks to be 15.5μs - 100 μs long +#define US_PER_OSTICK_EXPONENT 4 +#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +//#define LMIC_DEBUG_LEVEL 1 + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Any runtime assertion failures are printed to this serial port (or +// any other Print object). If this is unset, any failures just silently +// halt execution. +#define LMIC_FAILURE_TO Serial + +// Uncomment this to disable all code related to joining +//#define DISABLE_JOIN +// Uncomment this to disable all code related to ping +#define DISABLE_PING +// Uncomment this to disable all code related to beacon tracking. +// Requires ping to be disabled too +#define DISABLE_BEACONS + +// Uncomment these to disable the corresponding MAC commands. +// Class A +//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap +//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param +//#define DISABLE_MCMD_SNCH_REQ // set new channel +// Class B +//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING +//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON + +// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the +// same on RX. This ensures that gateways can talk to nodes and vice +// versa, but gateways will not hear other gateways and nodes will not +// hear other nodes. By uncommenting this macro, this inversion is +// disabled and this node can hear other nodes. If two nodes both have +// this macro set, they can talk to each other (but they can no longer +// hear gateways). This should probably only be used when debugging +// and/or when talking to the radio directly (e.g. like in the "raw" +// example). +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +// #define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +#define USE_IDEETRON_AES diff --git a/src/rcommand.cpp b/src/rcommand.cpp index 46fe8022..e1524e19 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -67,8 +67,9 @@ void set_reset(int val) { break; case 1: // reset MAC counter ESP_LOGI(TAG, "Remote command: reset MAC counter"); - macs.erase(macs.begin(), macs.end()); // clear RAM + macs.clear(); // clear macs container macnum = 0; + salt = random(65536); // get new 16bit random for salting hashes u8x8.clearLine(0); u8x8.clearLine(1); // clear Display counter u8x8.clearLine(5); u8x8.setCursor(0, 5); diff --git a/src/rokkithash.cpp b/src/rokkithash.cpp new file mode 100644 index 00000000..98b6c5c3 --- /dev/null +++ b/src/rokkithash.cpp @@ -0,0 +1,83 @@ +/* + * RokkitHash - Arduino port for Paul Hsieh's "SuperFastHash" + * + * A very quick hash function, (c) Paul Hsieh + * + * See http://www.azillionmonkeys.com/qed/hash.html for more information + * about its inner workings + * + * - Initial Arduino version: 2014 Alex K + * - 8-bit improvements: robtillaart + * - Current maintainer: SukkoPera + * + * See http://forum.arduino.cc/index.php?topic=226686.0 for some talk + * about the various improvements. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +uint32_t rokkit(const char * data, int len) { + uint32_t hash, tmp; + int rem; + + if (len <= 0 || data == 0) return 0; + hash = len; + rem = len & 3; + len >>= 2; + + /* Main loop */ + while (len > 0) { + hash += *((uint16_t*)data); + tmp = (*((uint16_t*)(data+2)) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2*2; + hash += hash >> 11; + len--; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += *((uint16_t*)data); + hash ^= hash << 16; + hash ^= ((signed char)data[2]) << 18; + hash += hash >> 11; + break; + case 2: hash += *((uint16_t*)data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += (signed char)*data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} diff --git a/src/wifisniffer.cpp b/src/wifisniffer.cpp index f2ee2d17..55b9fc19 100644 --- a/src/wifisniffer.cpp +++ b/src/wifisniffer.cpp @@ -14,7 +14,10 @@ // Local logging tag static const char *TAG = "wifisniffer"; -static wifi_country_t wifi_country = {.cc="EU", .schan=1, .nchan=13, .policy=WIFI_COUNTRY_POLICY_AUTO}; +// function defined in rokkithash.cpp +uint32_t rokkit(const char * , int ); + +static wifi_country_t wifi_country = {.cc=WIFI_MY_COUNTRY, .schan=WIFI_CHANNEL_MIN, .nchan=WIFI_CHANNEL_MAX, .policy=WIFI_COUNTRY_POLICY_MANUAL}; typedef struct { unsigned frame_ctrl:16; @@ -37,17 +40,15 @@ extern void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t void wifi_sniffer_init(void) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - cfg.nvs_enable = 0; // we don't want wifi settings from NVRAM - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL) ); - //ESP_ERROR_CHECK(esp_wifi_start()); // not sure if we need this in this application? - //ESP_ERROR_CHECK(esp_wifi_set_max_tx_power(-128)); // we don't need to TX, so we use lowest power level to save energy - wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames - ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter + cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM + wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg + ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); // set locales for RF and channels + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); + ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler)); - ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); + ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode } void wifi_sniffer_set_channel(uint8_t channel) { @@ -58,36 +59,40 @@ void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type) { const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff; const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload; const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; - char counter [10]; + char counter [6]; // uint16_t -> 2 byte -> 5 decimals + '0' terminator -> 6 chars + char macbuf [21]; // uint64_t -> 8 byte -> 20 decimals + '0' terminator -> 21 chars + uint64_t addr2int; + uint32_t vendor2int; + uint16_t hashedmac; + std::pair::iterator, bool> newmac; if (( cfg.rssilimit == 0 ) || (ppkt->rx_ctrl.rssi > cfg.rssilimit )) { // rssi is negative value - uint64_t addr2int = ( (uint64_t)hdr->addr2[0] ) | ( (uint64_t)hdr->addr2[1] << 8 ) | ( (uint64_t)hdr->addr2[2] << 16 ) | \ + addr2int = ( (uint64_t)hdr->addr2[0] ) | ( (uint64_t)hdr->addr2[1] << 8 ) | ( (uint64_t)hdr->addr2[2] << 16 ) | \ ( (uint64_t)hdr->addr2[3] << 24 ) | ( (uint64_t)hdr->addr2[4] << 32 ) | ( (uint64_t)hdr->addr2[5] << 40 ); -#ifdef VENDORFILTER - uint32_t vendor2int = ( (uint32_t)hdr->addr2[2] ) | ( (uint32_t)hdr->addr2[1] << 8 ) | ( (uint32_t)hdr->addr2[0] << 16 ); - +#ifdef VENDORFILTER // uses vendor array with prefiltered OUIs (no local nd no group MACs, bits 0+1 in 1st byte of OUI) + vendor2int = ( (uint32_t)hdr->addr2[2] ) | ( (uint32_t)hdr->addr2[1] << 8 ) | ( (uint32_t)hdr->addr2[0] << 16 ); if ( std::find(vendors.begin(), vendors.end(), vendor2int) != vendors.end() ) { #endif - macs.insert(addr2int); - - // INFO: RSSI when adding MAC - ESP_LOGI(TAG, "WiFi RSSI: %02d", ppkt->rx_ctrl.rssi); - - // if new unique MAC logged increment counter on display - if ( macs.size() > macnum ) { - macnum = macs.size(); - itoa(macnum, counter, 10); + // salt and hash MAC, and if new unique one, store identifier in container and increment counter on display + // https://en.wikipedia.org/wiki/MAC_Address_Anonymization + + addr2int |= (uint64_t) salt << 48; // prepend 12 bit salt to 48 bit MAC + itoa(addr2int, macbuf, 10); // convert 64 bit MAC to base 10 decimal string + hashedmac = rokkit(macbuf, 5); // hash MAC, use 5 chars to fit hash in uint16_t container + newmac = macs.insert(hashedmac); // store hashed MAC if new unique + if (newmac.second) { // first time seen MAC + macnum++; // increment MAC counter + itoa(macnum, counter, 10); // base 10 decimal counter value u8x8.draw2x2String(0, 0, counter); - ESP_LOGI(TAG, "MAC counter: %4i", macnum); + ESP_LOGI(TAG, "#%05i: RSSI %04d -> Salt %04x -> Hash %04x", macnum, ppkt->rx_ctrl.rssi, salt, hashedmac); } - - + #ifdef VENDORFILTER } #endif - } else { - ESP_LOGI(TAG, "Ignoring RSSI %02d (limit: %i)", ppkt->rx_ctrl.rssi, cfg.rssilimit ); - } + } else + ESP_LOGI(TAG, "RSSI %04d -> ignoring (limit: %i)", ppkt->rx_ctrl.rssi, cfg.rssilimit); + yield(); }