ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-12-31 15:58:03  阅读:403  来源: 互联网

标签:eloop iface scan supplicant driver Wpa CONFIG wpa


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去实现。

标签:eloop,iface,scan,supplicant,driver,Wpa,CONFIG,wpa
来源: https://blog.csdn.net/wit_732/article/details/112018926

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有