Wpa_supplicant工作流程分析之scan(从应用层到内核)(上)

Wpa_supplicant工作流程分析

wpa_supplicant官网:https://w1.fi/wpa_supplicant/

wpa_supplicant源码下载官网地址:https://w1.fi/releases/

本文分析基于版本:V2.6

1. 初始化

wpa_supplicant/main.c

在main()中,完成了四件事。先看一下源代码,我们再来一一分析。

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;

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

	for (;;) {
		c = getopt(argc, argv,
			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No: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;
			break;
		case 'C':
			iface->ctrl_interface = optarg;
			break;
		case 'D':
			iface->driver = optarg;
			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;
			break;
		case 'I':
			iface->confanother = optarg;
			break;
		case 'K':
			params.wpa_debug_show_keys++;
			break;
		case 'L':
			license();
			exitcode = 0;
			goto out;
#ifdef CONFIG_P2P
		case 'm':
			params.conf_p2p_dev = optarg;
			break;
#endif /* CONFIG_P2P */
		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;
#ifdef CONFIG_MATCH_IFACE
		case 'M':
			params.match_iface_count++;
			iface = os_realloc_array(params.match_ifaces,
						 params.match_iface_count,
						 sizeof(struct wpa_interface));
			if (!iface)
				goto out;
			params.match_ifaces = iface;
			iface = &params.match_ifaces[params.match_iface_count -
						     1];
			os_memset(iface, 0, sizeof(*iface));
			break;
#endif /* CONFIG_MATCH_IFACE */
		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;
	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");
	}

	if (fst_global_init()) {
		wpa_printf(MSG_ERROR, "Failed to initialize FST");
		exitcode = -1;
		goto out;
	}

#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
	if (!fst_global_add_ctrl(fst_ctrl_cli))
		wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
#endif

	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 ||
#ifdef CONFIG_MATCH_IFACE
						 params.match_iface_count ||
#endif /* CONFIG_MATCH_IFACE */
						 params.dbus_ctrl_interface))
				break;
			usage();
			exitcode = -1;
			break;
		}
		wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
		if (wpa_s == NULL) {
			exitcode = -1;
			break;
		}
	}

#ifdef CONFIG_MATCH_IFACE
	if (exitcode == 0)
		exitcode = wpa_supplicant_init_match(global);
#endif /* CONFIG_MATCH_IFACE */

	if (exitcode == 0)
		exitcode = wpa_supplicant_run(global);

	wpa_supplicant_deinit(global);

	fst_global_deinit();

out:
	wpa_supplicant_fd_workaround(0);
	os_free(ifaces);
#ifdef CONFIG_MATCH_IFACE
	os_free(params.match_ifaces);
#endif /* CONFIG_MATCH_IFACE */
	os_free(params.pid_file);

	os_program_deinit();

	return exitcode;
}
  1. 解析init.rc(这种情况是设备开机自启动wpa_supplicant进程时wpa_supplicant做的操作)或者是用户输入(如我们手动启动wpa_supplicant进程)的相关参数。一般格式如下:
wpa_supplicant/system/bin/wpa_supplicant -Dwext -iwlan0 -c /data/misc/wifi/wpa_supplicant.conf

-D : 表示用那种驱动程序,一般默认是wext。现在大部分
	 都是用nl80211。
-i : 表示网络接口名称
-c : 表示配置文件名称
    
从源码中我们可以看到,在main中,wpa_supplicant会根据传进来的参数填充iface和param这两个结构体。
  1. 调用wpa_supplicant_init()进行一些初始化的操作。

    wpa_supplicant_init()返回的数据指针可用于添加和删除

    网络接口。

  2. 调用wpa_supplicant_add_iface()来添加控制接口。

    1. 创建一个wpa_supplicant结构体。
    2. 调用wpa_supplicant_init_iface()去做一些出初始化接口的工作。
  3. 调用wpa_supplicant_run()运行wpa_supplicant事件主循环。

    对eloop循环进行配置后调用eloop_run)()让eloop循环run起来。然后wpa_supplicant就在这个循环中处理来自上层的命令和来自下层的事件。

