espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.63k stars 7.28k forks source link

esp_wifi_set_channel isn't working coherently for stepped sniffing (IDFGH-2747) #4815

Closed brian90013 closed 4 years ago

brian90013 commented 4 years ago

Environment

Problem description

I created a Wi-Fi sniffing application based on the IDF example simple-sniffer. One additional feature is the ability for the sniffer to step through all Wi-Fi channels automatically. Recent testing has showed the same SSID/BSSID show up on one, two, or three adjacent physical channels leading me to suspect a problem with coherence/settling time/synchronization between the sniffer component and the esp_wifi_set_channel() call. In the midst of my testing, I discovered the esp_wifi_set_channel() call in the v4.0 simple-sniffer doesn't currently work.

Problem details

My software solution to support stepping is very simple. I have a FreeRTOS timer set for 333ms and its handler simply calls esp_wifi_set_channel(). A static variable increments the current channel number and wraps when necessary.

This code did produce results from the channels I expected based on contemporaneous surveys via laptops and mobile apps. However, I noticed that my own APs, all on channel 6, would also occasionally show up on channel 7 and even channel 8. I doubt my router is actually drifting this much (I'd never keep a Wi-Fi connection) and the ESP32 reports this same behavior from other nearby routers).

My assumption is that when I call esp_wifi_set_channel(), perhaps at some point during or following the call, the software believes it is on the new channel while the hardware has not yet settled on the new channel. If this was the case, the software would be tagging results with the new channel number that were still from the old physical channel. Of course, I have no way to prove this with the binary library. But it would explain why I see my APs on channels 6, 7, 8 and never 4 or 5.

I tried a number of approaches to stop mis-labeled sniffing results. Most were thwarted by the requirement that sniffing be active via esp_wifi_set_promiscuous(true) when changing the channel. I would much prefer to disable sniffing (which would hopefully empty any queues), change the channel (assuming the hardware had settled), then re-enable sniffing. Likewise, my attempts to use esp_wifi_stop()/start() or even esp_wifi_deinit()/init() as ways of clearing out bad data didn't work since I didn't know how to set the sniffing channel until promiscuous collection was enabled.

Recent commit

Just this week I saw a new commit on the v4.0 branch 5894e049 with the tag "Fix some WiFi issues". On the list were:

That seemed close to my problem, so today I rebuilt my application and saw the default behavior of esp_wifi_set_channel() had changed. Now I get a

W (11473) wifi: STA is scanning or connecting, or AP has connected with external STAs, cannot set channel

message when calling esp_wifi_set_channel() while sniffing is running. However, calling esp_wifi_set_promiscuous(false) then esp_wifi_set_channel() gives the same error message even though I expected sniffing to be disabled. So the behavior of esp_wifi_set_channel() has changed but still doesn't seem to support being synchronized with the results and the hardware.

Example project doesn't work

After seeing the behavior of esp_wifi_set_channel() had changed, I looked at the simple_sniffer example on the v4.0 branch. It has the following:

wifi_filter.filter_mask = sniffer->filter; esp_wifi_set_promiscuous_filter(&wifi_filter); esp_wifi_set_promiscuous_rx_cb(wifi_sniffer_cb); SNIFFER_CHECK(esp_wifi_set_promiscuous(true) == ESP_OK, "start wifi promiscuous failed", err_start); esp_wifi_set_channel(sniffer->channel, WIFI_SECOND_CHAN_NONE); ESP_LOGI(SNIFFER_TAG, "start WiFi promiscuous ok");

that fails with the same error message W (11473) wifi: STA is scanning or connecting, or AP has connected with external STAs, cannot set channel

on my WROVER kit.

Summary

I believe there is a problem correctly tagging promiscuous results with the accurate channel number. I think this is complicated by the behavior of the set_channel call, which before had to be done while promiscuous mode was enabled. I'm not sure of the exact changes made by the recent commit, but given the example doesn't run properly, I'm asking for more clarity or a review of this code.

Thank you very much for your time and assistance. I've had great results with the IDF and the community.

brian90013 commented 4 years ago

It appears the breakage of esp_wifi_set_channel() was first reported in #4706 and acknowledged as a bug. I would suggest that my issue also requests:

HarveyRong-Esp commented 4 years ago

Hi @uname2219, Suggest to use master commit 4822e46. Try it.

brian90013 commented 4 years ago

Hello, Thank you for the response. I agree that the master branch (tested on e2e4cd1) and v4.1 can again use the esp_wifi_set_channel function. Great news! It would be very helpful if this could be back ported to v4.0 (if it hasn't already).

With that fixed, I return to the main part of my issue - I believe scan results are being marked with the wrong channel number (or I am not changing/reporting channels properly). I've created a minimal test program, ran it on master e2e4cd1, and show the results below. I consistently see my 3 APs (all on channel 6) show up on channels 5, 6, and 7.

I initially called esp_wifi_set_channel without the disable/enable of promiscuous mode but saw the same results. After the first cycle, you can track the channel number using the enable_sniffer log messages.

Test program

#include "esp_wifi.h"
#include "nvs_flash.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
#include <string.h>

typedef struct {
    unsigned channel;
    unsigned payload_len; 
    uint8_t payload[64];
} sniffer_packet_info_t;

static QueueHandle_t work_queue;

static void wifi_sniffer_cb(void * promisc_pkt, wifi_promiscuous_pkt_type_t type)
{
    wifi_promiscuous_pkt_t * param = (wifi_promiscuous_pkt_t *)promisc_pkt;
    if(work_queue && !param->rx_ctrl.rx_state && (param->rx_ctrl.sig_len > 4))
    {
        sniffer_packet_info_t packet;
        packet.channel = param->rx_ctrl.channel;
        packet.payload_len = param->rx_ctrl.sig_len - 4;
        if(packet.payload_len > 64) { packet.payload_len = 64; }
        memcpy(packet.payload, param->payload, packet.payload_len);
        xQueueSend(work_queue, &packet, 0);
    }
}

static void wifi_channel_timer(TimerHandle_t xTimer)
{
    static int channel = 1;
    esp_wifi_set_promiscuous(false);
    esp_wifi_set_channel(channel++, WIFI_SECOND_CHAN_NONE);
    esp_wifi_set_promiscuous(true);
    if(channel == 15) { channel = 1; }
}

static uint8_t * tlv_search(uint8_t * buffer, const int buffer_len, const int tag)
{
    uint8_t * pos = buffer;
    int len = buffer_len;
    while(len >= 1 + 1)
    {
        uint8_t t = *pos;
        uint8_t l = *(pos + 1);
        if(len >= 1 + 1 + l)
        {
            if(t == tag)
            {
                return pos;
            }
            pos += 1 + 1 + l;
            len -= 1 + 1 + l;
        }
        else { break; }
    }
    return NULL;
}

void app_main(void)
{
    nvs_flash_init();
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_start());

    work_queue = xQueueCreate(128, sizeof(sniffer_packet_info_t));
    esp_wifi_set_promiscuous_rx_cb(wifi_sniffer_cb);
    esp_wifi_set_promiscuous(true);
    TimerHandle_t xTimer = xTimerCreate("channel_timer", pdMS_TO_TICKS(333), 1, NULL, wifi_channel_timer);
    xTimerStart(xTimer, 0);

    sniffer_packet_info_t packet;
    while(1)
    {
        xQueueReceive(work_queue, &packet, portMAX_DELAY);
        if((packet.payload_len >= 24) &&
            ((((*(uint16_t *)&packet.payload[0]) & 0x000c) >> 2) == 0) &&
            ((((*(uint16_t *)&packet.payload[0]) & 0x00f0) >> 4) == 8))
        {
            uint8_t * ssid = tlv_search(packet.payload + 36, packet.payload_len - 36, 0);
            if(ssid)
            {
                printf("%i %.*s\n", packet.channel, ssid[1], &ssid[2]);
            }
        }
    }
}

Test output

I (1009) wifi:wifi driver task: 3ffc8c9c, prio:23, stack:3584, core=0
I (1009) system_api: Base MAC address is not set
I (1009) system_api: read default base MAC address from EFUSE
I (1029) wifi:wifi firmware version: b88ceb8
I (1029) wifi:wifi certification version: v7.0
I (1029) wifi:config NVS flash: enabled
I (1029) wifi:config nano formating: disabled
I (1029) wifi:Init dynamic tx buffer num: 32
I (1029) wifi:Init data frame dynamic rx buffer num: 32
I (1039) wifi:Init management frame dynamic rx buffer num: 32
I (1039) wifi:Init management short buffer num: 32
I (1049) wifi:Init static tx buffer num: 16
I (1049) wifi:Init static rx buffer size: 1600
I (1059) wifi:Init static rx buffer num: 10
I (1059) wifi:Init dynamic rx buffer num: 32
I (1159) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
I (1159) wifi:mode : softAP (30:bb:cc:dd:ee:ff) <<- Changed
I (1159) wifi:Total power save buffer number: 8
I (1169) wifi:Init max length of beacon: 752/752
I (1169) wifi:Init max length of beacon: 752/752
I (1169) wifi:ic_enable_sniffer
6 network1
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
I (1509) wifi:ic_disable_sniffer
I (1509) wifi:ic_enable_sniffer
I (1839) wifi:ic_disable_sniffer
I (1839) wifi:ic_enable_sniffer
I (2169) wifi:ic_disable_sniffer
I (2169) wifi:ic_enable_sniffer
I (2499) wifi:ic_disable_sniffer
I (2499) wifi:ic_enable_sniffer
I (2829) wifi:ic_disable_sniffer
I (2829) wifi:ic_enable_sniffer
5 network2
5 network3
5 network1
5 network2
5 network3
5 network1
5 network3
5 network1
I (3159) wifi:ic_disable_sniffer
I (3159) wifi:ic_enable_sniffer
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
I (3489) wifi:ic_disable_sniffer
I (3489) wifi:ic_enable_sniffer
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
I (3819) wifi:ic_disable_sniffer
I (3819) wifi:ic_enable_sniffer
I (4149) wifi:ic_disable_sniffer
I (4149) wifi:ic_enable_sniffer
I (4479) wifi:ic_disable_sniffer
I (4479) wifi:ic_enable_sniffer
I (4809) wifi:ic_disable_sniffer
I (4809) wifi:ic_enable_sniffer
I (5139) wifi:ic_disable_sniffer
I (5139) wifi:ic_enable_sniffer
I (5469) wifi:ic_disable_sniffer
I (5469) wifi:ic_enable_sniffer
I (5799) wifi:ic_disable_sniffer
I (5799) wifi:ic_enable_sniffer
I (6129) wifi:ic_disable_sniffer
I (6129) wifi:ic_enable_sniffer
I (6459) wifi:ic_disable_sniffer
I (6459) wifi:ic_enable_sniffer
I (6789) wifi:ic_disable_sniffer
I (6789) wifi:ic_enable_sniffer
I (7119) wifi:ic_disable_sniffer
I (7119) wifi:ic_enable_sniffer
I (7449) wifi:ic_disable_sniffer
I (7449) wifi:ic_enable_sniffer
5 network2
5 network1
5 network2
5 network3
5 network1
5 network2
5 network3
5 network1
I (7779) wifi:ic_disable_sniffer
I (7779) wifi:ic_enable_sniffer
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
I (8109) wifi:ic_disable_sniffer
I (8109) wifi:ic_enable_sniffer
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
7 network2
I (8439) wifi:ic_disable_sniffer
I (8439) wifi:ic_enable_sniffer
I (8769) wifi:ic_disable_sniffer
I (8769) wifi:ic_enable_sniffer
I (9099) wifi:ic_disable_sniffer
I (9099) wifi:ic_enable_sniffer
I (9429) wifi:ic_disable_sniffer
I (9429) wifi:ic_enable_sniffer
I (9759) wifi:ic_disable_sniffer
I (9759) wifi:ic_enable_sniffer
I (10089) wifi:ic_disable_sniffer
I (10089) wifi:ic_enable_sniffer
I (10419) wifi:ic_disable_sniffer
I (10419) wifi:ic_enable_sniffer
I (10749) wifi:ic_disable_sniffer
I (10749) wifi:ic_enable_sniffer
I (11079) wifi:ic_disable_sniffer
I (11079) wifi:ic_enable_sniffer
I (11409) wifi:ic_disable_sniffer
I (11409) wifi:ic_enable_sniffer
I (11739) wifi:ic_disable_sniffer
I (11739) wifi:ic_enable_sniffer
I (12069) wifi:ic_disable_sniffer
I (12069) wifi:ic_enable_sniffer
5 network2
5 network2
5 network3
5 network1
5 network3
5 network1
I (12399) wifi:ic_disable_sniffer
I (12399) wifi:ic_enable_sniffer
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
I (12729) wifi:ic_disable_sniffer
I (12729) wifi:ic_enable_sniffer
7 network3
7 network1
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
7 network2
7 network3
I (13059) wifi:ic_disable_sniffer
I (13059) wifi:ic_enable_sniffer
I (13389) wifi:ic_disable_sniffer
I (13389) wifi:ic_enable_sniffer
I (13719) wifi:ic_disable_sniffer
I (13719) wifi:ic_enable_sniffer
I (14049) wifi:ic_disable_sniffer
I (14049) wifi:ic_enable_sniffer
I (14379) wifi:ic_disable_sniffer
I (14379) wifi:ic_enable_sniffer
I (14709) wifi:ic_disable_sniffer
I (14709) wifi:ic_enable_sniffer
I (15039) wifi:ic_disable_sniffer
I (15039) wifi:ic_enable_sniffer
I (15369) wifi:ic_disable_sniffer
I (15369) wifi:ic_enable_sniffer
I (15699) wifi:ic_disable_sniffer
I (15699) wifi:ic_enable_sniffer
I (16029) wifi:ic_disable_sniffer
I (16029) wifi:ic_enable_sniffer
I (16359) wifi:ic_disable_sniffer
I (16359) wifi:ic_enable_sniffer
I (16689) wifi:ic_disable_sniffer
I (16689) wifi:ic_enable_sniffer
5 network2
5 network2
5 network3
5 network1
5 network2
5 network3
5 network1
I (17019) wifi:ic_disable_sniffer
I (17019) wifi:ic_enable_sniffer
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
6 network2
6 network3
6 network1
6 network2
I (17349) wifi:ic_disable_sniffer
I (17349) wifi:ic_enable_sniffer
7 network1
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
7 network2
7 network3
7 network1
HarveyRong-Esp commented 4 years ago

Hi @uname2219, Backported to v4.0. (commit fb14ab0) Regarding your feedback believe scan results are being marked with the wrong channel number (or I am not changing/reporting channels properly). I will debug the problem and update it in time.

makar-sasha commented 4 years ago

Hello. I have loaded latest master branch (ESP-IDF v4.2-dev-910-g4fe04f115-dirty) and started wifi -> station example. adding esp_wifi_set_channel (to the final lines where wifi is connected) results in the "STA is scanning or connecting, or AP has connected with external STAs, cannot set channel" error. @esp-HarveyRong could you specify is it by design? thanks!

HarveyRong-Esp commented 4 years ago

Hi @makar-sasha, esp_wifi_set_channel In STA mode, if an external AP has been connected, its channel is switched to the channel of the external AP. At this time, call esp_wifi_set_channel will not take effect.

makar-sasha commented 4 years ago

thanks @esp-HarveyRong for you comment.

brian90013 commented 4 years ago

@esp-HarveyRong hello and thank you for debugging the problem. I have also done similar tests with a TI Wi-Fi chip and a standalone Alfa Network USB Wi-Fi dongle. I see this phenomenon with all three devices on a number of networks and now believe it is a function of the adjacent channel rejection and processing gain of the Wi-Fi driver.

My latest testing used a fixed Wi-Fi channel assignment, instead of the hopping function I used above. I called set_channel(5) and left the chip on channel 5. I would see my APs, with an RSSI 2 or 3 below the value I see on channel 6. Besides confirming channel 6 in the router GUI, I also verified the DS Parameter Set IE in the beacon contained channel 6 as the active channel.

I saw similar behavior on channel 7. I then tried set_channel(2) and in addition to occasional beacons from APs that are normally on channel 1, I got a rare beacon from one of my channel 6 APs. This was observed on all three hardware platforms.

My guess is since channels 5 and 6 mostly overlap, and since the power level of my APs in my own home is high compared to other networks, there's enough SNR to decode the beacon even when off-tuned. The ESP32 does decode more beacons on channel 5 than either other device, which makes me wonder about the adjacent channel filtering in place.

I would love to hear if you observe this behavior as well and if there is anything that can be tweaked with the Wi-Fi RF configuration.

zhangyanjiaoesp commented 4 years ago

@brian90013 802.11 generally supports 14 channels (the 14th channel is usually not used). Their central frequencies are different, but because they all occupy a certain frequency range, there is some overlap. The figure below shows the common channel division of the 2.4GHz band. Each channel has an effective width of 20MHz, plus a mandatory isolation band of 2MHz (similar to the isolation band on a highway). image

It is easy to see that there is no overlap between channels 1, 6 and 11, and there is spectrum overlap between other channels.

liuzfesp commented 4 years ago

Hi @brainstorm Here is the description about the WiFi TX Signal from WiFi spec:

image

For the 2.4 GHz band, when transmitting in a 20 MHz channel, the transmitted spectrum shall have a 0 dBr (dB relative to the maximum spectral density of the signal) bandwidth not exceeding 18 MHz, –20 dBr at 11 MHz frequency offset, –28 dBr at 20 MHz frequency offset, and the maximum of –45 dBr and –53 dBm/ MHz at 30 MHz frequency offset and above. The transmitted spectral density of the transmitted signal shall fall within the spectral mask, as shown in above picture. The measurements shall be made using a 100 kHz resolution bandwidth and a 30 kHz video bandwidth.

image

For the 2.4 GHz band, when transmitting in a 40 MHz channel, the transmitted spectrum shall have a 0 dBr bandwidth not exceeding 38 MHz, –20 dBr at 21 MHz frequency offset, –28 dBr at 40 MHz offset, and the maximum of –45 dBr and –56 dBm/MHz at 60 MHz frequency offset and above. The transmitted spectral density of the transmitted signal shall fall within the spectral mask, as shown in above picture.

Notes that all 2.4G channel except channel 14 has 5Mhz bandwidth.

Alvin1Zhang commented 4 years ago

Thanks for reporting, feel free to reopen if the issue still happens, or please file a new ticket for new issue, thanks.