1. 程式人生 > >(一百零三)Android O wpa_supplicant初始化學習(二)

(一百零三)Android O wpa_supplicant初始化學習(二)

前言:之前在(一百零二)Android O wpa_supplicant初始化學習 梳理了對wpa_supplicant 啟動引數的解析以及儲存,對整體的初始化未作探究,繼續梳理一下。

 

1.初始化流程梳理

1.1 os_program_init

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_program_init()進行初始化。看下os_program_init()所做工作。

搜尋發現有5個檔案定義了該方法,看下Android.mk檔案

 137 ifndef CONFIG_OS
 138 ifdef CONFIG_NATIVE_WINDOWS
 139 CONFIG_OS=win32
 140 else
 141 CONFIG_OS=unix
 142 endif
 143 endif
 144 
 145 ifeq ($(CONFIG_OS), internal)
 146 L_CFLAGS += -DOS_NO_C_LIB_DEFINES
 147 endif
 148 
 149 OBJS += src/utils/os_$(CONFIG_OS).c
 150 OBJS_p += src/utils/os_$(CONFIG_OS).c
 151 OBJS_c += src/utils/os_$(CONFIG_OS).c

這裡表示如果沒有指定CONFIG_OS巨集,則看下有沒有指定CONFIG_NATIVE_WINDOWS,若指定了那麼CONFIG_OS就是win32,否則是unix。

看到這裡其實對上OS名稱,這裡其實是對作業系統進行區分,另一方面表示wpa_s是支援多作業系統的,比如win32和unix。

本文分析的主要還是Android系統,Android系統在如下config檔案有定義。

vim +248 ./wpa_supplicant/android.config

244 # Select wrapper for operating system and C library specific functions
245 # unix = UNIX/POSIX like systems (default)
246 # win32 = Windows systems
247 # none = Empty template
248 CONFIG_OS=unix

眾所周知,Android底層是基於Linux,屬於unix。

這裡再回頭看下os_program_init()這個方法自然是呼叫的os_unix.c中的方法。

int os_program_init(void)
{
#ifdef ANDROID
	struct __user_cap_header_struct header;
	struct __user_cap_data_struct cap;
	struct group *grp = getgrnam("wifi");
	gid_t gid_wifi = grp ? grp->gr_gid : 0;
	struct passwd *pwd = getpwnam("wifi");
	uid_t uid_wifi = pwd ? pwd->pw_uid : 0;

	/*
	 * We ignore errors here since errors are normal if we
	 * are already running as non-root.
	 */
#ifdef ANDROID_SETGROUPS_OVERRIDE
	gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };

	if (!gid_wifi || !uid_wifi) return -1;
#else /* ANDROID_SETGROUPS_OVERRIDE */
	gid_t groups[3];

	if (!gid_wifi || !uid_wifi) return -1;
	groups[0] = gid_wifi;

	grp = getgrnam("inet");
	groups[1] = grp ? grp->gr_gid : 0;
	if (!groups[1]) return -1;

	grp = getgrnam("keystore");
	groups[2] = grp ? grp->gr_gid : 0;
	if (!groups[2]) return -1;
#endif /* ANDROID_SETGROUPS_OVERRIDE */

	setgroups(ARRAY_SIZE(groups), groups);

	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);

	setgid(gid_wifi);
	setuid(uid_wifi);

	header.version = _LINUX_CAPABILITY_VERSION;
	header.pid = 0;
	cap.effective = cap.permitted =
		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
	cap.inheritable = 0;
	capset(&header, &cap);
#endif /* ANDROID */

	return 0;
}

如何初始化簡單看起來分為2個部分,group和capset的處理。