2. wpa_supplicant如何接收和响应上层命令

void eloop_run(void)
{
#ifdef CONFIG_ELOOP_POLL
	int num_poll_fds;
	int timeout_ms = 0;
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
	fd_set *rfds, *wfds, *efds;
	struct timeval _tv;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
	int timeout_ms = -1;
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
	struct timespec ts;
#endif /* CONFIG_ELOOP_KQUEUE */
	int res;
	struct os_reltime tv, now;

#ifdef CONFIG_ELOOP_SELECT
	rfds = os_malloc(sizeof(*rfds));
	wfds = os_malloc(sizeof(*wfds));
	efds = os_malloc(sizeof(*efds));
	if (rfds == NULL || wfds == NULL || efds == NULL)
		goto out;
#endif /* CONFIG_ELOOP_SELECT */

	while (!eloop.terminate &&
	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
		struct eloop_timeout *timeout;

		if (eloop.pending_terminate) {
			/*
			 * This may happen in some corner cases where a signal
			 * is received during a blocking operation. We need to
			 * process the pending signals and exit if requested to
			 * avoid hitting the SIGALRM limit if the blocking
			 * operation took more than two seconds.
			 */
			eloop_process_pending_signals();
			if (eloop.terminate)
				break;
		}

		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
					list);
		if (timeout) {
			os_get_reltime(&now);
			if (os_reltime_before(&now, &timeout->time))
				os_reltime_sub(&timeout->time, &now, &tv);
			else
				tv.sec = tv.usec = 0;
#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
#ifdef CONFIG_ELOOP_SELECT
			_tv.tv_sec = tv.sec;
			_tv.tv_usec = tv.usec;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_KQUEUE
			ts.tv_sec = tv.sec;
			ts.tv_nsec = tv.usec * 1000L;
#endif /* CONFIG_ELOOP_KQUEUE */
		}

#ifdef CONFIG_ELOOP_POLL
		num_poll_fds = eloop_sock_table_set_fds(
			&eloop.readers, &eloop.writers, &eloop.exceptions,
			eloop.pollfds, eloop.pollfds_map,
			eloop.max_pollfd_map);
		res = poll(eloop.pollfds, num_poll_fds,
			   timeout ? timeout_ms : -1);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
		eloop_sock_table_set_fds(&eloop.readers, rfds);
		eloop_sock_table_set_fds(&eloop.writers, wfds);
		eloop_sock_table_set_fds(&eloop.exceptions, efds);
		res = select(eloop.max_sock + 1, rfds, wfds, efds,
			     timeout ? &_tv : NULL);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
		if (eloop.count == 0) {
			res = 0;
		} else {
			res = epoll_wait(eloop.epollfd, eloop.epoll_events,
					 eloop.count, timeout_ms);
		}
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
		if (eloop.count == 0) {
			res = 0;
		} else {
			res = kevent(eloop.kqueuefd, NULL, 0,
				     eloop.kqueue_events, eloop.kqueue_nevents,
				     timeout ? &ts : NULL);
		}
#endif /* CONFIG_ELOOP_KQUEUE */
		if (res < 0 && errno != EINTR && errno != 0) {
			wpa_printf(MSG_ERROR, "eloop: %s: %s",
#ifdef CONFIG_ELOOP_POLL
				   "poll"
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
				   "select"
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
				   "epoll"
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
				   "kqueue"
#endif /* CONFIG_ELOOP_EKQUEUE */

				   , strerror(errno));
			goto out;
		}

		eloop.readers.changed = 0;
		eloop.writers.changed = 0;
		eloop.exceptions.changed = 0;

		eloop_process_pending_signals();


		/* check if some registered timeouts have occurred */
		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
					list);
		if (timeout) {
			os_get_reltime(&now);
			if (!os_reltime_before(&now, &timeout->time)) {
				void *eloop_data = timeout->eloop_data;
				void *user_data = timeout->user_data;
				eloop_timeout_handler handler =
					timeout->handler;
				eloop_remove_timeout(timeout);
				handler(eloop_data, user_data);
			}

		}

		if (res <= 0)
			continue;

		if (eloop.readers.changed ||
		    eloop.writers.changed ||
		    eloop.exceptions.changed) {
			 /*
			  * Sockets may have been closed and reopened with the
			  * same FD in the signal or timeout handlers, so we
			  * must skip the previous results and check again
			  * whether any of the currently registered sockets have
			  * events.
			  */
			continue;
		}

