现在的位置: 首页 > 综合 > 正文

跟一下wpa_supplicant(1) 初始化

2013年03月15日 ⁄ 综合 ⁄ 共 16269字 ⁄ 字号 评论关闭
环境:

  android 2.3.4 

  wpa_supplicant 0.8
 
一切从 main.c 开始: (=>表示第1级, 那么==>表示下一级,类推)
os_program_init => 参数解析& 获取

=> wpa_supplicant_init(&params)

   ==> eap_register_methods /* 注册EAP method ,比如EAP-PSK, EAP-TLS */

   ==> global->params获取, 比如daemonize-是否在后台运行wpa,

                               ctrl_interface-wpa_cli 命名socket

   ==> eloop_init , static struct eloop_data eloop; 结构清0

   ==> wpa_supplicant_global_ctrl_iface_init

           (init.rc wpa socket file regist to eloop table)

           在init.rc 中wpa service 启动命令中声明的socket如下:

    service wpa_supplicant /system/bin/wpa_supplicant

             socket wpa_wlan0:0

           通过 android_get_control_socket 函数(android 特有的)和socket名

    获得socket 句柄,放入struct wpa_global 的 ctrl_iface 变量中

           

   ==> wpas_notify_supplicant_initialized 

          其中进行 dbus init  但CONFIG_CTRL_IFACE_DBUS 没有设置! 什么也不做

   ==> wpa_drivers[i]->global_init(); 

          目前wpa_drivers 包含wext,和nl80211 其中nl80211 

   是nl80211_global_init 分配生成

   struct nl80211_global { struct dl_list interfaces; };

   接口双向list init ,然后给出pointer

   ==> 最后返回前面已经init 的struct wpa_global *global
=> wpa_supplicant_add_iface (如其名,添加接口,就是wlan0)

   ==> struct wpa_supplicant  wpa_s = wpa_supplicant_alloc();

       给struct wpa_supplicant *wpa_s 分配内存,并对下面这些变量init

        /* scan_req =1,表示手动scan ,即使没有网络在conf文件中配置

     除了0, 还可以设置为2,好象设置2时不做关联请求(associate req)*/

        wpa_s->scan_req = 1;

        /* time in sec between scans to find suitable AP 

   可见这个参数决定了ap 是否在线的判断,但是

   要省电时可以设置了长点*/

 wpa_s->scan_interval = 5;

 /* driver 参数,可见 kerneldoc 解释如下:
http://linuxwireless.org/en/developers/Documentation/nl80211/kerneldoc

 NL80211_ATTR_SCHED_SCAN_INTERVAL 

          Interval between scheduled scan cycles, in msecs. 

        这个参数看来必须小于上面的 scan_interval*/

 wpa_s->sched_scan_interval = 3;

 wpa_s->new_connection = 1;

 wpa_s->parent = wpa_s;

 wpa_s->sched_scanning = 0;

 wpa_s->override_sched_scan = 0;
 scan_interval 就是设置driver 调度scan 的间隔时间

 scan_req 就是wpa 定时去读driver scan 到的结果

 但是eloop 中scan 多播的事件,这个又如何解释???
   ==> wpa_supplicant_init_iface

       ===> struct wpa_config * wpa_config_read(wpa_s->confname);  

            读/data/misc/wifi/wpa_supplicant.conf 并解析到wpa_config 结构

            config.h 有default 设置,某些可以通过wpa_cli 设置

         全部读到 struct wpa_supplicant结构的wpa_s->conf

  (命令行-c /data/misc/wifi/wpa_supplicant.conf)

       ===> eapol_sm_notify_portEnabled, eapol_sm_notify_portValid 

            init eapol state machine ,但是目前sm==NULL,什么也没做
       ===> wpa_supplicant_set_driver (确定 wpa_s->driver = wpa_drivers[i];)

            根据driver name = 'nl80211'  查到 wpa_drivers中的某1个做下面动作:

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

     

     /*drv_priv 是前面wpa_drivers[i]->global_init()时获得的 

     struct nl80211_global {struct dl_list interfaces;};

     以后driver init 时将init 好的driver data挂到该双向list 上

            可参考 wpa_drv_init wpa_driver_nl80211_init,

            其实不用以后,下面马上开始wpa_drv_init

            */ 

     wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];
       ===> wpa_drv_init ====> init2 (wpa_driver_nl80211_init)

            分配内存给struct wpa_driver_nl80211_data * drv

     然后对各结构成员init :

            drv->global = global_priv; 还记得前面那个dl list 挂接口用的

     有了这个参数传入,driver 就可以访问该list ,做接口init 后挂表动作了

            drv->ctx = ctx; /* ctx 就是struct wpa_supplicant *wpa_s

                     这样driver opt function 中也能访问到 此结构*/

           

            ====> wpa_driver_nl80211_init_nl

               这部分主要是生成两个结构指针:

     struct nl_handle * nl_hanlde  nl_handle和nl_handle_event

     nl_handle用于向nl80211 netlink 写, 

     nl_handle_event表示接收,scan,mlme,regulatory等多播包,并把

   nl_handle_event  注册到eloop reads table

            ====> /sys/class/net/wlan0/phy80211/name   去读drv->phyname

            ====> drv->ioctl_sock  创建用于 访问网络接口kernel ioctl socket

                  看下下面这段注释:(比如up,down 接口使用该socket 去做)

