wpa_supplicant代码分析(一)——wpa_supplicant_init

 

目录

 

一、环境介绍

二、wpa_supplicant_init() 代码实现

2.1 重要结构体struct wpa_params

2.2  wpa_supplicant_init 代码实现

三、代码逻辑及过程详述

3.1 wpa_msg_register_ifname_cb()

3.1.1 wpa_msg_register_ifname_cb()代码实现

3.1.2 wpa_msg_ifname_cb的使用

3.2 params内容存放到global结构中

3.2.1 保存params代码实现

3.3 eloop_init()

3.3.1 eloop_init()代码实现

3.4、random_init()

3.4.1 random_init() 代码实现

3.5、eloop_register_timeout()

3.5.1 eloop_register_timeout() 代码实现

3.5.2 eloop中的timeout list


一、环境介绍

  • 硬件环境:Freescale IMX283
  • 软件环境:Linux
  • WI-FI驱动:rtl8192eu
  • Wpa_supplicant版本:2.6

 

二、wpa_supplicant_init() 代码实现

talk is cheap, show me the code......

2.1 重要结构体struct wpa_params

从wpa_supplicant代码的main函数开始讲起,其中涉及到比较重要的结构体,其中一个是 struct wpa_params,其他结构及作用将在其他章节中展开叙述,成员如下表格:

成员

类型

说明

daemonize

int

以守护进程方式运行wpa_supplicant库

wait_for_monitor

int

等待monitor接口启动

pid_file

char *

以deamon方式运行时的保存进程pid的文件

wpa_debug_level

int

打印等级

wpa_debug_show_keys

int

打印密码相关的关系到安全的内容

wpa_debug_timestamp

Int

打印前加时间戳

ctrl_interface

char *

控制接口,暂未使用

ctrl_interface_group

char *

控制接口组,暂未使用

dbus_ctrl_interface

int

dbus控制接口

wpa_debug_file_path

const char *

保存日志的文件

wpa_debug_syslog

int

调试信息通过syslog输出

wpa_debug_tracing

int

用于linux系统,前提必须挂载debugfs

override_driver

char *

暂未使用

override_ctrl_interface

char *

暂未使用

entropy_file

char *

暂未使用

2.2  wpa_supplicant_init 代码实现

main函数中调用 wpa_supplicant_init(&params),顾名思义主要是为了supplicant的一些变量、文件等等的一些初始化,show the code:

/**
 * wpa_supplicant_init - Initialize %wpa_supplicant
 * @params: Parameters for %wpa_supplicant
 * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure
 *
 * This function is used to initialize %wpa_supplicant. After successful
 * initialization, the returned data pointer can be used to add and remove
 * network interfaces, and eventually, to deinitialize %wpa_supplicant.
 */
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_ifname_cb = wpa_supplicant_msg_ifname_cb, 后详述*/
	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
#endif /* CONFIG_NO_WPA_MSG */

	/* 打开debug信息文件 */
	if (params->wpa_debug_file_path)
		wpa_debug_open_file(params->wpa_debug_file_path);
	else
		wpa_debug_setup_stdout();

	/* syslog记录日志 */
	if (params->wpa_debug_syslog)
		wpa_debug_open_syslog();

	/* 基于debugfs的记录linux的trace,本人未用*/
	if (params->wpa_debug_tracing) {
		ret = wpa_debug_open_linux_tracing();
		if (ret) {
			wpa_printf(MSG_ERROR,
				   "Failed to enable trace logging");
			return NULL;
		}
	}

	/* 注册认证方式eap methods */
	ret = eap_register_methods();
	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结构,并将params存放到global中 */
	global = os_zalloc(sizeof(*global));
	if (global == NULL)
		return NULL;
	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);
#ifdef CONFIG_MATCH_IFACE
	global->params.match_iface_count = params->match_iface_count;
	if (params->match_iface_count) {
		global->params.match_ifaces =
			os_calloc(params->match_iface_count,
				  sizeof(struct wpa_interface));
		os_memcpy(global->params.match_ifaces,
			  params->match_ifaces,
			  params->match_iface_count *
			  sizeof(struct wpa_interface));
	}
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_P2P
	if (params->conf_p2p_dev)
		global->params.conf_p2p_dev =
			os_strdup(params->conf_p2p_dev);