#ifdef CONFIG_ELOOP_POLL
		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
					  &eloop.exceptions, eloop.pollfds_map,
					  eloop.max_pollfd_map);
#endif /* CONFIG_ELOOP_POLL */
/*
*调用select系统调用以轮询几类事件:相关fd是否可读、可写
*是否有异常发生、超时。
*如果相关事件发生时,eloop_sock_table_dispatch()将按照*下面的方式来处理它,它会通过调用相关的handler()回调函数来*处理这些事件。
*/
#ifdef CONFIG_ELOOP_SELECT
		eloop_sock_table_dispatch(&eloop.readers, rfds);
		eloop_sock_table_dispatch(&eloop.writers, wfds);
		eloop_sock_table_dispatch(&eloop.exceptions, efds);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
		eloop_sock_table_dispatch(eloop.epoll_events, res);
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
		eloop_sock_table_dispatch(eloop.kqueue_events, res);
#endif /* CONFIG_ELOOP_KQUEUE */
	}

	eloop.terminate = 0;
out:
#ifdef CONFIG_ELOOP_SELECT
	os_free(rfds);
	os_free(wfds);
	os_free(efds);
#endif /* CONFIG_ELOOP_SELECT */
	return;
}

3. handler()回调函数的注册

以read事件为例:

不同类型的事件来源会写自己的handler函数,如udp、unix通用等。最终都会调用一下接口:
-->eloop_register_read_sock()
  -->eloop_register_sock()
    -->eloop_get_sock_table() //不同类型返回不同的table
    -->eloop_sock_table_add_sock() //将其添加到table->table[i].handler

通过上面这个流程,先关handler()回调函数就被注册到table,当相关事件发生后,在调用相关的handler()对事件进行处理。

4. wpa_supplicant 扫描流程分析

在上一小节中说到了注册handler()回调函数,其中一个read事件的handler()注册接口如下:

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

该接口注册了一个wpa_supplicant_ctrl_iface_receive()的handler。

  1. wpa_supplicant_ctrl_iface_receive()的主要工作是监听socket,并且处理来自相关socket的command。下面来看下该函数的源码:
static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,void *sock_ctx)
{
	struct wpa_supplicant *wpa_s = eloop_ctx;
	struct ctrl_iface_priv *priv = sock_ctx;
	char buf[4096];
	int res;
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	char *reply = NULL, *reply_buf = NULL;
	size_t reply_len = 0;
	int new_attached = 0;
	//recvform()监听相关socket
	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
		       (struct sockaddr *)&from,&fromlen);
	if (res < 0) {
	wpa_printf(MSG_ERROR,"recvfrom(ctrl_iface): 				%s",strerror(errno));
		return;
	}
	buf[res] = '\0';

	if (os_strcmp(buf, "ATTACH") == 0) {
		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,fromlen, 0))
		reply_len = 1;
		else {
			new_attached = 1;
			reply_len = 2;
		}
	} else if (os_strcmp(buf, "DETACH") == 0) {
		if(wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,fromlen))
			reply_len = 1;
		else
			reply_len = 2;
	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) 		{
		if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,buf + 6))
			reply_len = 1;
		else
			reply_len = 2;
	} else {
/*
*将相关命令,交给wpa_supplicant_ctrl_iface_process()  *来处理。
*/
		reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
							      &reply_len);
		reply = reply_buf;