/*The usual method in Unix to set and get parameters from a network device is through ioctl. 

  Ioctl are usually operations performed on a file descriptor, but they also apply on network sockets. 

  The ioctl is a kernel system call. The arguments of the ioctl define the operations to be done, 

  the parameters of these operations and the device they applies to.

*/

            ====> drv->netlink = netlink_init(cfg) 

          进行driver data部分的 netlink config 设置

                 netlink_init 注册NETLINK_ROUTE socket 到

       eloop reads table,其中handle cb func 为 netlink_receive

                 netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);  

   创建用于NETLINK_ROUTE socket

                 netlink目前使用最广泛的是通过NETLINK_ROUTE 这个选项来获取

       网络设备或者网址的一些信息,比如获得接口状态: IFF_RUNNING
  接收newlink, dellink 两个event ,然后使用cfg  

  设置的 callback 函数:

  * wpa_driver_nl80211_event_rtm_newlink :

                如果是if disable 到 enable 就执行wpa_supplicant_event

        (drv->ctx, EVENT_INTERFACE_ENABLED, NULL);

  该事件发生,引发 wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);

  还有就是请求 scan (wpa_supplicant_req_scan), 要等待iface up时

  

  * wpa_driver_nl80211_event_rtm_dellink, 

       处理iface down 的event,并更新wpa_s 的状态

            ====> rfkill_init /dev/rfkill 不存在,什么也没做

            ====> wpa_driver_nl80211_finish_drv_init

        wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION)  

               通过 NL80211_CMD_SET_INTERFACE 设置compat driver 接口为station,

       

               如果wlan0 没有up 就linux_set_iface_flags 让它up)

               netlink_send_oper_ifla 设置接口连接mode=1, operstate =5 

        (operstate 参考 RFC 2863 operational status)

        可以通过/sys/class/net/wlan0(也与可能是其他名字)/operstate 查看

        operstate的可能取值如下:

                   IF_OPER_UNKNOWN, 

                   IF_OPER_NOTPRESENT, 

                   IF_OPER_DOWN, 

                   IF_OPER_LOWERLAYERDOWN, 

                   IF_OPER_TESTING, 

                   IF_OPER_DORMANT, =5 表示休眠的操作状态

                   IF_OPER_UP, 

               对应到RFC2863兼容状态的策略link_mode

           IF_LINK_MODE_DEFAULT, 

                  IF_LINK_MODE_DORMANT,   =1 对应上面的5 -> IF_OPER_DORMANT
               wpa_driver_nl80211_capa :

                   设置 key manager 能力,比如有WAP2,WPA_PSK,WAP2_PSK

                   设置 加密方法 : WEP40, TKIP,CCMP

                   设置 设置802.11 认证方式,比如open,(注意不是802.1X认证)

                   接下来通过 wpa_driver_nl80211_get_info 获取driver的

                   max_scan_ssids, max_sched_scan_ssids

                   sched_scan_supported等等 ,

                   还有其他WPA_DRIVER_FLAGS_SME, WPA_DRIVER_FLAGS_P2P_CAPABLE

              通过SIOCGIFHWADDR 获得 mac addr (记住在这里获得mac地址)

                   所以你可以不改driver ,在这里修改mac addr

              nl80211_register_action_frame :

              NL80211_CMD_REGISTER_ACTION 

              NL80211_CMD_REGISTER_FRAME  看下面注释就知道该函数做了什么

                Register for receiving certain mgmt frames (via NL80211_CMD_FRAME) for processing in userspace. 

                This command requires an interface index, a frame type attribute (optional for backward compatibility reasons, 

                if not given assumes action frames) and a match attribute containing 

                the first few bytes of the frame that should match, e.g. 

                a single byte for only a category match or four bytes for vendor frames including the OUI. 

                The registration cannot be dropped, but is removed automatically when the netlink socket is closed. Multiple registrations can be made.
                注册user层接收的一些管理帧,注册后由user 层处理

                  主要有P2P 的 Generic Advertisement Services frame , P2P Action

                  802.11W 部分相关的 SA Query Response

                  FT Action frames ,Fast BSS Transition 相关(802.11r),用于VOIP的多点

                  AP ,快速漫游,小于50ms VOIP设备不会掉线
            ====> driver init 完成了,通过 struct wpa_driver_nl80211_data 中的

                  成员list,将driver data 挂到前面所讲的global->drv_priv

    在wpa_supplicant_set_driver 函数中

           

    最后返回1个struct i802_bss *bss 主要包含struct wpa_driver_nl80211_data
       ===> wpa_drv_set_param -> nl80211_set_param 

            可以通过命令行-p ,或者conf文件来设置

