1. 程式人生 > >Android Wi-Fi原始碼分析之wpa_supplicant初始化(一)

Android Wi-Fi原始碼分析之wpa_supplicant初始化(一)

一. wpa_supplicant配置編譯

將對應的平臺的wpa_supplicant包解壓改名為wpa_supplicant_8替換掉external下的wpa_supplicant_8目錄
執行:
source build/envsetup.sh
lunch qiyang_6dq-user

根據平臺定製所需要的wpa_supplican版本,
修改: BoardConfig.mk指明wpa_supplicant_8;指明所使用的驅動;指明wlan晶片對應的靜態庫
執行:
mmm hardware/Broadcom/wlan/bcmdhd/ wpa_supplicant_8_lib/
mmm external/wpa_supplicant_8
執行完之後把編譯好的wpa_supplican放入目標板並設定許可權為可執行.
結語: 除了修改BoardConfig.mk外, wpa_supplicant也定義了自己的android.config. 嚴格來說, android.cfg應該是唯一的編譯控制檔案. 但由於wlan晶片不同, wpa_supplicant可能還依賴其它模組.
Anadroid系統中, wpa_supplicant啟動是通過 “setprop ctrl.start wpa_supplicant” 來觸發init程序去fork一個子程序來完成的. wpa_supplicant在init配置檔案中定義為一個service.
路徑:qiyang-android442\device\fsl\qiyang_6dq\init.rc

service wpa_supplicant /system/bin/wpa_supplicant \
    -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \


最重要的是通過”-c” 引數指定的wpa_supplicant啟動配置檔案, 該配置檔案路徑名為/data/misc/wifi/wpa_supplicant.conf .
wpa_supplicant原始碼中包含一個啟動配置檔案的模板, 該檔案路徑為:external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.conf

network=
{ ssid="example" proto=RSN key_mgmt=WPA-EAP pairwise=CCMP TKIP group=CCMP TKIP eap=TLS identity="[email protected]" ca_cert="/etc/cert/ca.pem" client_cert="/etc/cert/user.pem" private_key="/etc/cert/user.prv" private_key_passwd="password" priority=1 }

其中:
ctrl_interface=/var/run/wpa_supplicant 指明瞭控制介面unix域socket的檔名.

二. main函式分析

Android平臺中, main函式定義於main.c中.