....
/*
*将相关的处理结果反馈给相应的socket。
*/
	if (reply) {
		wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply,
				     reply_len);
		if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
			   fromlen) < 0) {
			int _errno = errno;
			wpa_dbg(wpa_s, MSG_DEBUG,
				"ctrl_iface sendto failed: %d - %s",
				_errno, strerror(_errno));
			if (_errno == ENOBUFS || _errno == EAGAIN) {
				/*
				 * The socket send buffer could be full. This
				 * may happen if client programs are not
				 * receiving their pending messages. Close and
				 * reopen the socket as a workaround to avoid
				 * getting stuck being unable to send any new
				 * responses.
				 */
				sock = wpas_ctrl_iface_reinit(wpa_s, priv);
				if (sock < 0) {
					wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket");
				}
			}
			if (new_attached) {
				wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching");
				new_attached = 0;
				wpa_supplicant_ctrl_iface_detach(
					&priv->ctrl_dst, &from, fromlen);
			}
		}
	}
	os_free(reply_buf);

	if (new_attached)
		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
  1. wpa_supplicant_ctrl_iface_process()

    wpa_supplicant_ctrl_iface_process()接口会对命令的类型进行判断,如“SCAN”

···
    else if(os_strcmp(buf,"SCAN") == 0)
        if(!wpa_s->scan_ongoing){
            wpa_s->scan_req = 2;
            wpa_supplicant_req_scan(wpa_s,0,0);
        }else
            wpa_printf(MSG_DEBUG,"Ongoing Scan 							action...");
...
  1. wpa_ctrl_scan() 处理扫描命令
  2. wpa_supplicant_req_scan 安排对附近的AP进行扫描
  3. wpa_supplicant通过前面讲到的‘-D“参数调用driver的ioctl接口。这里可以看到wpa_supplicant对下的一些驱动接口。
const struct wpa_driver_ops *const wpa_drivers[] =
{
#ifdef CONFIG_DRIVER_NL80211
	&wpa_driver_nl80211_ops,
#endif /* CONFIG_DRIVER_NL80211 */
#ifdef CONFIG_DRIVER_WEXT
	&wpa_driver_wext_ops,
#endif /* CONFIG_DRIVER_WEXT */
#ifdef CONFIG_DRIVER_HOSTAP
	&wpa_driver_hostap_ops,
#endif /* CONFIG_DRIVER_HOSTAP */
#ifdef CONFIG_DRIVER_BSD
	&wpa_driver_bsd_ops,
#endif /* CONFIG_DRIVER_BSD */
#ifdef CONFIG_DRIVER_OPENBSD
	&wpa_driver_openbsd_ops,
#endif /* CONFIG_DRIVER_OPENBSD */
#ifdef CONFIG_DRIVER_NDIS
	&wpa_driver_ndis_ops,
#endif /* CONFIG_DRIVER_NDIS */
#ifdef CONFIG_DRIVER_WIRED
	&wpa_driver_wired_ops,
#endif /* CONFIG_DRIVER_WIRED */
#ifdef CONFIG_DRIVER_MACSEC_QCA
	&wpa_driver_macsec_qca_ops,
#endif /* CONFIG_DRIVER_MACSEC_QCA */
#ifdef CONFIG_DRIVER_ROBOSWITCH
	&wpa_driver_roboswitch_ops,
#endif /* CONFIG_DRIVER_ROBOSWITCH */
#ifdef CONFIG_DRIVER_ATHEROS
	&wpa_driver_atheros_ops,
#endif /* CONFIG_DRIVER_ATHEROS */
#ifdef CONFIG_DRIVER_NONE
	&wpa_driver_none_ops,
#endif /* CONFIG_DRIVER_NONE */
	NULL
};

//以wext为例看看它支持的相关接口
const struct wpa_driver_ops wpa_driver_wext_ops = {
	.name = "wext",
	.desc = "Linux wireless extensions (generic)",
	.get_bssid = wpa_driver_wext_get_bssid,
	.get_ssid = wpa_driver_wext_get_ssid,
	.set_key = wpa_driver_wext_set_key,
	.set_countermeasures = wpa_driver_wext_set_countermeasures,
	.scan2 = wpa_driver_wext_scan,
	.get_scan_results2 = wpa_driver_wext_get_scan_results,
	.deauthenticate = wpa_driver_wext_deauthenticate,
	.associate = wpa_driver_wext_associate,
	.init = wpa_driver_wext_init,
	.deinit = wpa_driver_wext_deinit,
	.add_pmkid = wpa_driver_wext_add_pmkid,
	.remove_pmkid = wpa_driver_wext_remove_pmkid,
	.flush_pmkid = wpa_driver_wext_flush_pmkid,
	.get_capa = wpa_driver_wext_get_capa,
	.set_operstate = wpa_driver_wext_set_operstate,
	.get_radio_name = wext_get_radio_name,
	.signal_poll = wpa_driver_wext_signal_poll,
	.status = wpa_driver_wext_status,
};
  1. wpa_supplicant_scan —> wpa_supplicant_trigger_scan 请求driver进行scan
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
				struct wpa_driver_scan_params *params)
{
	struct wpa_driver_scan_params *ctx;

