最近看到了一个大神的博客,结合自己学习 wpa_supplicant 的体验,有了一些感悟。

  1. 阅读源码,特别是一整份极大的源码时,要多去揣测作者的编码心态和编码习惯;
  2. 初期走读的时候,不要去尝试做到面面俱到,每个函数,每个变量都指望知道是用来干什么的,知道个大概即可;
  3. 重点是去理解穿插其中的一些机制,比如 eloop,比如 radio_work队列;

ok, 总结完毕。

这一期,打算从一个具体的动作 assoc 入手,去看 wpa_supplicant 与 driver 的交互,简单点说,assoc 这个动作怎么调起的driver

wpa_supplicant_associate

void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,struct wpa_bss *bss, struct wpa_ssid *ssid)
{struct wpa_connect_work *cwork;int rand_style;wpa_s->own_disconnect_req = 0;wpa_s->own_reconnect_req = 0;/** If we are starting a new connection, any previously pending EAPOL* RX cannot be valid anymore.*/wpabuf_free(wpa_s->pending_eapol_rx);wpa_s->pending_eapol_rx = NULL;if (ssid->mac_addr == -1)rand_style = wpa_s->conf->mac_addr;elserand_style = ssid->mac_addr;wpa_s->multi_ap_ie = 0;wmm_ac_clear_saved_tspecs(wpa_s);wpa_s->reassoc_same_bss = 0;wpa_s->reassoc_same_ess = 0;if (wpa_s->last_ssid == ssid) {wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");wpa_s->reassoc_same_ess = 1;if (wpa_s->current_bss && wpa_s->current_bss == bss) {wmm_ac_save_tspecs(wpa_s);wpa_s->reassoc_same_bss = 1;} else if (wpa_s->current_bss && wpa_s->current_bss != bss) {os_get_reltime(&wpa_s->roam_start);}} else {wpa_s_clear_sae_rejected(wpa_s);wpa_s_setup_sae_pt(wpa_s->conf, ssid);}if (rand_style > 0 && !wpa_s->reassoc_same_ess) {if (wpas_update_random_addr(wpa_s, rand_style) < 0)return;wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);} else if (rand_style == 0 && wpa_s->mac_addr_changed) {if (wpas_restore_permanent_mac_addr(wpa_s) < 0)return;}wpa_s->last_ssid = ssid;if (ssid->mode == WPAS_MODE_IBSS &&!(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {wpa_msg(wpa_s, MSG_INFO,"IBSS RSN not supported in the build");return;}if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP ""mode");return;}if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)wpas_p2p_ap_setup_failed(wpa_s);return;}wpa_s->current_bss = bss;return;}if (ssid->mode == WPAS_MODE_MESH) {wpa_msg(wpa_s, MSG_ERROR,"mesh mode support not included in the build");return;}/** Set WPA state machine configuration to match the selected network now* so that the information is available before wpas_start_assoc_cb()* gets called. This is needed at least for RSN pre-authentication where* candidate APs are added to a list based on scan result processing* before completion of the first association.*/wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);#ifdef CONFIG_DPPif (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)return;
#endif /* CONFIG_DPP */#ifdef CONFIG_TDLSif (bss)wpa_tdls_ap_ies(wpa_s->wpa, wpa_bss_ie_ptr(bss), bss->ie_len);
#endif /* CONFIG_TDLS */#ifdef CONFIG_MBOwpas_mbo_check_pmf(wpa_s, bss, ssid);
#endif /* CONFIG_MBO */if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&ssid->mode == WPAS_MODE_INFRA) {sme_authenticate(wpa_s, bss, ssid);return;}if (wpa_s->connect_work) {wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");return;}if (radio_work_pending(wpa_s, "connect")) {wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");return;}wpas_abort_ongoing_scan(wpa_s);cwork = os_zalloc(sizeof(*cwork));if (cwork == NULL)return;cwork->bss = bss;cwork->ssid = ssid;if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,wpas_start_assoc_cb, cwork) < 0) {os_free(cwork);}
}

wpa_supplicant_associate 需要传入两个参数 wpa_bss, wpa_ssid
可以看到 wpa_supplicant_associate 的最终操作就是把 connect 动作塞进 radio_work 队列

radio_work队列

一个wpa_supplicant实体拥有一个wpa_radio变量,wpa_radio内含一个wpa_radio_work list,表示正在排队的需要用到此radio口的动作
supplicant想要进行一些动作,都会将 动作(字符串形式)和实际动作(注册的回调函数)使用 radio_add_work 方法注入当前radio的radio_work队列,按序执行(当然,也可以插队)
注:笔者看的源码里,radio口支持同时跑两个动作,即 MAX_ACTIVE_WORKS == 2