1)group(參考https://blog.csdn.net/lina_acm/article/details/51911343)

相關函式:initgroups, getgroup, getgid, setgid

標頭檔案:#include <grp.h>

定義函式:int setgroups(size_t size, const gid_t * list);

函式說明:setgroups()用來將list 陣列中所標明的組加入到目前程序的組設定中. 引數size 為list()的gid_t 數目, 最大值為NGROUP(32)。

返回值:設定成功則返回0, 如有錯誤則返回-1.

錯誤程式碼:
EFAULT:引數list 陣列地址不合法.
EPERM:許可權不足, 必須是root 許可權
EINVAL:引數size 值大於NGROUP(32).

獲取了wifi的gid、inet的gid和keystore的gid,然後設定了一下。

2)capset(參考https://blog.csdn.net/aa838260772/article/details/39584667)

capset 設定了程序許可權,獲取net相關許可權?

總體說來是許可權相關的設定。

 

1.2 wpa_supplicant中的log

	os_memset(&params, 0, sizeof(params));
	params.wpa_debug_level = MSG_INFO;

這裡定義了wpa_s的初始化log等級為MSG_INFO.

	/**
	 * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO)
	 */
	int wpa_debug_level;

在 src/utils/wpa_debug.h 中聲明瞭所有等級

/* Debugging function - conditional printf and hex dump. Driver wrappers can
 * use these for debugging purposes. */

enum {
	MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
};

簡單看下這個等級的作用,隨便開啟一個wpa_s中帶log的檔案

wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");

該log列印方法是在src/utils/wpa_debug.c中定義的

void wpa_printf(int level, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	if (level >= wpa_debug_level) {
#ifdef CONFIG_ANDROID_LOG
		__android_log_vprint(wpa_to_android_level(level),
				     ANDROID_LOG_NAME, fmt, ap);
#else /* CONFIG_ANDROID_LOG */
#ifdef CONFIG_DEBUG_SYSLOG
		if (wpa_debug_syslog) {
			vsyslog(syslog_priority(level), fmt, ap);
		} else {
#endif /* CONFIG_DEBUG_SYSLOG */
		wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
		if (out_file) {
			vfprintf(out_file, fmt, ap);
			fprintf(out_file, "\n");
		} else {
#endif /* CONFIG_DEBUG_FILE */
		vprintf(fmt, ap);
		printf("\n");
#ifdef CONFIG_DEBUG_FILE
		}
#endif /* CONFIG_DEBUG_FILE */
#ifdef CONFIG_DEBUG_SYSLOG
		}
#endif /* CONFIG_DEBUG_SYSLOG */
#endif /* CONFIG_ANDROID_LOG */
	}
	va_end(ap);

#ifdef CONFIG_DEBUG_LINUX_TRACING
	if (wpa_debug_tracing_file != NULL) {
		va_start(ap, fmt);
		fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
		vfprintf(wpa_debug_tracing_file, fmt, ap);
		fprintf(wpa_debug_tracing_file, "\n");
		fflush(wpa_debug_tracing_file);
		va_end(ap);
	}
#endif /* CONFIG_DEBUG_LINUX_TRACING */
}

簡單說來就是和Android一樣,MSG_INFO, MSG_WARNING, MSG_ERROR等級的log才能打印出來。

 

1.3 wpa_supplicant_fd_workaround

static void wpa_supplicant_fd_workaround(int start)
{
#ifdef __linux__
	static int fd[3] = { -1, -1, -1 };
	int i;
	/* When started from pcmcia-cs scripts, wpa_supplicant might start with
	 * fd 0, 1, and 2 closed. This will cause some issues because many
	 * places in wpa_supplicant are still printing out to stdout. As a
	 * workaround, make sure that fd's 0, 1, and 2 are not used for other
	 * sockets. */
	if (start) {
		for (i = 0; i < 3; i++) {
			fd[i] = open("/dev/null", O_RDWR);
			if (fd[i] > 2) {
				close(fd[i]);
				fd[i] = -1;
				break;
			}
		}
	} else {
		for (i = 0; i < 3; i++) {
			if (fd[i] >= 0) {
				close(fd[i]);
				fd[i] = -1;
			}
		}
	}
#endif /* __linux__ */
}

參考http://www.it610.com/article/4927804.htm

如果使用了pcmcia-cs scripts來啟動wpa_supplicant,這個時候fd 0,1,2是關閉的。(注意: 習慣上,標準輸入fd為0,標準輸出為1,標準錯誤為2) 
由於在wpa_supplicant中會使用stdout來輸出,如果不管0,1,2三個檔案描述符可能會出現一些錯誤。 
所以,就有了該函式。 

下面來分析一下該函式: 
如果不使用pcmcia-cs scripts,那麼第一次呼叫fd[i] = open("/dev/null", O_RDWR)時,fd[i]取值就是3(開啟應用程式時,會預設啟動0,1,2三個檔案),第二次就是4... 
後面緊跟了一個判斷,如果fd[i]大於2,就close(fd[i])。整個函式相當與什麼都沒有做。 
如果使用了pcmcia-cs scripts,依據上面的註釋,三次呼叫fd[i] = open("/dev/null", O_RDWR)時,fd[i]取值就是0,1,2;即相當於,將標準輸入、標準輸出、標準錯誤重定向到/dev/null中。避免了其他socket使用0,1,2這三個檔案描述符而導致的程式出錯。 

說的很明白了=-=

 

1.4 wpa_supplicant_init

/**
 * 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_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
#endif /* CONFIG_NO_WPA_MSG */

	if (params->wpa_debug_file_path)
		wpa_debug_open_file(params->wpa_debug_file_path);
	else
		wpa_debug_setup_stdout();
	if (params->wpa_debug_syslog)
		wpa_debug_open_syslog();
	if (params->wpa_debug_tracing) {
		ret = wpa_debug_open_linux_tracing();
		if (ret) {
			wpa_printf(MSG_ERROR,
				   "Failed to enable trace logging");
			return NULL;
		}
	}

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

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

	if (wpas_notify_supplicant_initialized(global)) {
		wpa_supplicant_deinit(global);
		return NULL;
	}

	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 */

	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
			       wpas_periodic, global, NULL);

	return global;
}

