hostapd wpa_supplicant madwifi详细分析(三)——hostapd_global_init()函数

原文链接: hostapd wpa_supplicant madwifi详细分析(三)——hostapd_global_init()函数_奔跑的路的博客-CSDN博客

一、预备知识(eap_sm、eap_method结构体

struct eap_sm {   //状态机,存储eap的状态
	enum {   //枚举eap的各种状态
		EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
		EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
		EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
		EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
		EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
		EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
		EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
		EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
		EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
	} EAP_state;
 
	/* Constants */
	int MaxRetrans;   //最大重传次数,eap支持超时重发机制.eap_sm在初始化时赋值为5
 
	struct eap_eapol_interface eap_if;//主要放些直接与消息相关的,如req及resp的数据,当前是req还是resp,是否到了重传的时机(retransWhile)等
 
	/* Full authenticator state machine local variables */
 
	/* Long-term (maintained between packets) */
	EapType currentMethod; //当前采用的Method,初始为EAP_TYPE_NONE,其后根据响应中的type定或自选
	int currentId; //当前eap id,开始设为-1,作为backend_AAA时被设为响应消息eapid,需要发送eapreq的时候设为nextId
	enum {
		METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
	} methodState;
	int retransCount;//传送次数
	struct wpabuf *lastReqData;//记下已经发出的请求数据,如需要重传时需要发此数据
	int methodTimeout;
 
	/* Short-term (not maintained between packets) */
	Boolean rxResp; //收到消息的id为resp时设置rxResp为TRUE
	int respId;//收到的resp消息的id
	EapType respMethod;
	int respVendor;
	u32 respVendorMethod;
	Boolean ignore;
	enum {
		DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
		DECISION_PASSTHROUGH
	} decision;
 
	/* Miscellaneous variables */
	const struct eap_method *m; /* selected EAP method,当前选定的eap method */ 
	/* not defined in RFC 4137 */
	Boolean changed;//状态机是否改变,在不变时则退出状态机运行,后续可能要发送eapreq,eapsuccess或eapfailure,或在pending时不做事情
	void *eapol_ctx, *msg_ctx;//eapol_ctx:上下文信息,在状态机初始化时指向session,之后不动;msg_ctx:尚未使用
	struct eapol_callbacks *eapol_cb;//状态机初始化时设置eapol_cb。struct eapol_callbacks为多个需要用到的回调函数如get_eap_user等组成的结构体
	void *eap_method_priv;//由各个eap method定义的数据,在EAP_INITIALIZE是sm->eap_method_priv = sm->m->initPickUp(sm)   指向eap_identity_data
                                    //在具体EAP method阶段则是具体eap_xxx_data
	u8 *identity;     //在eap_identity_process内赋值,取自eap-resp/identity
	size_t identity_len;
	/* Whether Phase 2 method should validate identity match */
	int require_identity_match; //EAP-GTC用到
	int lastId; /* Identifier used in the last EAP-Packet */
	struct eap_user *user;
	int user_eap_method_index;
	int init_phase2;  //eap_ttls_phase2_eap_init  eap_peap_phase2_init两个函数内设置为1
	void *ssl_ctx;  //在状态机初始化eap_server_sm_init内设置为一个全局的g_ssl_context上下文。后者通过g_ssl_context = tls_init(NULL)实现初始化
	struct eap_sim_db_data *eap_sim_db_priv;//指向系统配置的eap_sim/aka的配置信息,为eap_sim_db_data结构。主要含有与hlr的通信套接字信息,假名表,
                                               //重鉴权用户信息,pending的用户查询等
	Boolean backend_auth;       //是否作为backend authentication server
	Boolean update_user;      //sm->identity是否更新了的标志,如为true时可能需要重新获取用户信息
	int eap_server;            //是作为eapserver还是passthrough
 
	int num_rounds;            //eap交互次数,最大允许EAP_MAX_AUTH_ROUNDS=50次
	enum {
		METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
	} method_pending;
        /*状态机初始化时method_pending为METHOD_PENDING_NONE,因业务需要,可以将method_pending设置为METHOD_PENDING_WAIT。
          eap状态机在处理EAP_PROPOSE_METHOD或EAP_METHOD_RESPONSE时,如果为WAIT则什么不做,退出状态机。
         如果为CONT则设置method_pending = METHOD_PENDING_NONE并继续执行EAP_METHOD_RESPONSE状态。
         eap具体method业务在收到响应等需要的时候调用eap_sm_pending_cb,他会设置method_pending为CONT,这样再激活状态机他会继续执行*/
 
        u8 *auth_challenge;
	u8 *peer_challenge; //均是eap-mschapv2鉴权过程中的参数,分别由server和peer生成的随机数
 
	u8 *pac_opaque_encr_key;
	u8 *eap_fast_a_id;
	size_t eap_fast_a_id_len;
	char *eap_fast_a_id_info;
	enum {
		NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
	} eap_fast_prov;
	int pac_key_lifetime;
	int pac_key_refresh_time;
	int eap_sim_aka_result_ind;
	int tnc;  //以上均取自配置文件,eap server用不着这些,可以到配置文件中查看配置的这些变量的值
	u16 pwd_group;
	struct wps_context *wps;
	struct wpabuf *assoc_wps_ie;
	struct wpabuf *assoc_p2p_ie;
 
	Boolean start_reauth;
 
	u8 peer_addr[ETH_ALEN];
 
	/* Fragmentation size for EAP method init() handler */
	int fragment_size;
 
	int pbc_in_m1;
 
	const u8 *server_id;
	size_t server_id_len;
 
#ifdef CONFIG_TESTING_OPTIONS
	u32 tls_test_flags;
#endif /* CONFIG_TESTING_OPTIONS */
};
 
 

struct eap_method {                //这个结构体用来存放每种加密方法的各种操作函数和变量
    int vendor;          //存放eap vender ID
    EapType method;      //EapType是一个枚举类型,里面的值定义了method type
    const char *name;    //存放method的名字,比如PSK
 
    void * (*init)(struct eap_sm *sm);   //初始化eap method
    void * (*initPickUp)(struct eap_sm *sm);
    void (*reset)(struct eap_sm *sm, void *priv);
 
    struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id);  //处理一个eap request 请求包
    int (*getTimeout)(struct eap_sm *sm, void *priv);
    Boolean (*check)(struct eap_sm *sm, void *priv, struct wpabuf *respData);
    void (*process)(struct eap_sm *sm, void *priv,struct wpabuf *respData);
    Boolean (*isDone)(struct eap_sm *sm, void *priv);
    u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);  //从eap method中获取秘钥内容
 
    Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
 
   
    void (*free)(struct eap_method *method);  //释放eap method 数据
 