int main(int argc, char *argv[])
{
    int c, i;
    struct wpa_interface *ifaces, *iface;
    int iface_count, exitcode = -1;
    struct wpa_params params;
    struct wpa_global *global;

    /* Android平臺中, 下面這個函式的實現在os_unix.c中. Android對其做了一些修改, 
     * 主要是許可權方面的設定防止某些情況下被破解者利用許可權漏洞以獲得root許可權. */
    if (os_program_init())
        return -1;

    os_memset(&params, 0, sizeof(params));
    params.wpa_debug_level = MSG_INFO;

    iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
    if (ifaces == NULL)
        return -1;
    iface_count = 1;

    wpa_supplicant_fd_workaround(1);/*輸入輸出重定向到/dev/null裝置*/

    for (;;) {/*引數解析*/
        c = getopt(argc, argv,
               "b:Bc:C:D:de:f:g:G:hi:I:KLNo:O:p:P:qsTtuvW");
        if (c < 0)
            break;
        switch (c) {
        case 'b':
            iface->bridge_ifname = optarg;
            break;
        case 'B':
            params.daemonize++;
            break;
        case 'c':
            iface->confname = optarg;/*指定配置檔名.該引數賦值給了wpa_interface中的變數*/
            break;
        case 'C':
            iface->ctrl_interface = optarg;
            break;
        case 'D':
            iface->driver = optarg;/*指定driver名稱.該引數賦值給了wpa_interface中的變數*/
            break;
        case 'd':
    #ifdef CONFIG_NO_STDOUT_DEBUG
            printf("Debugging disabled with "
                   "CONFIG_NO_STDOUT_DEBUG=y build time "
                   "option.\n");
            goto out;
    #else /* CONFIG_NO_STDOUT_DEBUG */
            params.wpa_debug_level--;
            break;
    #endif /* CONFIG_NO_STDOUT_DEBUG */
        case 'e':
            params.entropy_file = optarg;/*指定初始隨機數檔案,用於後續隨機數的生成*/
            break;
    #ifdef CONFIG_DEBUG_FILE
        case 'f':
            params.wpa_debug_file_path = optarg;
            break;
    #endif /* CONFIG_DEBUG_FILE */
        case 'g':
            params.ctrl_interface = optarg;
            break;
        case 'G':
            params.ctrl_interface_group = optarg;
            break;
        case 'h':
            usage();
            exitcode = 0;
            goto out;
        case 'i':
            iface->ifname = optarg;/*指定網路裝置介面名, 本例是"wlan0"*/
            break;
        case 'I':
            iface->confanother = optarg;
            break;
        case 'K':
            params.wpa_debug_show_keys++;
            break;
        case 'L':
            license();
            exitcode = 0;
            goto out;
        case 'o':
            params.override_driver = optarg;
            break;
        case 'O':
            params.override_ctrl_interface = optarg;
            break;
        case 'p':
            iface->driver_param = optarg;
            break;
        case 'P':
            os_free(params.pid_file);
            params.pid_file = os_rel2abs_path(optarg);
            break;
        case 'q':
            params.wpa_debug_level++;
            break;
    #ifdef CONFIG_DEBUG_SYSLOG
        case 's':
            params.wpa_debug_syslog++;
            break;
    #endif /* CONFIG_DEBUG_SYSLOG */
    #ifdef CONFIG_DEBUG_LINUX_TRACING
        case 'T':
            params.wpa_debug_tracing++;
            break;
    #endif /*   CONFIG_DEBUG_LINUX_TRACING */
        case 't':
            params.wpa_debug_timestamp++;
            break;
    #ifdef CONFIG_DBUS
        case 'u':
            params.dbus_ctrl_interface = 1;
            break;
    #endif /* CONFIG_DBUS */
        case 'v':
            printf("%s\n", wpa_supplicant_version);
            exitcode = 0;
            goto out;
        case 'W':
            params.wait_for_monitor++;
            break;
        case 'N':
            iface_count++;
            iface = os_realloc_array(ifaces, iface_count,
                         sizeof(struct wpa_interface));
            if (iface == NULL)
                goto out;
            ifaces = iface;
            iface = &ifaces[iface_count - 1]; 
            os_memset(iface, 0, sizeof(*iface));
            break;
        default:
            usage();
            exitcode = 0;
            goto out;
        }
    }

    exitcode = 0;
    /*關鍵函式1:根據傳入的引數,建立並初始化一個wpa_global物件*/
    global = wpa_supplicant_init(&params);
    if (global == NULL) {
        wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
        exitcode = -1;
        goto out;
    } else {
        wpa_printf(MSG_INFO, "Successfully initialized "
               "wpa_supplicant");
    }

    for (i = 0; exitcode == 0 && i < iface_count; i++) {
        struct wpa_supplicant *wpa_s;

        if ((ifaces[i].confname == NULL &&
             ifaces[i].ctrl_interface == NULL) ||
            ifaces[i].ifname == NULL) {
            if (iface_count == 1 && (params.ctrl_interface ||
                         params.dbus_ctrl_interface))
                break;
            usage();
            exitcode = -1;
            break;
        }
        /* 關鍵函式2:wpa_supplicant支援操作多個無線網路裝置,此處將它們一一新增到
         * wpa_supplicant中, wpa_supplicant內部將初始化這些裝置*/
        wpa_s = wpa_supplicant_add_iface(global, &ifaces[i]);
        if (wpa_s == NULL) {
            exitcode = -1;
            break;
        }
    #ifdef CONFIG_P2P
        if (wpa_s->global->p2p == NULL &&
            (wpa_s->drv_flags &
             WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
            wpas_p2p_add_p2pdev_interface(wpa_s) < 0)
            exitcode = -1;
    #endif /* CONFIG_P2P */
    }

    /*Android平臺中, wpa_supplicant通過select或epoll方式實現多路I/O複用*/
    if (exitcode == 0)
        exitcode = wpa_supplicant_run(global);

    wpa_supplicant_deinit(global);

out:
    wpa_supplicant_fd_workaround(0);
    os_free(ifaces);
    os_free(params.pid_file);

os_program_deinit();

return exitcode;
}   

三. wpa_supplicant_init函式分析

路徑:external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.c

struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
{
    struct wpa_global *global;
    int ret, i;

    if (params == NULL)
        return NULL;

#ifdef CONFIG_DRIVER_NDIS
    {
        void driver_ndis_init_ops(void);
        driver_ndis_init_ops();
    }
#endif /* CONFIG_DRIVER_NDIS */

#ifndef CONFIG_NO_WPA_MSG
    wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);/*設定全域性回撥函式*/
#endif /* CONFIG_NO_WPA_MSG */

    wpa_debug_open_file(params->wpa_debug_file_path);/*輸出日誌檔案設定*/
    if (params->wpa_debug_syslog)
        wpa_debug_open_syslog();
    if (params->wpa_debug_tracing) {
        ret = wpa_debug_open_linux_tracing();
        if (ret) {
            wpa_printf(MSG_ERROR,
                   "Failed to enable trace logging");
            return NULL;
        }
    }

    ret = eap_register_methods();/*1. 註冊EAP方法*/
    if (ret) {
        wpa_printf(MSG_ERROR, "Failed to register EAP methods");
        if (ret == -2)
            wpa_printf(MSG_ERROR, "Two or more EAP methods used "
                   "the same EAP type.");
        return NULL;
    }

    global = os_zalloc(sizeof(*global));/*建立一個wpa_global物件*/
    if (global == NULL)
        return NULL;

    /*初始化global中的其它引數*/
    dl_list_init(&global->p2p_srv_bonjour);
    dl_list_init(&global->p2p_srv_upnp);
    global->params.daemonize = params->daemonize;
    global->params.wait_for_monitor = params->wait_for_monitor;
    global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
    if (params->pid_file)
        global->params.pid_file = os_strdup(params->pid_file);
    if (params->ctrl_interface)
        global->params.ctrl_interface =
            os_strdup(params->ctrl_interface);
    if (params->ctrl_interface_group)
        global->params.ctrl_interface_group =
            os_strdup(params->ctrl_interface_group);
    if (params->override_driver)
        global->params.override_driver =
            os_strdup(params->override_driver);
    if (params->override_ctrl_interface)
        global->params.override_ctrl_interface =
            os_strdup(params->override_ctrl_interface);
    wpa_debug_level = global->params.wpa_debug_level =
        params->wpa_debug_level;
    wpa_debug_show_keys = global->params.wpa_debug_show_keys =
        params->wpa_debug_show_keys;
    wpa_debug_timestamp = global->params.wpa_debug_timestamp =
        params->wpa_debug_timestamp;

    wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);
    /*2. 初始化事件迴圈機制*/
    if (eloop_init()) {
        wpa_printf(MSG_ERROR, "Failed to initialize event loop");
        wpa_supplicant_deinit(global);
        return NULL;
    }

    /*初始化隨機數相關資源, 用於提升後續隨機數生成的隨機性*/
    random_init(params->entropy_file);

    /*初始化全域性控制介面物件. */
    global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
    if (global->ctrl_iface == NULL) {
        wpa_supplicant_deinit(global);
        return NULL;
    }

    /*初始化通知機制相關資源, 它和dbus有關.*/
    if (wpas_notify_supplicant_initialized(global)) {
        wpa_supplicant_deinit(global);
        return NULL;
    }

    /*wpa_drivers是全域性變數*/
    for (i = 0; wpa_drivers[i]; i++)
        global->drv_count++;
    if (global->drv_count == 0) {
        wpa_printf(MSG_ERROR, "No drivers enabled");
        wpa_supplicant_deinit(global);
        return NULL;
    }

    /*分配全域性driver wrapper上下文資訊陣列*/
    global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
    if (global->drv_priv == NULL) {
        wpa_supplicant_deinit(global);
        return NULL;
    }