#endif /* CONFIG_P2P */
	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);

	/* eloop全局变量初始化 核心结构,后详述 */
	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;
	}

	/* wpa notify 初始化 dbus或者binder时会用到,本人未使用 */
	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;
	}
	global->drv_priv = os_calloc(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 */

	/* wpas_periodic 注册到eloop的timeout中,后详述 */
	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
			       wpas_periodic, global, NULL);

	return global;
}

三、代码逻辑及过程详述

提炼一下上述整个过程:

wpa_supplicant_init()
    |- wpa_msg_register_ifname_cb()
    |- wpa_debug_open_file()
    |- wpa_debug_open_syslog()
    |- wpa_debug_open_linux_tracing()
    |- eap_register_methods()
    |- params >>> global
    |- eloop_init()
    |- random_init()
    |- wpa_supplicant_global_ctrl_iface_init()
    |- wpas_notify_supplicant_initialized()
    |- eloop_register_timeout()

上述红色部分的过程将展开讲述下,以逐步建立对wpa_supplicant的理解;

 

3.1 wpa_msg_register_ifname_cb()

3.1.1 wpa_msg_register_ifname_cb()代码实现


static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;

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

static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
{
	struct wpa_supplicant *wpa_s = ctx;
	if (wpa_s == NULL)
		return NULL;
	return wpa_s->ifname;
}

/* 在wpa_supplicant_init中调用关系 */
wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);

 wpa_msg_ifname_cb() 的内容实际上就是将 wpa_supplicant_msg_ifname_cb 注册到 wpa_msg_ifname_cb 后进行回调使用,而按照命名和代码是实现看该函数就是返回ifname。该ifname由启动wpa_supplicant指定,如下通过 -i 选项指定 wlan0:

#!/bin/sh

wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf

3.1.2 wpa_msg_ifname_cb的使用

其主要作用就要关系到函数 wpa_msg(void *ctx, int level, const char *fmt, ...); 该函数中会调用wpa_msg_ifname_cb 将ifname加到调试信息的前面作为前缀prefix,如扫描结果:

1380.318157: wlan0:    skip - SSID mismatch
1380.318375: wlan0: 1: dc:fe:18:c1:c4:ea ssid='TPTPTP' wpa_ie_len=26 rsn_ie_len=24 caps=0x11 level=24 freq=2412 
1380.318563: wlan0:    skip - SSID mismatch
1380.318813: wlan0: 2: 10:be:f5:1b:80:24 ssid='1543-1' wpa_ie_len=26 rsn_ie_len=24 caps=0x11 level=28 freq=2412  wps
1380.651938: wlan0:    skip - SSID mismatch
1380.652188: wlan0: 3: bc:54:fc:3f:2a:b8 ssid='MERCURY_2AB8' wpa_ie_len=22 rsn_ie_len=20 caps=0x11 level=20 freq=2462 
1380.652344: wlan0:    skip - SSID mismatch

整个扫描结果的调试信息是通过函数 wpa_msg () 打印出来,而请字符串 wlan0 就是通过回调函数 wpa_msg_ifname_cb() 获取的当前所使用的网络节点。关于wpa_msg () 后面章节再进行剖析,调试接口可以引领我们去熟悉 wpa_supplicant 的代码逻辑。

 

3.2 params内容存放到global结构中

3.2.1 保存params代码实现

/* 创建global结构,并将params存放到global中 */
	global = os_zalloc(sizeof(*global));
	if (global == NULL)
		return NULL;
	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);
#ifdef CONFIG_MATCH_IFACE
	global->params.match_iface_count = params->match_iface_count;
	if (params->match_iface_count) {
		global->params.match_ifaces =
			os_calloc(params->match_iface_count,
				  sizeof(struct wpa_interface));
		os_memcpy(global->params.match_ifaces,
			  params->match_ifaces,
			  params->match_iface_count *
			  sizeof(struct wpa_interface));
	}
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_P2P
	if (params->conf_p2p_dev)
		global->params.conf_p2p_dev =
			os_strdup(params->conf_p2p_dev);
