Program Listing for File wifi.c

Return to documentation for file (components/wifi_sniffer/wifi.c)

// wifi.c
#include "wifi.h"

#include <string.h>
#include <stdio.h>

#include "esp_wifi.h"
#include "esp_log.h"
#include "sniffer.h"

static const char *TAG = "WIFI_MGR";

//  wifi manager 내부 상태
static wifi_config_t s_sta_config;
static wifi_config_t s_ap_config;
static wifi_mode_t   s_current_mode = WIFI_MODE_NULL;
static bool          s_initialized  = false;

// Forward declarations
static void ensure_ap_defaults(void);
static esp_err_t apply_wifi_mode_internal(wifi_mode_t mode);
static esp_err_t update_sta_ssid(const char *ssid);
static esp_err_t update_sta_password(const char *password);
static esp_err_t update_ap_ssid(const char *ssid);
static esp_err_t update_ap_password(const char *password);

// WIFI AP MODE defaults

#define WIFI_AP_DEFAULT_CHANNEL         1
#define WIFI_AP_DEFAULT_MAX_CONNECTION  4
#define WIFI_AP_DEFAULT_BEACON_INTERVAL 100

// AP 설정 기본값 보장
static void ensure_ap_defaults(void)
{
    if (s_ap_config.ap.channel == 0) {
        s_ap_config.ap.channel = WIFI_AP_DEFAULT_CHANNEL;
    }
    if (s_ap_config.ap.max_connection == 0) {
        s_ap_config.ap.max_connection = WIFI_AP_DEFAULT_MAX_CONNECTION;
    }
    if (s_ap_config.ap.beacon_interval == 0) {
        s_ap_config.ap.beacon_interval = WIFI_AP_DEFAULT_BEACON_INTERVAL;
    }
}

// 모드 문자열을 Wi-Fi 모드로 변환
const char *wifi_mgr_mode_to_string(wifi_mode_t mode)
{
    switch (mode) {
        case WIFI_MODE_NULL: return "null";
        case WIFI_MODE_STA:  return "sta";
        case WIFI_MODE_AP:   return "ap";
        case WIFI_MODE_APSTA:return "ap+sta";
        default:             return "unknown";
    }
}


esp_err_t wifi_mgr_init(void)
{
    if (s_initialized) {
        return ESP_OK;
    }
    s_initialized = true;

    memset(&s_sta_config, 0, sizeof(s_sta_config));
    memset(&s_ap_config, 0, sizeof(s_ap_config));

    (void)esp_wifi_get_config(WIFI_IF_STA, &s_sta_config);
    if (esp_wifi_get_config(WIFI_IF_AP, &s_ap_config) != ESP_OK) {
        memset(&s_ap_config, 0, sizeof(s_ap_config));
    }
    ensure_ap_defaults();
    (void)esp_wifi_get_mode(&s_current_mode);

    ESP_LOGI(TAG, "wifi_mgr_init: mode=%s", wifi_mgr_mode_to_string(s_current_mode));
    return ESP_OK;
}


esp_err_t wifi_mgr_set_mode(wifi_mode_t mode)
{
    ESP_ERROR_CHECK(wifi_mgr_init());
    return apply_wifi_mode_internal(mode);
}

esp_err_t wifi_mgr_set_mode_str(const char *mode_str)
{
    if (mode_str == NULL) {
        return ESP_ERR_INVALID_ARG;
    }

    wifi_mode_t mode = WIFI_MODE_NULL;

    if (strcasecmp(mode_str, "null") == 0) {
        mode = WIFI_MODE_NULL;
    }
    else if (strcasecmp(mode_str, "sta") == 0) {
        mode = WIFI_MODE_STA;
    }
    else if (strcasecmp(mode_str, "ap") == 0) {
        mode = WIFI_MODE_AP;
    }
    else if (strcasecmp(mode_str, "ap+sta") == 0 ||
             strcasecmp(mode_str, "apsta") == 0 ||
             strcasecmp(mode_str, "sta+ap") == 0) {
        mode = WIFI_MODE_APSTA;
    }
    else {
        return ESP_ERR_INVALID_ARG;
    }

    // 모드 적용
    esp_err_t err = wifi_mgr_set_mode(mode);
    if (err != ESP_OK) {
        return err;
    }

    return ESP_OK;
}