回到上文,当 radio_work 队列空闲(轮到add的 connect 动作时),wpas_start_assoc_cb 就会被调用

wpas_start_assoc_cb

简单说下做的一些关键动作

1. struct wpa_driver_associate_params params;
2. wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
3. 填充 params
4. ret = wpa_drv_associate(wpa_s, &params);
5. wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
6. wpa_supplicant_initiate_eapol(wpa_s);

5.6步目前还不确定走不走,这个函数 囊括了 assoc、auth、EAPOL动作

我们只关系 ret = wpa_drv_associate(wpa_s, &params); 这一步,继续看

static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,struct wpa_driver_associate_params *params)
{if (wpa_s->driver->associate) {return wpa_s->driver->associate(wpa_s->drv_priv, params);}return -1;
}

可以看到这里就是调用 wpa_driver_ops 中的 associate,这里的 wpa_driver_ops 当然是 wpa_driver_nl80211_ops 啦

static int wpa_driver_nl80211_associate(void *priv, struct wpa_driver_associate_params *params)
{struct i802_bss *bss = priv;struct wpa_driver_nl80211_data *drv = bss->drv;int ret = -1;struct nl_msg *msg;nl80211_unmask_11b_rates(bss);if (params->mode == IEEE80211_MODE_AP)return wpa_driver_nl80211_ap(drv, params);if (params->mode == IEEE80211_MODE_IBSS)return wpa_driver_nl80211_ibss(drv, params);if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {enum nl80211_iftype nlmode = params->p2p ?NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)return -1;if (params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)bss->use_nl_connect = 1;elsebss->use_nl_connect = 0;return wpa_driver_nl80211_connect(drv, params,get_connect_handle(bss));}nl80211_mark_disconnected(drv);wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",drv->ifindex);//先创建一个基础的 msgmsg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);if (!msg)return -1;//将 parmas 参数塞进 msgret = nl80211_connect_common(drv, params, msg);if (ret)goto fail;if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))goto fail;if (params->fils_kek) {wpa_printf(MSG_DEBUG, "  * FILS KEK (len=%u)",(unsigned int) params->fils_kek_len);if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,params->fils_kek))goto fail;}if (params->fils_nonces) {wpa_hexdump(MSG_DEBUG, "  * FILS nonces (for AAD)",params->fils_nonces,params->fils_nonces_len);if (nla_put(msg, NL80211_ATTR_FILS_NONCES,params->fils_nonces_len, params->fils_nonces))goto fail;}//将 msg 发出ret = send_and_recv_msgs_owner(drv, msg,get_connect_handle(drv->first_bss), 1,NULL, NULL, NULL, NULL);msg = NULL;if (ret) {wpa_dbg(drv->ctx, MSG_DEBUG,"nl80211: MLME command failed (assoc): ret=%d (%s)",ret, strerror(-ret));nl80211_dump_scan(drv);} else {wpa_printf(MSG_DEBUG,"nl80211: Association request send successfully");}fail:nlmsg_free(msg);return ret;
}

凭借之前对 netlink 的一些了解看,supplicant执行动作时是按照 netlink 指定的消息格式(即什么字段填充什么内容,比如nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid) 就可以看出 NL80211_ATTR_MAC 应填充 params->bssid),然后通过 send_and_recv_msgs_owner 发出消息,且很快可以得到执行结果

继续看 kernel/net/wireless

以 kernel-4.19 为例,看 /kernel-4.19/net/wireless/nl80211.c 中 static const struct genl_ops nl80211_ops[] 可知

{            .cmd = NL80211_CMD_ASSOCIATE,.doit = nl80211_associate,.policy = nl80211_policy,.flags = GENL_UNS_ADMIN_PERM,.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |NL80211_FLAG_NEED_RTNL |NL80211_FLAG_CLEAR_SKB,
},

NL80211_CMD_ASSOCIATE 对应处理函数为 nl80211_associate

(坏了,越看越觉得有点不对劲,,先写到这里,缓缓)

