• wpa_supplicant 初始化


    几个重要的结构体介绍:

    1. struct wpa_interface --- Parameters for wpa_supplicant_add_iface().

    wpa_interface对应网络接口。因为wpa_supplicant支持多个网络接口,所以可能有多个wpa_interface结构体,可以通过命令行指定不同的接口。wpa_supplicant在main函数开始的地方会进行遍历!(参考代码main.c)

    2. struct wpa_global --- Internal, global data for all %wpa_supplicant interfaces.

    This structure is initialized by calling wpa_supplicant_init() when starting  starting %wpa_supplicant.

    3. struct wpa_params --- Parameters for wpa_supplicant_init().

    wpa_params主要记录一些与网卡本身没关的参数设置;

     

    struct wpa_global {
        struct wpa_supplicant *ifaces;
        struct wpa_params params;
        struct ctrl_iface_global_priv *ctrl_iface;
        struct ctrl_iface_dbus_priv *dbus_ctrl_iface;
    };

    1. struct wpa_supplicant --- Internal data for wpa_supplicant interface.

    每个网络接口都有一个对应的wpa_supplicant数据结构,该指针指向最近加入的一个,在wpa_supplicant数据结构中有指针指向next。

    2. struct ctrl_iface_global_priv --- Global control interface

    3. struct ctrl_iface_dbus_priv --- DBUS control interface

     

    init.rc中有语句: 

    import /init.${ro.hardware}.rc

    其中ro.hardware为qcom,所以导入的文件名为init.qcom.rc。

    查看./device/qcom/common/rootdir/etc/init.qcom.rc文件,有以下代码:

    service wpa_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant 
        -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf -dddd 
        -e/data/misc/wifi/entropy.bin
        #   we will start as root and wpa_supplicant will switch to user wifi
        #   after setting up the capabilities required for WEXT
        #   user wifi
        #   group wifi inet keystore
        class main
        socket wpa_wlan0 dgram 660 wifi wifi
        disabled
        oneshot

     

    当start supplicant后,就会调用main.c::main方法,对这些代码进行解析。

    ps:通过property_set("ctl.start",”wpa_supplicant”)语句即可

    ---------------------------------------------------------------------------------------

    初始化

    初始化工作主要是由main()函数来执行。

    在这个函数中,主要做了四件事:

    a. 解析命令行传进的参数。

    b. 调用wpa_supplicant_init()函数,做wpa_supplicant的初始化工作。

    c. 调用wpa_supplicant_add_iface()函数,增加网络接口。

    d. 调用wpa_supplicant_run()函数,让wpa_supplicant真正的run起来。

     

    ps:最开始调用的os_program_init函数,主要就是来给wpa_supplicant进程分配权限。

    定义在[-->./src/utils/os.h],实现在[--->./src/utils/os_unix.c]

     

    解析命令行传进的参数

    调用getopt函数循环解析参数:

    c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW");

    函数原型: int getopt(int argc, char * const argv[], const char *optstring);

    该函数调用一次,返回一个选项。在命令行选项参数再也检查不到optstring中包含的选项时,返回-1。

    具体参考文章:http://www.cnitblog.com/zouzheng/archive/2007/04/02/25034.aspx

    wpa_supplicant_init  分析

    1.open debug files. [--->./src/utils/wpa_debug.c ]

    wpa_debug_open_file   /  wpa_debug_open_syslog

    2.register eap peer methods. [--->./wpa_supplicant/eap_register.c]
    eap_peer_md5_register
    eap_peer_tls_register
    eap_peer_mschapv2_register
    eap_peer_peap_register
    eap_peer_ttls_register
    eap_peer_gtc_register
    eap_peer_otp_register
    eap_peer_sim_register
    eap_peer_leap_register
    eap_peer_psk_register
    eap_peer_aka_register
    eap_peer_aka_prime_register
    eap_peer_fast_register
    ...

    3. 为wpa_global对象申请内存.[--->./wpa_supplicant/wpa_supplicant_i.h]

     

    (1) 分析wpa_global结构体

    struct wpa_global {
    /*
    *每个网络接口都有一个对应的wpa_supplicant数据结构,该指针指向最近加入的一个。*在wpa_supplicant数据结构中有指针指向next。
    */
      struct wpa_supplicant *ifaces; 
      
      /*启动命令行中带的通用的参数*/
      struct wpa_params params;
      /*global 的控制接口*/
      struct ctrl_iface_global_priv *ctrl_iface;
      /*dbus 的控制接口*/
        struct wpas_dbus_priv *dbus;   
      ......
    }

    4. 设置wpa_global中的wpa_params中的参数

    通过wpa_supplicant_init方法的参数params为wpa_global中的wpa_params中的参数赋值。

     

    5. 调用eloop_init函数。这个函数将全局变量eloop中的user_data指针指向wpa_global。

    ps:所说的user_data指针可能是其他结构体中的变量。

     

    eloop 是一个结构体变量,看下其结构体:

    struct eloop_data {
        int max_sock;
    
        int count; /* sum of all table counts */
    #ifdef CONFIG_ELOOP_POLL
        int max_pollfd_map; /* number of pollfds_map currently allocated */
        int max_poll_fds; /* number of pollfds currently allocated */                                                                                                        
        struct pollfd *pollfds;
        struct pollfd **pollfds_map;
    #endif /* CONFIG_ELOOP_POLL */
        struct eloop_sock_table readers;
        struct eloop_sock_table writers;
        struct eloop_sock_table exceptions;                                                                                                                                  
    
        struct dl_list timeout;
    
        int signal_count;
        struct eloop_signal *signals;  
        int signaled;
        int pending_terminate;
    
        int terminate;
        int reader_table_changed; 
    };
    
    static struct eloop_data eloop;

    6. 调用wpa_supplicant_global_ctrl_iface_init函数:初始化global的控制接口。

    [--->./wpa_supplicant/ctrl_iface_unix.c]

    global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);

     

    对于第一个接口的初始化,实际上通过socket进行了内部进程间通信,如下:

    priv->sock = android_get_control_socket(global->params.ctrl_interface);
    /* 此处通过getenv获得了sockfd(android平台),相当于如果本身有了fd的话,将直接跳转到havesock,如果没有的话,将创建连接,如下所示 */
    priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); /* PF_UNIX 代表内部进程间通信 */

    下面bind或者connect,错误基本上也是gotofail。到这里控制接口初始化结束。

    7. 调用wpas_notify_supplicant_initialized函数:初始化dbus的控制接口。

    [--->./wpa_supplicant/notify.c]
    int wpas_notify_supplicant_initialized(struct wpa_global *global)
    {
    #ifdef CONFIG_DBUS
        if (global->params.dbus_ctrl_interface) {
            global->dbus = wpas_dbus_init(global);
            if (global->dbus == NULL)
                return -1;
        }  
    #endif /* CONFIG_DBUS */
    
        return 0;
    }

    8.最终返回一个wpa_global对象。

    over~~

     

    网上有说:下面还有一步是将该daemon的pid写入pid_file中。但是在android4.1上,这一步骤并不是在wpa_supplicant_init函数中实现,而是在wpa_supplicant_daemon函数中。具体执行流程是:

    main.c::main ---> wpa_supplicant_run ---> wpa_supplicant_daemon 实现了该功能。

     

    wpa_supplicant_add_iface():增加网络接口

    该函数根据启动命令行中带有的参数增加网络接口, 有几个就增加几个。

    1. struct wpa_supplicant wpa_s = wpa_supplicant_alloc();

    wpa_supplicant是与网络接口对应的重要的数据结构,这里通过wpa_supplicant_alloc函数分配一个wpa_supplicant数据结构的内存,并对其中一些变量进行init:

    static struct wpa_supplicant * wpa_supplicant_alloc(void)
    {
        struct wpa_supplicant *wpa_s;  
    
        wpa_s = os_zalloc(sizeof(*wpa_s));
        if (wpa_s == NULL)
            return NULL;
      /* scan_req就是wpa定时去读driver scan到的结果。
       * scan_req=1,表示手动scan,即使conf文件中没有配置任何网络。
       * 除了0,还可以设置为2,好象设置2时不做关联请求(associate req)
       */
      wpa_s->scan_req = 1;
      /* time in sec between scans to find suitable AP 
       * 就是设置driver调度scan的间隔时间,若要省电时,可将改时间设置的长一些
       */
        wpa_s->scan_interval = 5;
        wpa_s->new_connection = 1;
        wpa_s->parent = wpa_s;
        wpa_s->sched_scanning = 0;
    
        return wpa_s;
    }

    2. 调用wpa_supplicant_init_iface()函数:来做网络接口的初始工作。包括以下内容:

    a. 调用wpa_config_read函数:

    (1)读取配置文件:/data/misc/wifi/wpa_supplicant.conf  

    (解析命令行时的-c 选项,会将该配置文件添加进来)

    (2)并将其中的信息解析到struct wpa_supplicant数据结构的wpa_s->conf中,这个conf是一个wpa_config类型的数据结构;

     

    b. eapol_sm_notify_portEnabled / eapol_sm_notify_portValid 

    init eapol state machine,但是目前sm==NULL,所以什么也没做

    ps:EAPOL state machine负责处理PTK 4-way HS和GTK 2-way HS  (这个不懂,这里不继续深入)

     

    c. 调用wpa_supplicant_set_driver函数:完成驱动指定,设置驱动参数。

    该函数会调用select_driver函数,其中分析下面这句代码:

    /* 指向某个struct wpa_driver_ops */
    wpa_s->driver = wpa_drivers[i];

    wpa_drivers定义在./src/drivers/drivers.c中,

    wpa_driver_ops定义在./src/drivers/driver.h

    ops这个结构体对象注册了一系列wext(我们采用wext的驱动类型)相关的函数指针,从而提供了相应的驱动接口!

     

    d. wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); 

    调用相应驱动的init函数。

     

    e. 调用wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param)函数:

    设置driver的param参数

     

    f. 调用wpa_drv_get_ifname()函数:获得网络接口的名称。对于wext类型的driver,没有这个接口函数;

     

    g. 调用wpa_supplicant_init_wpa()函数:初始化wpa,并做相应的初始化工作

     

    h. 调用wpa_supplicant_driver_init()函数:初始化driver接口参数。

    在该函数的最后,会执行以下代码:

    wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
    ...
    wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
    ...

    这样可以来主动发起scan。

     

    i. 调用wpa_supplicant_ctrl_iface_init()函数:初始化控制接口。

    对于UNIX SOCKET这种方式,其本地socket文件是由配置文件里的ctrl_interface参数指定的路径加上网络接口名称。

     

    wpa_supplicant_run()函数:真正启动 wpa_supplicant

    1. 调用wpa_supplicant_daemon函数:将该daemon的pid写入pid_file中。

     

    2. 调用eloop_run函数

    a.将许多socket注册到eloop event模块:

    在wpa_supplicant中,有许多与外界通信的socket,它们都是需要注册到eloop event模块中的,具体地说,就是在eloop_sock_table中增加一项记录,其中包括了sock_fd, handle, eloop_data, user_data。

     

    b.利用select机制管理socket通信:

    eloop event模块就是将这些socket组织起来,统一管理,然后在eloop_run中利用select机制来管理socket的通信。

     

    终于结束了-.-!!! 到这里,wpa_supplicant的初始化就介绍完了...真正启动起来了...

     

     

  • 相关阅读:
    VS.NET2005中的源代码管理
    IE6升级后需要激活ActiveX控件的解决办法
    SQL Server的数据库开发工具
    今天更新了ActiveSync4.2
    永远等你先挂电话
    这回软设考试通过了!
    在Windows2003中FSO组件不能使用的问题
    七天的假期好长哟!
    发现博客园的一个Bug 存为草稿后就找不到了
    MySQL服务不能启动的解决方法
  • 原文地址:https://www.cnblogs.com/chenbin7/p/3221975.html
Copyright © 2020-2023  润新知