#endif /* CONFIG_P2P */
	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_supplicant 的main函数中定义了一个 struct wpa_global *global, 其中就包含了struct wpa_params结构。而上述代码就是将整合了用户传递的参数之后的params 信息存放到global中;当然global中的信息实际很丰富,包括很多内容,这里暂时不一一列举;

 

3.3 eloop_init()

3.3.1 eloop_init()代码实现

int eloop_init(void)
{
	os_memset(&eloop, 0, sizeof(eloop));
	dl_list_init(&eloop.timeout);
	
	return 0;
}

eloop 的初始化其实再简单不过了,而这里之所以将其梳理出来主要是因为 eloop 本身的重要性,整个 wpa_supplicant 都是围绕eloop实现,后续会进行详细阐述。

 

3.4、random_init()

3.4.1 random_init() 代码实现

void random_init(const char *entropy_file)
{
	os_free(random_entropy_file);
	if (entropy_file)
		random_entropy_file = os_strdup(entropy_file);
	else
		random_entropy_file = NULL;
	random_read_entropy();

#ifdef __linux__
	if (random_fd >= 0)
		return;

	random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
	if (random_fd < 0) {
		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
			   strerror(errno));
		return;
	}
	wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
		   "/dev/random");

	eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
#endif /* __linux__ */

	random_write_entropy();
}

由于没有传递 params.entropy_file,因此该函数参数为NULL,因此有效代码就是在 #ifdef __linux__和#endif之间,其内容为打开 "/dev/random" 节点,并将文件描述符 random_fd 和 random_read_fd 函数通过eloop_register_read_sock() 注册到eloop中;

int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data);

该函数在后续会进行重点展开叙述,其为wpa_supplicant的核心代码;

 

3.5、eloop_register_timeout()

3.5.1 eloop_register_timeout() 代码实现

int eloop_register_timeout(unsigned int secs, unsigned int usecs,
			   eloop_timeout_handler handler,
			   void *eloop_data, void *user_data)
{
	struct eloop_timeout *timeout, *tmp;
	os_time_t now_sec;

	timeout = os_zalloc(sizeof(*timeout));
	if (timeout == NULL)
		return -1;
	if (os_get_reltime(&timeout->time) < 0) {
		os_free(timeout);
		return -1;
	}
	now_sec = timeout->time.sec;
	timeout->time.sec += secs;
	if (timeout->time.sec < now_sec) {
		/*
		 * Integer overflow - assume long enough timeout to be assumed
		 * to be infinite, i.e., the timeout would never happen.
		 */
		wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
			   "ever happen - ignore it", secs);
		os_free(timeout);
		return 0;
	}
	timeout->time.usec += usecs;
	while (timeout->time.usec >= 1000000) {
		timeout->time.sec++;
		timeout->time.usec -= 1000000;
	}
	timeout->eloop_data = eloop_data;
	timeout->user_data = user_data;
	timeout->handler = handler;
	wpa_trace_add_ref(timeout, eloop, eloop_data);
	wpa_trace_add_ref(timeout, user, user_data);
	wpa_trace_record(timeout);

	/* Maintain timeouts in order of increasing time */
	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
		if (os_reltime_before(&timeout->time, &tmp->time)) {
			dl_list_add(tmp->list.prev, &timeout->list);
			return 0;
		}
	}
	dl_list_add_tail(&eloop.timeout, &timeout->list);

	return 0;
}

3.5.2 eloop中的timeout list

struct eloop_data {
	int max_sock;

	int count; /* sum of all table counts */

	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;
};

timeout的事件及参数如下结构:

struct eloop_timeout {
	struct dl_list list;
	struct os_reltime time;
	void *eloop_data;
	void *user_data;
	eloop_timeout_handler handler;
	WPA_TRACE_REF(eloop);
	WPA_TRACE_REF(user);
	WPA_TRACE_INFO
};

eloop 的 timeout 事件通过双向链表进行串联起来,eloop.timeout为链表的head节点,框图如下:

根据代码可以看到其排序依据为从timeout.time的时间,时间小的排在前面(eloop.timeout.next指针方向);

实际何时调用在链表中的timeout事件则后续章节中进行阐述,当然从这个timeout时间的time成员就可以看出这个和时间的推移有关。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页