【wpa_supplicant】从 assoc 动作窥伺supplicant与driver的交互(一)相关推荐

  1. V4L2用户空间和kernel层driver的交互过程

    这篇文章详细分析了V4L2用户空间和kernel层driver的交互过程,目的只有一个: 更清晰的理解V4L2视频驱动程序的系统结构,驱动编程方法,为以后开发视频驱动打好基础 既然从用户层出发探究驱动 ...

  2. Android WiFi从app到driver详解

    分三大部分: (1)    Wifi模块相关文件的解析 (2)    Wpa_supplicant解析 (3)    Wifi的启动流程(有代码供参考分析) 一,Wifi模块相关文件解析 1)     ...

  3. wpa_supplicant与kernel的接口

    2019独角兽企业重金招聘Python工程师标准>>> 1. 接口定义实现wpa_drivers wpa_drivers的定义如下: [cpp] view plaincopy str ...

  4. wpa_supplicant 源码分析 --conf 配置文件

    原文:wpa_supplicant源码分析--conf配置文件 | Winddoing's Notes 解析 wpa_supplicant 的配置文件,一般叫做 wpa_supplicant.conf ...

  5. wpa_supplicant软件架构分析

    wpa_supplicant软件架构分析 1. 启动命令 wpa supplicant 在启动时,启动命令可以带有很多参数,目前我们的启动命令如下: wpa_supplicant /system/bi ...

  6. WiFi Direct 在wpa_supplicant中的流程一(初始化P2P)

    P2P在wpa_supplicant中的初始化流程 WIFI-Driect在WPA_S中的初始化流程: 注册action wpas_p2p_init() 代码段1 wpas_p2p_init() 代码 ...

  7. Selenium3 Python WebDriver API源码探析(10):动作链(ActionChains):鼠标事件和键盘事件

    鼠标.键盘事件是我们利用Selenium操控浏览器的重要交互手段,主要由selenium\webdriver\common\action_chains.py中的ActionChains类实现.该类通过 ...

  8. 中继器 删除行_Axure9实操教程9-中继器动作(二)

    难得连续两天都有时间来更新,嘿嘿,我不是个勤劳的人,如果白天没时间,不要指望我晚上健完身会在家里奋笔疾书,哈哈哈,因为肥宅已经累趴下不想起来了-- 添加行 在中继器中加入数据.我们事先已经在中继器左侧 ...

  9. 深化学习(RL)概念应用以及基于表格型、神经网络型、策略梯度、连续动作空间求解RL​​​​​​​

    深化学习(RL)概念应用以及基于表格型.神经网络型.策略梯度.连续动作空间求解RL 目录 一.概念以及应用 二.基于表格型求解RL--Sarsa和learning 表格型方法--Sarsa 1. Sa ...

最新文章

  1. 这组动画完美演绎了一个程序员从接手新项目到交货的复杂心情
  2. 如何清除Git中的本地工作目录? [重复]
  3. Notes-stringr-part2
  4. php定义变量f= e怎么写,php变量怎么定义-PHP问题
  5. Android使用AudioRecord录制pcm音频原始数据以及使用AudioTrack播放
  6. 【译】BINDER - ANALYSIS AND EXPLOITATION OF CVE-2020-0041
  7. 林绪虹:看好QoE、音视频内容理解与AV1
  8. UVa 11044 - Searching for Nessy
  9. 大数据之-Hadoop源码编译_源码编译的意义---大数据之hadoop工作笔记0044
  10. 亚马逊发布新版MXNet:支持英伟达Volta和稀疏张量
  11. 基于深度学习的2D和3D仿射变换配准
  12. matlab 里%s,matlab中fprintf函数的用法详解-PHP问题
  13. 马士兵Python基础版2020教程P98-P134 PPT笔记+课堂代码
  14. 如何在vmware workstation 8下成功安装fedora 14
  15. 自动将视频文件生成字幕的软件autosub安装及使用(支持英文、日语和法语)
  16. oracle中insert语句怎,Oracle中Insert语句的总结
  17. 吃字母------线程同步与互斥的学习
  18. 新版androd studio怎样连接第三方模拟器
  19. Maven项目依赖外部jar进行打包的两种方式
  20. 简单编程---哥德巴赫猜想

热门文章

  1. AndroidKiller之APK 编译失败,无法继续下一步签名【BUG解决】【App反向解析】
  2. Java在Word中插入上标和下标
  3. 霍尼236主机说明书_你好 你有 霍尼韦尔Honeywell 236 PLUS 报警主机说明书吗  我想要谢谢你 发给我好吗...
  4. Altium designer PCB走线包地处理 教程
  5. C语言股票交易软件,股票交易系统源程序代码
  6. ​cmd中如何退出Python​
  7. 区块链钱包开发的前景
  8. day4_shop程序
  9. SIM800C的二次开发(EAT开发)
  10. 2021年阜阳一中高考成绩查询,2020年阜阳高考状元名单资料,阜阳今年高考状元是谁...