#ifdef CONFIG_WIFI_DISPLAY
    if (wifi_display_init(global) < 0) {
        wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
        wpa_supplicant_deinit(global);
        return NULL;
    }
#endif /* CONFIG_WIFI_DISPLAY */

    return global;
}


wpa_supplicant_init函式的主要功能是初始化wpa_global以及一些與整個程式相關的資源, 包括隨機數資源, eloop事件迴圈機制以及設定訊息全域性回撥函式.這裡的回撥函式有兩個.:
wpa_msg_get_ifname_func: 某些輸出資訊中需要打印出網絡卡介面名. 該回調函式用於獲取網絡卡介面名.
wpa_msg_cb_func:特殊處理, 把輸出資訊發給客戶端進行處理.

1. wpa_debug.c分析

void wpa_msg_register_cb(wpa_msg_cb_func func)
{
    wpa_msg_cb = func;/*wpa_msg_cb的實現函式是wpa_supplicant_ctrl_ifname_msg_cb, 他將輸出資訊傳送給客戶端*/
}


static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;/*wpa_msg_ifname_cb用於獲取無線網絡卡介面;wpa_supplicant的實現函式為wpa_supplicant_msg_ifname_cb*/

void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
{
    wpa_msg_ifname_cb = func;
}


後面再來看wpa_supplicant_init中列出的三個關鍵點.

相關推薦

Android Wi-Fi原始碼分析wpa_supplicant初始()

一. wpa_supplicant配置編譯 將對應的平臺的wpa_supplicant包解壓改名為wpa_supplicant_8替換掉external下的wpa_supplicant_8目錄 執行: source build/envsetup.sh

Android Wi-Fi原始碼分析wpa_supplicant初始(四):wpa_supplicant_init_iface函式分析