esp_err_t wifi_mgr_set_sta_ssid(const char *ssid)
{
    ESP_ERROR_CHECK(wifi_mgr_init());
    esp_err_t err = update_sta_ssid(ssid);
    if (err == ESP_OK &&
        (s_current_mode == WIFI_MODE_STA || s_current_mode == WIFI_MODE_APSTA)) {
        sniffer_enable_promiscuous();
    }
    return err;
}

esp_err_t wifi_mgr_set_sta_password(const char *password)
{
    ESP_ERROR_CHECK(wifi_mgr_init());
    esp_err_t err = update_sta_password(password);
    if (err == ESP_OK &&
        (s_current_mode == WIFI_MODE_STA || s_current_mode == WIFI_MODE_APSTA)) {
        sniffer_enable_promiscuous();
    }
    return err;
}

esp_err_t wifi_mgr_set_ap_ssid(const char *ssid)
{
    ESP_ERROR_CHECK(wifi_mgr_init());
    esp_err_t err = update_ap_ssid(ssid);
    if (err == ESP_OK &&
        (s_current_mode == WIFI_MODE_AP || s_current_mode == WIFI_MODE_APSTA)) {
        sniffer_enable_promiscuous();
    }
    return err;
}

esp_err_t wifi_mgr_set_ap_password(const char *password)
{
    ESP_ERROR_CHECK(wifi_mgr_init());
    esp_err_t err = update_ap_password(password);
    if (err == ESP_OK &&
        (s_current_mode == WIFI_MODE_AP || s_current_mode == WIFI_MODE_APSTA)) {
        sniffer_enable_promiscuous();
    }
    return err;
}

esp_err_t wifi_mgr_set_channel(uint8_t primary)
{
    wifi_mode_t mode = WIFI_MODE_NULL;

    if (primary < 1 || primary > 13) {   // 필요하면 14까지 허용
        return ESP_ERR_INVALID_ARG;
    }

    ESP_ERROR_CHECK(wifi_mgr_init());
    (void)esp_wifi_get_mode(&mode);

    // 1) STA / APSTA인 경우: 경고 또는 막기
    if (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) {
        // 여기서 정책 결정:
        //   - 그냥 막을지
        //   - 또는 경고만 찍고 시도할지
        ESP_LOGW(TAG,
                 "Channel change in STA/APSTA mode may be invalid or break connection. "
                 "Use NULL/AP mode for sniffer channel hopping.");
        // 나는 아예 막는 쪽이 더 안전하다고 봄
        return ESP_ERR_INVALID_STATE;
    }
    // 2) NULL / AP 모드인 경우: 채널 변경 시도
    esp_err_t err = esp_wifi_set_channel(primary, WIFI_SECOND_CHAN_NONE);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "esp_wifi_set_channel failed: %s", esp_err_to_name(err));
        return err;
    }

    // 채널 변경 후 스니퍼 다시 세팅 (필요시)
    sniffer_enable_promiscuous();

    ESP_LOGI(TAG, "Wi-Fi channel set to %u", primary);
    return ESP_OK;
}


void wifi_mgr_print_status(void)
{
    wifi_mode_t mode = WIFI_MODE_NULL;
    (void)esp_wifi_get_mode(&mode);

    wifi_config_t sta;
    wifi_config_t ap;
    memset(&sta, 0, sizeof(sta));
    memset(&ap, 0, sizeof(ap));

    if (esp_wifi_get_config(WIFI_IF_STA, &sta) != ESP_OK) {
        sta = s_sta_config;
    }
    if (esp_wifi_get_config(WIFI_IF_AP, &ap) != ESP_OK) {
        ap = s_ap_config;
    }

    printf("Wi-Fi Status :\n");
    printf("  - Wi-Fi MODE: %s\n", wifi_mgr_mode_to_string(mode));
    printf("  - STA SSID: %s\n", sta.sta.ssid[0] ? (char *)sta.sta.ssid : "<unset>");
    printf("  - STA password: %s (%s)\n", sta.sta.password[0] ? "<set>" : "<empty>",sta.sta.password);
    printf("  - AP SSID: %s\n", ap.ap.ssid[0] ? (char *)ap.ap.ssid : "<unset>");
    printf("  - AP password: %s\n",
           (ap.ap.authmode == WIFI_AUTH_OPEN) ? "<open>" :
           (ap.ap.password[0] ? "<set>" : "<invalid>"));
    printf("  - AP channel: %u\n", ap.ap.channel);
}