Driver interface parameters:

  This field can be used to configure arbitrary driver interace parameters. The

  format is specific to the selected driver interface. This field is not used

  in most cases.  driver_param="field=value"
             如果p2p 支持 就处理 use_p2p_group_interface=1,

      其他情况什么也不做,代码注释如下:

       /*When this is added(use_p2p_group_interface=1), start the supplicant normally on wlan0 like above. 

          Then, when P2P negotiation finishes, it will create a new interface for the group (called "p2p-wlan0-0") 

         and put it into the appropriate mode (GO or P2P client). */
       ===> wpa_supplicant_init_wpa 

            设置struct wpa_sm_ctx  相关function  (处理wpa状态机变化的func !!!)

            ====>wpa_sm_init(ctx);  wpa state machine 初始化 

          =====> pmksa_cache_init ( 专门有show PMKSA cache的命令 ???)

下面聊下PMKSA

The Pairwise Master Key Security Association (PMKSA)

成对主蜜钥安全关联

  在802.11i 中成对主蜜钥是工作站和AP 成功认证产生的结果.

PMK的生命周期和唯一标识被称为PMKID。 这些信息的集合被称为成对主蜜钥安全关联

工作站判断是否是1个有效的目标AP的PMK,通过检查是否PMKSA 和目标AP 的mac 地址匹配,如果这样的PMK不存在,就和AP 使用EAP 进行认证

如果存在这样1个目标AP的PMK,那么工作站就试图使用PMK,通过将PMKID放入到关联请求消息的RSN IE中,当AP接收到关联请求中包含PMKID,AP就检查是否是1个有效的PMKSA,其中有

相同的PMKID,如果有,开始的4次握手就使用已经协商的PMKSA.

  

      根据上面解释下面这3个pmksa_cache_init参数,应就可以理解了

      sm->dot11RSNAConfigPMKLifetime = 43200; /*生命周期*/

      /* 超过生命周期的%70,需要进行re auth */

   sm->dot11RSNAConfigPMKReauthThreshold = 70;

      /*Security association timeout

            就是进行上面说的安全关联的建立应该在60s内完成*/

   sm->dot11RSNAConfigSATimeout = 60;

           

       ===> wpa_sm_set_ifname, wpa_sm_set_fast_reauth

       ===>  wpa_sm_set_param 

         1.RSNA_PMK_LIFETIME 2. RSNA_PMK_REAUTH_THRESHOLD 3. RSNA_SA_TIMEOUT

               这3个参数前面已经解释过了,同样设置和上面wpa_drv_set_param

        不同处是现在只是将这些参数保存到wpa_sm 结构中
    ===> wpa_drv_get_capa 取 driver 的 capability 然后设置到

               struct wpa_supplicant 的 max_scan_ssids 等member

        ===> wpa_supplicant_driver_init

             ====> l2_packet_init (ifname,macaddr,ETH_P_PAE,

                                    wpa_supplicant_rx_eapol(rx cb)

                    直接看代码:

/* create l2 socket fd!!!

 l2 就是链路层,所以上面init 传了mac 地址

*/

l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,htons(protocol));