	if (wpa_s->scan_work) {
		wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
		return -1;
	}

	ctx = wpa_scan_clone_params(params);
	if (!ctx ||
	    radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
	{
		wpa_scan_free_params(ctx);
		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
		return -1;
	}

	return 0;
}
  1. radio_add_work–>wpas_trigger_scan_cb(回调函数)–>wpa_drv_scan

    调用之前指定驱动所注册的函数,比如之前-D参数指定的驱动,假如指定的是wext,那么会调用到wpa_s->driver->scan2调用到wpa_driver_wext_scan(这个在前面有提到过,这个结构体wpa_driver_wext_ops会注册这个接口)通过ioctl将扫描指令下发给driver。

static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,struct wpa_driver_scan_params *params)
{
#ifdef CONFIG_TESTING_OPTIONS
	if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
		return -EBUSY;
#endif /* CONFIG_TESTING_OPTIONS */
//此处回去选择调用相关驱动注册的scan接口
	if (wpa_s->driver->scan2)
		return wpa_s->driver->scan2(wpa_s->drv_priv, params);
	return -1;
}
...
int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
{
	....//省略无关代码
//通过ioctl()接口将扫描请求下发到driver,drv->ioctl_sock在下面会讲到
	if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
			   strerror(errno));
		ret = -1;
	}

	....//省略无关代码
}  

//ioctl()接口中使用了drv->ioctl_sock,它是来自于wpa_driver_wext_init(),该函数主要对wext driver接口进行初始化,如下:
void * wpa_driver_wext_init(void *ctx, const char *ifname)
{
	struct wpa_driver_wext_data *drv;
	struct netlink_config *cfg;
	struct rfkill_config *rcfg;
	char path[128];
	struct stat buf;

	drv = os_zalloc(sizeof(*drv));
	if (drv == NULL)
		return NULL;
	drv->ctx = ctx;
	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));

	os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname);
	if (stat(path, &buf) == 0) {
		wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
		drv->cfg80211 = 1;
		wext_get_phy_name(drv);
	}
//我们用到的drv->ioctl_sock,在此处创建以及初始化
	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (drv->ioctl_sock < 0) {
		wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",strerror(errno));
		goto err1;
	}

	cfg = os_zalloc(sizeof(*cfg));
	if (cfg == NULL)
		goto err1;
	cfg->ctx = drv;
	cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
	cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
	drv->netlink = netlink_init(cfg);
	if (drv->netlink == NULL) {
		os_free(cfg);
		goto err2;
	}

	rcfg = os_zalloc(sizeof(*rcfg));
	if (rcfg == NULL)
		goto err3;
	rcfg->ctx = drv;
	os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
	rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
	rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
	drv->rfkill = rfkill_init(rcfg);
	if (drv->rfkill == NULL) {
		wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
		os_free(rcfg);
	}

	drv->mlme_sock = -1;

	if (wpa_driver_wext_finish_drv_init(drv) < 0)
		goto err3;

	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);

	return drv;

err3:
	rfkill_deinit(drv->rfkill);
	netlink_deinit(drv->netlink);
err2:
	close(drv->ioctl_sock);
err1:
	os_free(drv);
	return NULL;
}

至此,wpa_supplicant的工作已经全部完成。接下,这个scan指令会继续下发kernel中交给driver去实现。

上一篇:python常用网络获取信息函数


下一篇:Android Retrofit+RXJava的使用