// Wi-Fi 모드 적용 내부 함수
static esp_err_t apply_wifi_mode_internal(wifi_mode_t mode)
{
    esp_err_t err = esp_wifi_stop();
    if (err != ESP_OK &&
        err != ESP_ERR_WIFI_NOT_INIT &&
        err != ESP_ERR_WIFI_NOT_STARTED) {
        ESP_LOGE(TAG, "esp_wifi_stop failed: %s", esp_err_to_name(err));
        return err;
    }

    err = esp_wifi_set_mode(mode);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(err));
        return err;
    }

    if (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) {
        err = esp_wifi_set_config(WIFI_IF_STA, &s_sta_config);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "STA config failed: %s", esp_err_to_name(err));
            return err;
        }
    }

    if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA) {
        wifi_config_t cfg = s_ap_config;
        cfg.ap.ssid_len = (uint8_t)strnlen((const char *)cfg.ap.ssid,
                                           sizeof(cfg.ap.ssid));
        size_t pwd_len = strnlen((const char *)cfg.ap.password,
                                 sizeof(cfg.ap.password));
        cfg.ap.authmode = (pwd_len >= 8) ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN;
        if (cfg.ap.authmode == WIFI_AUTH_OPEN) {
            memset(cfg.ap.password, 0, sizeof(cfg.ap.password));
        }
        ensure_ap_defaults();
        err = esp_wifi_set_config(WIFI_IF_AP, &cfg);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "AP config failed: %s", esp_err_to_name(err));
            return err;
        }
    }

    err = esp_wifi_start();
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "esp_wifi_start failed: %s", esp_err_to_name(err));
        return err;
    }

    s_current_mode = mode;

    // 여기서 스니퍼용 promiscuous 모드 활성화
    sniffer_enable_promiscuous();

    ESP_LOGI(TAG, "Wi-Fi mode set to %s", wifi_mgr_mode_to_string(mode));
    return ESP_OK;
}

static esp_err_t update_sta_ssid(const char *ssid)
{
    size_t len = strlen(ssid);
    if (len == 0 || len > sizeof(s_sta_config.sta.ssid)) {
        return ESP_ERR_INVALID_ARG;
    }
    memset(s_sta_config.sta.ssid, 0, sizeof(s_sta_config.sta.ssid));
    memcpy(s_sta_config.sta.ssid, ssid, len);
    return esp_wifi_set_config(WIFI_IF_STA, &s_sta_config);
}

static esp_err_t update_sta_password(const char *password)
{
    size_t len = strlen(password);
    if (len > sizeof(s_sta_config.sta.password) - 1) {
        return ESP_ERR_INVALID_ARG;
    }
    memset(s_sta_config.sta.password, 0, sizeof(s_sta_config.sta.password));
    memcpy(s_sta_config.sta.password, password, len);
    return esp_wifi_set_config(WIFI_IF_STA, &s_sta_config);
}

static esp_err_t update_ap_ssid(const char *ssid)
{
    size_t len = strlen(ssid);
    if (len == 0 || len > sizeof(s_ap_config.ap.ssid)) {
        return ESP_ERR_INVALID_ARG;
    }
    memset(s_ap_config.ap.ssid, 0, sizeof(s_ap_config.ap.ssid));
    memcpy(s_ap_config.ap.ssid, ssid, len);
    s_ap_config.ap.ssid_len = (uint8_t)len;
    ensure_ap_defaults();
    return esp_wifi_set_config(WIFI_IF_AP, &s_ap_config);
}

static esp_err_t update_ap_password(const char *password)
{
    size_t len = strlen(password);
    if (len == 0) {
        memset(s_ap_config.ap.password, 0, sizeof(s_ap_config.ap.password));
        s_ap_config.ap.authmode = WIFI_AUTH_OPEN;
    } else {
        if (len < 8 || len > sizeof(s_ap_config.ap.password) - 1) {
            return ESP_ERR_INVALID_ARG;
        }
        memset(s_ap_config.ap.password, 0, sizeof(s_ap_config.ap.password));
        memcpy(s_ap_config.ap.password, password, len);
        s_ap_config.ap.authmode = WIFI_AUTH_WPA2_PSK;
    }
    ensure_ap_defaults();
    wifi_config_t cfg = s_ap_config;
    cfg.ap.ssid_len = (uint8_t)strnlen((const char *)cfg.ap.ssid,
                                       sizeof(cfg.ap.ssid));
    return esp_wifi_set_config(WIFI_IF_AP, &cfg);
}