ll.sll_family = PF_PACKET;   

ll.sll_ifindex = ifr.ifr_ifindex;  

ll.sll_protocol = htons(protocol);

/* l2 socket addr !!! */

if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {    

     /* 注册eloop 读入 l2 packet 事件,callback l2_packet_receive!!! */

    eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
       

             ====> wpa_clear_keys 

           =====> wpa_drv_set_key

          ======> wpa_driver_nl80211_set_key

           清除key key-index 0-3 ??? why 4key

             ====> wpa_drv_set_countermeasures /*  TKIP countermeasures*/

             ====> wpa_drv_flush_pmkid -> nl80211 driver 没有该接口???

             ====> wpa_supplicant_delayed_sched_scan 对conf 文件中的network设置

             进行扫描调度,如果不支持(sched_scan_supported==0)

                    100ms 后wpa_supplicant_req_scan,如果conf没有 network ,

                    那么需要设置 inactive state如下:

             ====> /* Inactive state (wpa_supplicant disabled)*/

                    wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); 

                   /* 上面函数中 后台scan 进行的判断标准是:4-Way Handshake

           结束后就是wpa_complete ,这之前停止后台scan !!! */

                   =====> netlink_send_oper_ifla (??????)

                   =====> wpas_notify_state_changed

       ===> wpa_drv_set_country(wpa_s, wpa_s->conf->country)

             ====>wpa_driver_nl80211_set_country

         if (wpa_s->conf->country[0] && wpa_s->conf->country[1]  

         /* 上面这句的意思 country已经通过CRDA获得了才设置 */

       ===> wpa_sm_set_own_addr

       ===> wpa_supplicant_init_eapol(struct wpa_supplicant *wps)

                  和wpa_supplicant_init_wpa 类似

           设置struct eapol_ctx  相关function  (处理eapol 状态机变化的func !!!)

            ====> eapol_sm_init  Initialize EAPOL state machine

         (RFC4137,)

            ====> eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); 

               =====> tls_init (tls openssl 的init)

            ====> eloop_register_timeout(1, 0, eapol_port_timers_tick

                /* PAE 端口认证实体,EAP 端口超时处理 */, NULL, sm);

        ===> wpa_sm_set_eapol (/* wpa-sm 与 eapol -sm 关联起来*/)
        ===> wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); 

     (wpa_cli)

            1. 根据conf ctrl_iface=wlan0:0设置 在/data/misc/wifi/wlan0:0 

            (android init.rc 中已经做了)

            2. eloop_register_read_sock(priv->sock, 

                wpa_supplicant_ctrl_iface_receive, wpa_s, priv);

            3. wpa_msg_register_cb (wpa_supplicant_ctrl_iface_msg_cb);

        /* 注册msg debug call back func*/

  由 wpa_msg_ctrl 回调 wpa_supplicant_ctrl_iface_msg_cb 

                             -> wpa_supplicant_ctrl_iface_send

                ( Send a control interface packet to monitors)

   发给V/WifiMonitor( 1172): Event [CTRL-EVENT-BSS-ADDED 3 04:21:b0:e0:20:20]

                sendmsg(priv->sock, &msg, 0) ,priv 就是init.rc 中

                priv->sock = android_get_control_socket(addr.sun_path);

      socket wpa_wlan0:0 dgram 660 wifi wifi

       ===>  wpas_p2p_init (struct wpa_global * (wpa_s->global), 

                         struct wpa_supplicant * wpa_s) 

     p2p 相关,以后再看,已经不行了

       ===>  wpa_bss_init 

    eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, 

                 wpa_bss_timeout, wpa_s, NULL);

           关于上面看下面这段话,可能和bss(网络或AP)的过期与定期更新有关

Currently, this is very simple: check every WPA_BSS_EXPIRATION_PERIOD

(10 sec by default) whether there are entries that are over