#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
 
    int version;   //peer端EAP interface版本
 
  
    struct eap_method *next;  // 用于建立链表,指向下一个节点
 
    u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);//获取扩展的秘钥内容
};

上面两个结构体封装了很多参数和方法,显得尤其重要,接下来,我们进入hostapd_global_init()函数。

<span style="color:#000000;">static int hostapd_global_init(struct hapd_interfaces *interfaces,const char *entropy_file)
{
	os_memset(&global, 0, sizeof(global));//重置global变量
	hostapd_logger_register_cb(hostapd_logger_cb);
	
        if (eap_server_register_methods()) {      //注册eap server的加密方法
		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
		return -1;
	}
 
	if (eloop_init()) {                   //
		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
		return -1;
	}
 
	random_init(entropy_file);
 
#ifndef CONFIG_NATIVE_WINDOWS
	eloop_register_signal(SIGHUP, handle_reload, interfaces);
	eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
#endif /* CONFIG_NATIVE_WINDOWS */
	eloop_register_signal_terminate(handle_term, interfaces);
 
	for (i = 0; wpa_drivers[i]; i++)
		global.drv_count++;
	if (global.drv_count == 0) {
		wpa_printf(MSG_ERROR, "No drivers enabled");
		return -1;
	}
	global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
	if (global.drv_priv == NULL)
		return -1;
 
	return 0;
}</span>

1. 使用eap_server_register_methods函数注册eap server支持的安全模式,并存放在一个链表里面,下图是支持的安全模式。

int eap_server_register_methods(void)
{
	int ret = 0;
 
#ifdef EAP_SERVER_IDENTITY
	if (ret == 0)
		ret = eap_server_identity_register();
#endif /* EAP_SERVER_IDENTITY */
 
#ifdef EAP_SERVER_MD5
	if (ret == 0)
		ret = eap_server_md5_register();
#endif /* EAP_SERVER_MD5 */
 
#ifdef EAP_SERVER_TLS
	if (ret == 0)
		ret = eap_server_tls_register();
.......

 根据宏开关来确认哪些安全模式是支持的,并调用相应协议的注册函数,注册一个加密安全模式放在struct eap_method链表中,因为这些安全模式注册函数都差不多,所以只介绍其中一种模式eap_server_psk_register()。

int eap_server_psk_register(void)
{
	struct eap_method *eap;
	int ret;
 
	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
				      EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
	if (eap == NULL)
		return -1;
 
	eap->init = eap_psk_init;
	eap->reset = eap_psk_reset;
	eap->buildReq = eap_psk_buildReq;
	eap->check = eap_psk_check;
	eap->process = eap_psk_process;
	eap->isDone = eap_psk_isDone;
	eap->getKey = eap_psk_getKey;
	eap->isSuccess = eap_psk_isSuccess;
	eap->get_emsk = eap_psk_get_emsk;
 
	ret = eap_server_method_register(eap);
	if (ret)
		eap_server_method_free(eap);
	return ret;
}

首先定义一个struct eap_method 对象,用eap_server_method_alloc给这个对象申请一块空间,然后给这个对象根据不同的安全模式指向不同的操作函数,最后将这个eap对象通过eap_server_method_register函数添加到struct eap_method 结构体对象的链表里面。

2. 使用eloop_init()对struct eloop_data eloop对象进行初始化,至于struct eloop_data的作用将在后面介绍。
 

int eloop_init(void)
{
	os_memset(&eloop, 0, sizeof(eloop));
	dl_list_init(&eloop.timeout);
#ifdef CONFIG_ELOOP_EPOLL
	eloop.epollfd = epoll_create1(0);
	if (eloop.epollfd < 0) {
		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
			   __func__, strerror(errno));
		return -1;
	}
	eloop.readers.type = EVENT_TYPE_READ;
	eloop.writers.type = EVENT_TYPE_WRITE;
	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef WPA_TRACE
	signal(SIGSEGV, eloop_sigsegv_handler);
#endif /* WPA_TRACE */
	return 0;

这个函数主要是重置eloop对象和初始化链表,然后对eloop成员的一些赋值等

3. 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) {
#ifndef CONFIG_NO_STDOUT_DEBUG
		int error = errno;
		perror("open(/dev/random)");
		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
			   strerror(error));
#endif /* CONFIG_NO_STDOUT_DEBUG */
		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();
}

这里面eloop_register_read_sock很重要,具体的需要用源代码去深入跟踪。

4. 最后是中断的注册和global对像的赋值等操作。

这篇主要对初始化过程进行了介绍,功能的具体实现将在后面篇幅中讲述。


————————————————
版权声明:本文为CSDN博主「奔跑的路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lee244868149/article/details/42028811

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值