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

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

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


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


 
 
  1. struct eap_method { //这个结构体用来存放每种加密方法的各种操作函数和变量
  2. int vendor; //存放eap vender ID
  3. EapType method; //EapType是一个枚举类型,里面的值定义了method type
  4. const char *name; //存放method的名字,比如PSK
  5. void * (*init)( struct eap_sm *sm); //初始化eap method
  6. void * (*initPickUp)( struct eap_sm *sm);
  7. void (*reset)( struct eap_sm *sm, void *priv);
  8. struct wpabuf * (*buildReq)( struct eap_sm *sm, void *priv, u8 id); //处理一个eap request 请求包
  9. int (*getTimeout)( struct eap_sm *sm, void *priv);
  10. Boolean (*check)( struct eap_sm *sm, void *priv, struct wpabuf *respData);
  11. void (*process)( struct eap_sm *sm, void *priv, struct wpabuf *respData);
  12. Boolean (*isDone)( struct eap_sm *sm, void *priv);
  13. u8 * (*getKey)( struct eap_sm *sm, void *priv, size_t *len); //从eap method中获取秘钥内容
  14. Boolean (*isSuccess)( struct eap_sm *sm, void *priv);
  15. void (*free)( struct eap_method *method); //释放eap method 数据
  16. #define EAP_SERVER_METHOD_INTERFACE_VERSION 1
  17. int version; //peer端EAP interface版本
  18. struct eap_method *next; // 用于建立链表,指向下一个节点
  19. u8 * (*get_emsk)( struct eap_sm *sm, void *priv, size_t *len); //获取扩展的秘钥内容
  20. };
上面两个结构体封装了很多参数和方法,显得尤其重要,接下来,我们进入hostapd_global_init()函数。


 
 
  1. <span style= "color:#000000;"> static int hostapd_global_init(struct hapd_interfaces *interfaces,const char *entropy_file)
  2. {
  3. os_memset(& global, 0, sizeof( global)); //重置global变量
  4. hostapd_logger_register_cb(hostapd_logger_cb);
  5. if (eap_server_register_methods()) { //注册eap server的加密方法
  6. wpa_printf(MSG_ERROR, "Failed to register EAP methods");
  7. return -1;
  8. }
  9. if (eloop_init()) { //
  10. wpa_printf(MSG_ERROR, "Failed to initialize event loop");
  11. return -1;
  12. }
  13. random_init(entropy_file);
  14. #ifndef CONFIG_NATIVE_WINDOWS
  15. eloop_register_signal(SIGHUP, handle_reload, interfaces);
  16. eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
  17. #endif /* CONFIG_NATIVE_WINDOWS */
  18. eloop_register_signal_terminate(handle_term, interfaces);
  19. for (i = 0; wpa_drivers[i]; i++)
  20. global.drv_count++;
  21. if ( global.drv_count == 0) {
  22. wpa_printf(MSG_ERROR, "No drivers enabled");
  23. return -1;
  24. }
  25. global.drv_priv = os_calloc( global.drv_count, sizeof( void *));
  26. if ( global.drv_priv == NULL)
  27. return -1;
  28. return 0;
  29. }</span>

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



 
 
  1. int eap_server_register_methods(void)
  2. {
  3. int ret = 0;
  4. #ifdef EAP_SERVER_IDENTITY
  5. if (ret == 0)
  6. ret = eap_server_identity_register();
  7. #endif /* EAP_SERVER_IDENTITY */
  8. #ifdef EAP_SERVER_MD5
  9. if (ret == 0)
  10. ret = eap_server_md5_register();
  11. #endif /* EAP_SERVER_MD5 */
  12. #ifdef EAP_SERVER_TLS
  13. if (ret == 0)
  14. ret = eap_server_tls_register();
  15. .......

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


 
 
  1. int eap_server_psk_register(void)
  2. {
  3. struct eap_method *eap;
  4. int ret;
  5. eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
  6. EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
  7. if (eap == NULL)
  8. return -1;
  9. eap->init = eap_psk_init;
  10. eap->reset = eap_psk_reset;
  11. eap->buildReq = eap_psk_buildReq;
  12. eap->check = eap_psk_check;
  13. eap->process = eap_psk_process;
  14. eap->isDone = eap_psk_isDone;
  15. eap->getKey = eap_psk_getKey;
  16. eap->isSuccess = eap_psk_isSuccess;
  17. eap->get_emsk = eap_psk_get_emsk;
  18. ret = eap_server_method_register(eap);
  19. if (ret)
  20. eap_server_method_free(eap);
  21. return ret;
  22. }

首先定义一个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的作用将在后面介绍。


 
 
  1. int eloop_init(void)
  2. {
  3. os_memset(&eloop, 0, sizeof(eloop));
  4. dl_list_init(&eloop.timeout);
  5. #ifdef CONFIG_ELOOP_EPOLL
  6. eloop.epollfd = epoll_create1( 0);
  7. if (eloop.epollfd < 0) {
  8. wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
  9. __func__, strerror(errno));
  10. return -1;
  11. }
  12. eloop.readers.type = EVENT_TYPE_READ;
  13. eloop.writers.type = EVENT_TYPE_WRITE;
  14. eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
  15. #endif /* CONFIG_ELOOP_EPOLL */
  16. #ifdef WPA_TRACE
  17. signal(SIGSEGV, eloop_sigsegv_handler);
  18. #endif /* WPA_TRACE */
  19. return 0;

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


3. random_init()


 
 
  1. void random_init(const char *entropy_file)
  2. {
  3. os_free(random_entropy_file);
  4. if (entropy_file)
  5. random_entropy_file = os_strdup(entropy_file);
  6. else
  7. random_entropy_file = NULL;
  8. random_read_entropy();
  9. #ifdef __linux__
  10. if (random_fd >= 0)
  11. return;
  12. random_fd = open( "/dev/random", O_RDONLY | O_NONBLOCK);
  13. if (random_fd < 0) {
  14. #ifndef CONFIG_NO_STDOUT_DEBUG
  15. int error = errno;
  16. perror( "open(/dev/random)");
  17. wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
  18. strerror(error));
  19. #endif /* CONFIG_NO_STDOUT_DEBUG */
  20. return;
  21. }
  22. wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
  23. "/dev/random");
  24. eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
  25. #endif /* __linux__ */
  26. random_write_entropy();
  27. }
这里面eloop_register_read_sock很重要,具体的需要用源代码去深入跟踪。


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


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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值