wpa_config_read分析 路徑為:external\wpa_supplicant_8\wpa_supplicant\config_file.c struct wpa_config * wpa_config_read(const char *na

Android Wi-Fi原始碼分析wpa_supplicant初始(三):wpa_supplicant_add_iface函式分析

路徑為:external\wpa_supplicant_8\wpa_supplicant\wpa_supplicant.c /** * wpa_supplicant_add_iface - Add a new network interface * @

Android Wi-Fi原始碼分析WifiService操作Wi-Fi():分析Wifi.c中的wifi_load_driver()函式

Wi-Fi原始碼分析之WifiService操作Wi-Fi(一) 分析Wifi.c中的wifi_load_driver()函式 int wifi_load_driver() { AL

Android Wi-Fi原始碼分析WifiService操作Wi-Fi(二):WifiStateMachine.java中的SUP_CONNECTION_EVENT分析

Wi-Fi原始碼分析之WifiService操作Wi-Fi(二) 一. SupplicantStartingState中的的processMessage方法分析 public boolean processMessage(Messag

Spring Security4.0.3原始碼分析FilterChainProxy初始

最近在學習安全框架Spring Security,想弄清楚其中實現的具體步驟,於是下定決心,研究一下Spring Security原始碼,這篇部落格的目的是想把學習過程記錄下來。學習過程中主要參考了http://dead-knight.iteye.com/blo

Spring MVC原始碼分析DispatcherServlet初始過程

DispatcherServelt本質是也是Servlet,由Servlet容器進行載入。 1.Servlet介面提供了Servlet的初始化方法:init(ServletConfig config)。 2.GenericServlet實現了方法init(S

Android4.4 wpa_supplicant深入分析wpa_supplicant初始流程

Android系統中,wpa_supplicant啟動是通過“setprop ctrl.start wpa_supplicant”來觸發init程序去fork一個子程序來完成的。wpa_supplicant在init配置檔案中被定義為一個service。

Spring原始碼分析容器初始

AnnotationConfigApplicationContext 核心類,BeanDefinition註冊器,所有的BeanD

OpenStackNeutron原始碼分析 Neutron-server初始

從資料夾的命名也基本可以得出該目錄程式碼的作用,幾個重要的資料夾如下: agent: 主要是l3 agent及l3 agent ha的相關程式碼; common: 主要是各底層驅動與linux系統命令的互動層; db: 是neutron各功能與資料庫互動資

玩轉「Wi-Fi」系列wpa_supplicant 介紹(七)

簡介 wpa_supplicant是Linux BSD, Mac OSX和Windows的WPA的服務,支援WPA和WPA2(IEEE 802.11i/RSN),它適用於桌上型電腦/筆記本和嵌入式系統,Supplicant是在客戶端站中使用的IEEE 802.1

玩轉「Wi-Fi」系列wpa_supplicant

命令啟動 wpa_supplicnt 一般通過如下引數進行啟動: wpa_supplicant -Dnl80211 -iwlan0 -c/etc/wpa_supplicant.conf 其中比較主要的是-c引數, 指定啟動配置檔案。配置檔案的模板

第3階段——內核啟動分析start_kernel初始函數(5)

本質 aware 字符 信息 異常 性能 ack ifdef 取數 內核啟動分析之start_kernel初始化函數(init/main.c) stext函數啟動內核後,就開始進入start_kernel初始化各個函數, 下面只是淺嘗輒止的描述一下函數的功能,很多函數真正理

05.Fabric原始碼分析–kvledger的初始

Fabric原始碼分析5–kvledger的初始化 前兩篇文章藉由/fabric/peer/main.go這個線頭,簡單分析了fabric的配置和日誌系統。雖然還有一部分可說的內容,如common.InitCrypto()呼叫,但現在暫且按下main.go不管,而把目光投到/fabric

Netty原始碼分析:1.3初始NioServerSocketChannel

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

Netty原始碼分析:1.2初始NioEventLoop

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

Netty原始碼分析:1.1初始NioEventLoopGroup

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

Mybatis原始碼解析Mybatis初始過程

一、搭建一個簡單的Mybatis工程 為了瞭解Mybatis的初始化過程,這裡需要搭建一個簡單的Mybatis工程操作資料庫,工程結構如下: 一個UserBean.java private int id; private String user

Minix3原始碼分析(2)——系統初始

minix3的啟動牽扯到幾次控制權轉移,它們發生在mpx386.s中的組合語言例程和start.c及main.c中的C語言例程之間。 彙編程式碼需要做許多工作,包括建立一個 棧幀 以便為C編譯器編譯的程式碼提供適當的環境,複製處理器所使用的表格來定義儲存器段,建

Dubbo原始碼解析consumer初始

閱讀須知 dubbo版本:2.6.0 spring版本:4.3.8 文章中使用/* */註釋的方法會做深入分析 正文 dubbo的consumer由ReferenceBean初始化,我們先來看一下這個類的層次結構: 我們看到ReferenceBean實現了