將之前從配置中讀取的引數賦值初始化。

 

1.5 wpa_supplicant_add_iface

/**
 * wpa_supplicant_add_iface - Add a new network interface
 * @global: Pointer to global data from wpa_supplicant_init()
 * @iface: Interface configuration options
 * @parent: Parent interface or %NULL to assign new interface as parent
 * Returns: Pointer to the created interface or %NULL on failure
 *
 * This function is used to add new network interfaces for %wpa_supplicant.
 * This can be called before wpa_supplicant_run() to add interfaces before the
 * main event loop has been started. In addition, new interfaces can be added
 * dynamically while %wpa_supplicant is already running. This could happen,
 * e.g., when a hotplug network adapter is inserted.
 */
struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
						 struct wpa_interface *iface,
						 struct wpa_supplicant *parent)
{
	struct wpa_supplicant *wpa_s;
	struct wpa_interface t_iface;
	struct wpa_ssid *ssid;

	if (global == NULL || iface == NULL)
		return NULL;

	wpa_s = wpa_supplicant_alloc(parent);
	if (wpa_s == NULL)
		return NULL;

	wpa_s->global = global;

	t_iface = *iface;
	if (global->params.override_driver) {
		wpa_printf(MSG_DEBUG, "Override interface parameter: driver "
			   "('%s' -> '%s')",
			   iface->driver, global->params.override_driver);
		t_iface.driver = global->params.override_driver;
	}
	if (global->params.override_ctrl_interface) {
		wpa_printf(MSG_DEBUG, "Override interface parameter: "
			   "ctrl_interface ('%s' -> '%s')",
			   iface->ctrl_interface,
			   global->params.override_ctrl_interface);
		t_iface.ctrl_interface =
			global->params.override_ctrl_interface;
	}
	if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
		wpa_printf(MSG_DEBUG, "Failed to add interface %s",
			   iface->ifname);
		wpa_supplicant_deinit_iface(wpa_s, 0, 0);
		return NULL;
	}

	/* Notify the control interfaces about new iface */
	if (wpas_notify_iface_added(wpa_s)) {
		wpa_supplicant_deinit_iface(wpa_s, 1, 0);
		return NULL;
	}

	/* Notify the control interfaces about new networks for non p2p mgmt
	 * ifaces. */
	if (iface->p2p_mgmt == 0) {
		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
			wpas_notify_network_added(wpa_s, ssid);
	}

	wpa_s->next = global->ifaces;
	global->ifaces = wpa_s;

	wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);

#ifdef CONFIG_P2P
	if (wpa_s->global->p2p == NULL &&
	    !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
	    wpas_p2p_add_p2pdev_interface(
		    wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
		wpa_printf(MSG_INFO,
			   "P2P: Failed to enable P2P Device interface");
		/* Try to continue without. P2P will be disabled. */
	}
#endif /* CONFIG_P2P */

	return wpa_s;
}

從之前可以看到一共有兩個介面,p2p和wifi

vim +699 ./device/google/marlin/init.common.rc

service wpa_supplicant /vendor/bin/hw/wpa_supplicant \
    -ip2p0 -Dnl80211 -c/data/misc/wifi/p2p_supplicant.conf \
    -I/vendor/etc/wifi/p2p_supplicant_overlay.conf -N \
    -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \
    -I/vendor/etc/wifi/wpa_supplicant_overlay.conf \
    -O/data/misc/wifi/sockets -puse_p2p_group_interface=1 \
    -e/data/misc/wifi/entropy.bin [email protected]:wpa_wlan0
#   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
--------------------- 
作者:i加加 
來源:CSDN 
原文:https://blog.csdn.net/sinat_20059415/article/details/83449860 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

 

1.6 wpa_supplicant_run

/**
 * wpa_supplicant_run - Run the %wpa_supplicant main event loop
 * @global: Pointer to global data from wpa_supplicant_init()
 * Returns: 0 after successful event loop run, -1 on failure
 *
 * This function starts the main event loop and continues running as long as
 * there are any remaining events. In most cases, this function is running as
 * long as the %wpa_supplicant process in still in use.
 */
int wpa_supplicant_run(struct wpa_global *global)
{
	struct wpa_supplicant *wpa_s;

	if (global->params.daemonize &&
	    (wpa_supplicant_daemon(global->params.pid_file) ||
	     eloop_sock_requeue()))
		return -1;

#ifdef CONFIG_MATCH_IFACE
	if (wpa_supplicant_match_existing(global))
		return -1;
#endif

	if (global->params.wait_for_monitor) {
		for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
			if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
				wpa_supplicant_ctrl_iface_wait(
					wpa_s->ctrl_iface);
	}

	eloop_register_signal_terminate(wpa_supplicant_terminate, global);
	eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);

	eloop_run();

	return 0;
}

 

有很多debug的log,後續自己編譯下改下log等級再梳理吧,待續。。。