WPA_BSS_EXPIRATION_AGE (180 seconds) old and expire them if they are ot

in use (BSS entry age is the time from the last update in it, e.g.,

based on scan results). The other rule for expiring entries is based on

new scan results: expire an entry if it has not been seen in last

WPA_BSS_EXPIRATION_SCAN_COUNT (2). The latter one will eventually be

improved to handle partial scans (i.e., only some channels/SSIDs being

scanned; those should only expire matching BSS entries). In addition,

there is a maximum limit on the BSS entries (200) and new BSS entries

added above that will end up getting the oldest entry getting removed.
These values will likely end up being configurable at some point and I

have also considered providing options for controlling the BSS list

updating "mode". For example, wpa_supplicant could be requested to keep

the BSS table more frequently up-to-date for all BSSes or for specific

ESSes. This could have some connections with the bgscan mechanism. In

addition, this could get more input from things like Microsoft Wireless

Provisioning Services (multi-SSID/hidden SSID) and IEEE 802.11k neighbor

reports, etc.

      

   ==> wpas_notify_iface_added   /* 不支持dbus,什么也不做*/

   ==> 读init.rc 设置supplicant_scan_interval 

         wifi无线局域网扫描间隔时间,单位为秒。调大这个值可节约耗电。

         如果不设置,就用 wpa_supplicant_alloc 设置的5 秒 (wpa_s->scan_interval = 5;)

   ==> wpas_notify_network_addedstruct wpa_supplicant *wpa_s,struct wpa_ssid *ssid)

        把conf 文件中的 network {中的
=> wpa_supplicant_run  -> eloop_run();  开始epool 大循环 ,进行event 处理(callback)
 
 
下面简但列下 eloop_run 相关handle

        eloop_register_read_sock , eloop_register_timeout

 

1. nl_handle_event :

  注册nl_handle_event 的sock 到eloop reads table 而nl_handle_event  

  加入多播scan ,mlme,regulator组

  eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), 

  wpa_driver_nl80211_event_receive, drv, drv->nl_handle_event);

  和compat driver 通讯 接收scan,mlme,regulator 多播
 
2.netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);  

 netlink目前使用最广泛的是通过NETLINK_ROUTE 这个选项来获取网络设备或者网址的一些信息

 注册到NETLINK_ROUTE socket 到eloop reads table

 eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL);
 
3. l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol));   /* l2 socket !!! */

   eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); eloop 读l2 packet  进入 !!!
 
4. 每隔1s 执行eapol_port_timers_tick

  / * This statemachine is implemented as a function that will be called

      * once a second as a registered event loop timeout.

      */

   eloop_register_timeout(1, 0, eapol_port_timers_tick/*PAE 端口认证实体,EAP 端口超时*/, NULL, sm);
 
5. scan 结果,过期处理? 

  eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, wpa_bss_timeout, wpa_s, NULL);
 
6.wpa ctrl 命令处理:

   eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);


7. scan timeout when driver 

  eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,drv, drv->ctx);

  wpa_driver_nl80211_scan_timeout:

/* This function can be used as registered timeout when starting a scan to

 * generate a scan completed event if the driver does not report this.

 */


8. P2P: 主要有下面这些
P2p.c (src\p2p):   eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,

P2p.c (src\p2p):   eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);

P2p.c (src\p2p):   eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,

P2p.c (src\p2p):   eloop_register_timeout(timeout, 0, p2p_find_timeout,

P2p.c (src\p2p):   eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,

P2p.c (src\p2p):   eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);

P2p.c (src\p2p):   eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);

P2p.c (src\p2p):   eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,

P2p.c (src\p2p):   eloop_register_timeout(p2p->ext_listen_interval_sec,

P2p.c (src\p2p):   eloop_register_timeout(p2p->ext_listen_interval_sec,

P2p_supplicant.c:  eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);

P2p_supplicant.c:  eloop_register_timeout(15 + res->peer_config_timeout / 100,

P2p_supplicant.c:  eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);

P2p_supplicant.c:  eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);

P2p_supplicant.c:  eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,

P2p_supplicant.c:  eloop_register_timeout(timeout, 0,

P2p_supplicant.c:  eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0,
 
画个结构关联图:

(*x) --表示注释

 
【上篇】
【下篇】

抱歉!评论已关闭.