中国石油销售公司网站建设,深圳赶集同城网站建设,wordpress twenty eleven,wordpress 导入图片1 wpa_supplicant与用户态程序wpa_cli的交互过程
1.1 交互接口类型
wpa_supplicant与用户态程序交互的主要接口包括以下几种#xff1a;
1#xff09;命令行界面#xff1a;通过命令行工具 wpa_cli 可以与 wpa_supplicant 进行交互。wpa_cli 允许用户执行各种 wpa_suppli…1 wpa_supplicant与用户态程序wpa_cli的交互过程
1.1 交互接口类型
wpa_supplicant与用户态程序交互的主要接口包括以下几种
1命令行界面通过命令行工具 wpa_cli 可以与 wpa_supplicant 进行交互。wpa_cli 允许用户执行各种 wpa_supplicant 操作如配置网络、扫描网络、断开连接等。用户可以通过命令行输入命令然后 wpa_cli 会将命令传递给 wpa_supplicant并返回执行结果。2控制接口文件wpa_supplicant 会创建一个控制接口文件通常位于 /var/run/wpa_supplicant/ 目录下以与外部程序进行通信。通过控制接口文件外部程序可以向 wpa_supplicant 发送命令以配置和管理无线网络连接。这通常涉及到读写控制接口文件中的数据以执行各种操作。3D-Bus 接口wpa_supplicant 也提供了一个 D-Bus 接口允许外部程序使用 D-Bus 协议与其通信。通过 D-Bus 接口外部程序可以查询和配置 wpa_supplicant 的状态、网络配置等信息。D-Bus 是一种通用的进程间通信机制在许多Linux系统上都受支持。4自定义接口有些外部程序可能会使用 wpa_supplicant 提供的自定义接口通过编程方式与其交互。
用户态程序和wpa_supplicant两个进程之间通信的方式一般为unix socket
wpa_cli就是一个用户态程序本文以wpa_cli为代表分析wpa_supplicant与用户态之间的交互
1.2 交互命令和日志
首先执行wpa_supplicant命令
sudo ./wpa_supplicant -i wlan0 -D nl80211 -c /etc/wpa_supplicant/wpa_supplicant.conf
该命令会调用wpa_supplicant/main.c文件中的main()主函数
然后执行wpa_cli命令
sudo ./wpa_cli -i wlan0 scan
该命令会调用wpa_supplicant/wpa_cli.c文件中的main()主函数
在wpa_cli端发出扫描命令后wpa_supplicant端接收到来自wpa_cli的消息其处理日志如下
wlan0: Control interface command SCAN
wlan0: Setting scan request: 0.000000 sec
wlan0: Starting AP scan for wildcard SSID
WPS: Building WPS IE for Probe Request
WPS: * Version (hardcoded 0x10)
WPS: * Request Type
WPS: * Config Methods (3108)
WPS: * UUID-E
WPS: * Primary Device Type
WPS: * RF Bands (1)
WPS: * Association State
WPS: * Configuration Error (0)
WPS: * Device Password ID (0)
WPS: * Manufacturer
WPS: * Model Name
WPS: * Model Number
WPS: * Device Name
WPS: * Version2 (0x20)
P2P: * P2P IE header
P2P: * Capability dev25 group00
P2P: * Listen Channel: Regulatory Class 81 Channel 1
wlan0: Add radio work scan0x5558d881f330
wlan0: First radio work item in the queue - schedule start immediately
wlan0: Starting radio work scan0x5558d881f330 after 0.000006 second wait
wlan0: nl80211: scan request
Scan requested (ret0) - scan timeout 30 seconds
nl80211: Drv Event 33 (NL80211_CMD_TRIGGER_SCAN) received for wlan0
wlan0: nl80211: Scan trigger
wlan0: Event SCAN_STARTED (47) received
wlan0: Own scan request started a scan in 0.000000 seconds
RTM_NEWLINK: ifi_index4 ifnamewlan0 wext ifi_family0 ifi_flags0x11043 ([UP][RUNNING][LOWER_UP])
nl80211: Drv Event 34 (NL80211_CMD_NEW_SCAN_RESULTS) received for wlan0
wlan0: nl80211: New scan results available
nl80211: Scan probed for SSID
nl80211: Scan included frequencies: 2412 2417 2422 2427 2432 2437 2442 2447 2452 2457 2462 2467 2472
wlan0: Event SCAN_RESULTS (3) received
wlan0: Scan completed in 11.500205 seconds
nl80211: Received scan results (35 BSSes)
nl80211: Scan results indicate BSS status with 48:2f:6b:2a:07:80 as associated
wlan0: BSS: Start scan result update 3
wlan0: BSS: Add new id 56 BSSID 7c:10:c9:b4:d0:48 SSID ASUS_2G freq 2412
wlan0: BSS: Add new id 57 BSSID 9c:8c:d8:00:a8:e0 SSID i-amlogic freq 2412
wlan0: BSS: Add new id 58 BSSID 9a:00:74:f7:03:b6 SSID ChinaNet-UuxC freq 2412
wlan0: BSS: Add new id 59 BSSID 9c:8c:d8:00:a8:e1 SSID sunshine freq 2412
wlan0: BSS: Add new id 60 BSSID 9c:8c:d8:00:a8:e2 SSID galaxy freq 2412
wlan0: BSS: Add new id 61 BSSID 48:5b:ea:eb:9d:30 SSID ChinaNet-DFrr freq 2432
wlan0: BSS: Add new id 62 BSSID 9c:8c:d8:fe:de:60 SSID i-amlogic freq 2462
BSS: last_scan_res_used35/64
wlan0: New scan results available (own1 ext0)
WPS: AP 48:5b:ea:eb:9d:30 type 0 added
WPS: AP[0] b8:3a:08:17:7f:71 type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[1] 92:a5:af:5e:27:dc type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[2] 58:48:49:0b:b8:63 type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[3] 50:2b:73:c9:11:29 type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[4] 48:5b:ea:eb:a1:2c type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[5] 9c:74:6f:40:a0:40 type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[6] a2:cd:b6:00:c9:b9 type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[7] 14:f5:09:dd:64:f6 type0 tries0 last_attempt-1 sec ago bssid_ignore0
WPS: AP[8] 48:5b:ea:eb:9d:30 type0 tries0 last_attempt-1 sec ago bssid_ignore0
wlan0: Radio work scan0x5558d881f330 done in 11.507001 seconds
wlan0: radio_work_free(scan0x5558d881f330): num_active_works -- 0
wlan0: Scan results matching the currently selected network
wlan0: 6: 48:2f:6b:2a:d9:40 freq2462 level-56 snr33 est_throughput65000
wlan0: 9: 48:2f:6b:2a:07:80 freq2462 level-58 snr31 est_throughput65000
wlan0: 13: 9c:8c:d8:00:a8:e0 freq2412 level-62 snr27 est_throughput65000
wlan0: 29: 9c:8c:d8:fe:3f:80 freq2462 level-80 snr9 est_throughput19500
wlan0: 31: 9c:8c:d8:fe:de:60 freq2462 level-88 snr1 est_throughput3250
wlan0: Selecting BSS from priority group 0
wlan0: 0: 22:f2:2c:43:84:a1 ssid wpa_ie_len22 rsn_ie_len20 caps0x431 level-54 freq2462
wlan0: skip - SSID not known
wlan0: 1: 18:f2:2c:43:84:a1 ssidTV-SE wpa_ie_len22 rsn_ie_len20 caps0x1431 level-56 freq2462
wlan0: skip - SSID mismatch
wlan0: 2: f6:84:8d:21:4c:3b ssid wpa_ie_len22 rsn_ie_len20 caps0x411 level-67 freq2462
wlan0: skip - SSID not known
wlan0: 3: f4:84:8d:21:4c:3b ssidQA_2.4G wpa_ie_len22 rsn_ie_len20 caps0x1411 level-68 freq2462
wlan0: skip - SSID mismatch
wlan0: 4: 7c:10:c9:b4:d0:48 ssidASUS_2G wpa_ie_len0 rsn_ie_len20 caps0x1411 level-69 freq2412
wlan0: skip - SSID mismatch
wlan0: 5: 48:2f:6b:2a:d9:41 ssidsunshine wpa_ie_len0 rsn_ie_len20 caps0x431 level-56 freq2462
wlan0: skip - SSID mismatch
wlan0: 6: 48:2f:6b:2a:d9:40 ssidi-amlogic wpa_ie_len0 rsn_ie_len20 caps0x431 level-56 freq2462
wlan0: selected based on RSN IE
wlan0: selected BSS 48:2f:6b:2a:d9:40 ssidi-amlogic
wlan0: Considering within-ESS reassociation
wlan0: Current BSS: 48:2f:6b:2a:07:80 freq2462 level-58 snr31 est_throughput65000
wlan0: Selected BSS: 48:2f:6b:2a:d9:40 freq2462 level-56 snr33 est_throughput65000
wlan0: Using signal poll values for the current BSS: level-59 snr30 est_throughput65000
wlan0: Skip roam - Current BSS has good SNR (30 25)
wlan0: BSS: Remove id 26 BSSID 6c:b1:58:e4:97:0d SSID TP-LINK_970D due to wpa_bss_flush_by_age
wlan0: BSS: Remove id 31 BSSID b8:3a:08:17:7f:71 SSID Moonflower due to wpa_bss_flush_by_age
wlan0: BSS: Remove id 41 BSSID 48:2f:6b:2a:37:80 SSID i-amlogic due to wpa_bss_flush_by_age
wlan0: BSS: Remove id 48 BSSID 9c:54:c2:fb:66:30 SSID cyem due to wpa_bss_flush_by_age
wlan0: BSS: Remove id 52 BSSID 14:f5:09:dd:64:f6 SSID due to wpa_bss_flush_by_age
1.3 wpa_cli的main函数
wpa_cli的main函数依次调用了以下子函数 1调用wpa_cli_open_global_ctrl()函数用于打开global接口因为本文执行命令时没有指定-g参数所以该函数实际没有起到作用 2wpa_cli_open_connection()函数用于打开socket连接修改全局变量ctrl_conn的值传入该函数的参数即为命令中-i指定的waln0 3wpa_request()函数用于向wpa_supplicant发起请求命令传入该函数的参数即为全局接口ctrl_conn和命令中的scan字符
//wpa_supplicant\wpa_cli.c
int main(int argc, char *argv[])
{int c;int daemonize 0;int ret 0;
if (os_program_init())return -1;
for (;;) {c getopt(argc, argv, a:Bg:G:hi:p:P:rs:v);if (c 0)break;switch (c) {case i:os_free(ctrl_ifname);ctrl_ifname os_strdup(optarg);break;}
if (eloop_init())return -1;
if (wpa_cli_open_global_ctrl() 0)return -1;
eloop_register_signal_terminate(wpa_cli_terminate, NULL);
if (wpa_cli_open_connection(ctrl_ifname, 0) 0) {fprintf(stderr, Failed to connect to non-global ctrl_ifname: %s error: %s\n,ctrl_ifname ? ctrl_ifname : (nil),strerror(errno));return -1;}ret wpa_request(ctrl_conn, argc - optind,argv[optind]);
os_free(ctrl_ifname);eloop_destroy();wpa_cli_cleanup();
return ret;
}
1.3.1 wpa_cli_open_global_ctrl函数
wpa_cli_open_global_ctrl()函数没有输入参数返回参数为整型变量返回0表示成功返回-1表示失败wpa_cli_open_global_ctrl()函数中继续调用了wpa_ctrl_open2()函数该函数用于打开wpa_supplicant的控制接口
wpa_ctrl_open2()函数返回一个指向控制接口数据的指针wpa_ctrl该函数有2个输入参数如下 第1个输入参数为wpa_supplicant控制接口的unix套接字路径实际传入的是NULL 第2个输入参数为客户端wpa_cli的unix套接字路径实际传入的是NULL
函数结构如下
//wpa_supplicant\wpa_cli.c
static struct wpa_ctrl *ctrl_conn;
static const char *global NULL;//wpa_supplicant\wpa_cli.c
static int wpa_cli_open_global_ctrl(void)
{ctrl_conn wpa_ctrl_open(global);return 0;
}//src\common\wpa_ctrl.c
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{return wpa_ctrl_open2(ctrl_path, NULL);
}//src\common\wpa_ctrl.c
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path)
{struct wpa_ctrl *ctrl;if (ctrl_path NULL)return NULL;
}
因为在命令中没有指定-g参数所以全局变量global参数默认为NULL
因此传递给函数wpa_ctrl_open2()的参数ctrl_path为NULL所以实际上该函数返回为NULL全局变量ctrl_conn也就被设置为NULL
1.3.2 wpa_cli_open_connection函数
wpa_cli_open_connection()函数用于打开和wpa_supplicant的连接并在函数中改变全局变量ctrl_conn的值wpa_cli_open_connection()函数返回1个整型变量成功返回0失败返回-1该函数有2个输入参数如下 传入第1个参数接口名称实际传入的为命令中-i指定的wlan 传入第2个参数为指定连接的方式或附加方式实际传入0表示只建立连接不附加到接口
函数结构如下
//wpa_supplicant\wpa_cli.c
#ifndef CONFIG_CTRL_IFACE_DIR
#define CONFIG_CTRL_IFACE_DIR /var/run/wpa_supplicant
#endif /* CONFIG_CTRL_IFACE_DIR */
static const char *ctrl_iface_dir CONFIG_CTRL_IFACE_DIR;
static const char *client_socket_dir NULL;
static struct wpa_ctrl *ctrl_conn;//wpa_supplicant\wpa_cli.c
static int wpa_cli_open_connection(const char *ifname, int attach)
{char *cfile NULL;int flen, res;if (ifname NULL)return -1;if (cfile NULL) {flen os_strlen(ctrl_iface_dir) os_strlen(ifname) 2;cfile os_malloc(flen);if (cfile NULL)return -1;res os_snprintf(cfile, flen, %s/%s, ctrl_iface_dir,ifname);if (os_snprintf_error(flen, res)) {os_free(cfile);return -1;}}ctrl_conn wpa_ctrl_open2(cfile, client_socket_dir);if (ctrl_conn NULL) {os_free(cfile);return -1;}os_free(cfile);return 0;
}
该函数仍然调用了wpa_ctrl_open2()函数并将返回的控制接口指针wpa_ctrl赋值给全局变量ctrl_conn
ctrl_conn为全局变量类型为结构体指针wpa_ctrl
//src\common\wpa_ctrl.c
struct wpa_ctrl {int s;struct sockaddr_un local;struct sockaddr_un dest;
};
wpa_ctrl的接口类型实际有3种分别为udp、unix、pipe本文只分析unix
wpa_cli_open_connection()函数调用wpa_ctrl_open2()函数时与之前的wpa_cli_open_global_ctrl()函数不一样
wpa_ctrl_open2()函数返回1个结构体指针ctrl该函数有2个输入参数如下 传入的第1个参数已经是接口的名称/var/run/wpa_supplicant/wlan0而不是NULL了 传入的第2个参数是client_socket_dir其初始值依然是NULL
此时的wpa_ctrl_open2函数结构如下
//src\common\wpa_ctrl.c
#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
#define CONFIG_CTRL_IFACE_CLIENT_DIR /tmp
#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
#define CONFIG_CTRL_IFACE_CLIENT_PREFIX wpa_ctrl_
#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX *///src\common\wpa_ctrl.c
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path)
{struct wpa_ctrl *ctrl;static int counter 0;int ret;size_t res;int tries 0;int flags;if (ctrl_path NULL)return NULL;ctrl os_zalloc(sizeof(*ctrl));if (ctrl NULL)return NULL;ctrl-s socket(PF_UNIX, SOCK_DGRAM, 0);if (ctrl-s 0) {os_free(ctrl);return NULL;}ctrl-local.sun_family AF_UNIX;counter;
try_again:ret os_snprintf(ctrl-local.sun_path,sizeof(ctrl-local.sun_path),CONFIG_CTRL_IFACE_CLIENT_DIR /CONFIG_CTRL_IFACE_CLIENT_PREFIX %d-%d,(int) getpid(), counter);if (os_snprintf_error(sizeof(ctrl-local.sun_path), ret)) {close(ctrl-s);os_free(ctrl);return NULL;}tries;if (bind(ctrl-s, (struct sockaddr *) ctrl-local,sizeof(ctrl-local)) 0) {if (errno EADDRINUSE tries 2) {unlink(ctrl-local.sun_path);goto try_again;}close(ctrl-s);os_free(ctrl);return NULL;}ctrl-dest.sun_family AF_UNIX;res os_strlcpy(ctrl-dest.sun_path, ctrl_path, sizeof(ctrl-dest.sun_path));if (res sizeof(ctrl-dest.sun_path)) {close(ctrl-s);os_free(ctrl);return NULL;}if (connect(ctrl-s, (struct sockaddr *) ctrl-dest,sizeof(ctrl-dest)) 0) {close(ctrl-s);unlink(ctrl-local.sun_path);os_free(ctrl);return NULL;}flags fcntl(ctrl-s, F_GETFL);if (flags 0) {flags | O_NONBLOCK;if (fcntl(ctrl-s, F_SETFL, flags) 0) {perror(fcntl(ctrl-s, O_NONBLOCK));}}return ctrl;
}
对该函数说明如下 调用socket()为控制接口创建套接字ctrl-s地址族为PF_UNIX表示本地unix域套接字类型为SOCK_DGRAM表示无连接通信协议为0表示自动选择 创建本地套接字的地址族ctrl-local.sun_family为AF_UNIX 创建本地套接字的地址例如创建路径为/tmp/wpa_ctrl_13152-1其命名方式为/tmp/wpa_ctrl_进程pid-尝试次数 调用bind()将创建的套接字与本地地址绑定 创建目标套接字的地址族ctrl-dest.sun_family为AF_UNIX 创建目标套接字的地址例如创建路径为/var/run/wpa_supplicant/wlan0其命名方式为/var/run/wpa_supplicant/接口名称 调用connect()将本地socket与目标地址连接 通过一系列socket函数实现wpa_cli与wpa_supplicant进行通信如果出错则会调用close()关闭socket连接 最后通过fcntl()获取套接字ctrl-s的标志位并将套接字设置为非阻塞模式以避免在目标程序意外终止时导致程序永远阻塞
整个过程中设置了控制接口的ctrl-s、ctrl-local、ctrl→dest这3个成员的值
//src\common\wpa_ctrl.c
struct wpa_ctrl {int s; //文件描述符struct sockaddr_un local; //本地UNIX域套接字的地址信息struct sockaddr_un dest; //目标UNIX域套接字的地址信息
};
相关定义如下
//winsock.h
#define AF_UNIX 1
#define PF_UNIX AF_UNIX//sys/un.h
struct sockaddr_un {sa_family_t sun_family; // 地址族通常设置为 AF_UNIXchar sun_path[UNIX_PATH_MAX]; // 套接字文件的路径
};
代码运行后返回的结构体指针wpa_ctrl的相关值如下
ctrl-s: 3
ctrl-local.sun_family: 1
ctrl-local.sun_path: /tmp/wpa_ctrl_13152-1
ctrl-dest.sun_family: 1
ctrl-dest.sun_path: /var/run/wpa_supplicant/wlan0
socket()函数返回的文件描述符从0开始分配其中 0表示标准输入stdin 1 表示标准输出stdout 2 表示标准错误输出stderr 3 表示一个新的文件描述符不与标准输入、输出或错误输出重叠 如果返回值为-1表明创建socket套接字失败
最终全局变量ctrl_conn的值也就被修改为ctrl
1.3.3 wpa_request函数
在main()函数的最后调用了wpa_request()函数发送命令
ret wpa_request(ctrl_conn, argc - optind, argv[optind]);
argc表示参数的个数sudo ./wpa_cli -i wlan0 scan 这条命令的参数共有4个除sudo所以argc为4
optind表示解析命令行参数的状态初始值为1每处理一个参数-i、wlan0、scan均为参数optind的值加1当解析完所有参数时optind的值为3
argv[optind]表示最后一个参数的地址即字符串scan
wpa_request()函数返回1个整型变量成功返回0失败返回-1该函数有3个输入参数如下 第1个输入参数为结构体指针ctrl实际传递为全局变量ctrl_conn的值 第2个输入参数为待处理的参数个数argc实际传递为argc - optind即为4 - 3 1 第3个输入参数为具体的参数数组实际为字符数组内容为scan
该函数结构如下
//wpa_supplicant\wpa_cli.c
struct wpa_cli_cmd {const char *cmd;int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);char ** (*completion)(const char *str, int pos);enum wpa_cli_cmd_flags flags;const char *usage;
};//wpa_supplicant\wpa_cli.c
static const struct wpa_cli_cmd wpa_cli_commands[] {{ scan, wpa_cli_cmd_scan, NULL,cli_cmd_flag_none, request new BSS scan },
}//wpa_supplicant\wpa_cli.c
static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
{const struct wpa_cli_cmd *cmd, *match NULL;int count;int ret 0;ifname_prefix NULL;if (argc 0)return -1;count 0;cmd wpa_cli_commands;while (cmd-cmd) {if (os_strncasecmp(cmd-cmd, argv[0], os_strlen(argv[0])) 0){match cmd;if (os_strcasecmp(cmd-cmd, argv[0]) 0) {/* we have an exact match */count 1;break;}count;}cmd;}if (count 1) {printf(Ambiguous command %s; possible commands:, argv[0]);cmd wpa_cli_commands;while (cmd-cmd) {if (os_strncasecmp(cmd-cmd, argv[0],os_strlen(argv[0])) 0) {printf( %s, cmd-cmd);}cmd;}printf(\n);ret 1;} else if (count 0) {printf(Unknown command %s\n, argv[0]);ret 1;} else {ret match-handler(ctrl, argc - 1, argv[1]);}return ret;
}
该函数将参数scan与已经定义的数组wpa_cli_commands中的命令元素进行完全匹配
while (cmd-cmd) {if (os_strncasecmp(cmd-cmd, argv[0], os_strlen(argv[0])) 0){match cmd;if (os_strcasecmp(cmd-cmd, argv[0]) 0) {count 1;break;}}cmd;
}
匹配到相同的命令后调用该命令对应的句柄函数并将未处理参数个数减1scan后已经没有参数所以此时传递给句柄的值为0
match-handler(ctrl, argc - 1, argv[1]);
scan命令对应的句柄函数为wpa_cli_cmd_scan()之后的调用关系如下 继续调用到wpa_cli_cmd()函数传递cmd参数为SCAN 继续调用到wpa_ctrl_command()函数传递cmd参数为SCAN 继续调用到_wpa_ctrl_command()函数传递cmd参数为SCAN 最终调用到wpa_ctrl_request()函数传递cmd参数为SCAN回调函数为wpa_cli_msg_cb()
主要函数调用如下
//wpa_supplicant\wpa_cli.c
static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
{return wpa_cli_cmd(ctrl, SCAN, 0, argc, argv);
}//wpa_supplicant\wpa_cli.c
static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,int argc, char *argv[])
{char buf[4096];if (write_cmd(buf, sizeof(buf), cmd, argc, argv) 0)return -1;return wpa_ctrl_command(ctrl, buf);
}//wpa_supplicant\wpa_cli.c
static int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
{return _wpa_ctrl_command(ctrl, cmd, 1);
}//wpa_supplicant\wpa_cli.c
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
{char buf[4096];size_t len;int ret;len sizeof(buf) - 1;ret wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, len,wpa_cli_msg_cb);return 0;
}
wpa_ctrl_request()函数返回1个整型变量成功返回0失败返回-1该函数有6个输入参数如下 参数ctrl是socket控制接口 参数cmd是发送给wpa_supplicant的命令 参数cmd_len是命令长度 参数reply是wpa_supplicant对命令的回复 参数reply_len是回复的长度 参数msg_cb是绑定的消息回调函数
该函数结构如下
//src\common\wpa_ctrl.c
int wpa_ctrl_request(struct wpa_ctrl *ctrl,const char *cmd,size_t cmd_len,char *reply,size_t *reply_len,void (*msg_cb)(char *msg, size_t len))
{struct timeval tv;struct os_reltime started_at;int res;fd_set rfds;const char *_cmd;char *cmd_buf NULL;size_t _cmd_len;{_cmd cmd;_cmd_len cmd_len;}errno 0;started_at.sec 0;started_at.usec 0;
retry_send:if (send(ctrl-s, _cmd, _cmd_len, 0) 0) {if (errno EAGAIN || errno EBUSY || errno EWOULDBLOCK){if (started_at.sec 0)os_get_reltime(started_at);else {struct os_reltime n;os_get_reltime(n);if (os_reltime_expired(n, started_at, 5))goto send_err;}os_sleep(1, 0);goto retry_send;}send_err:os_free(cmd_buf);return -1;}os_free(cmd_buf);for (;;) {tv.tv_sec 10;tv.tv_usec 0;FD_ZERO(rfds);FD_SET(ctrl-s, rfds);res select(ctrl-s 1, rfds, NULL, NULL, tv);if (res 0 errno EINTR)continue;if (res 0)return res;if (FD_ISSET(ctrl-s, rfds)) {res recv(ctrl-s, reply, *reply_len, 0);if (res 0)return res;if ((res 0 reply[0] ) ||(res 6 strncmp(reply, IFNAME, 7) 0)) {if (msg_cb) {if ((size_t) res *reply_len)res (*reply_len) - 1;reply[res] \0;msg_cb(reply, res);}continue;}*reply_len res;break;} else {return -2;}}return 0;
}
该函数调用send()发送命令到wpa_supplicant
然后在for循环里调用select()监视使用的socket文件添加到可读文件集合超时时间设置为10s
然后调用recv()接收来自wpa_supplicant的回复
最后如果回调函数存在则调用回调函数msg_cb因回调函数设置为wpa_cli_msg_cb所以实际调用了wpa_cli_msg_cb()函数
//src\common\wpa_ctrl.c
static void wpa_cli_msg_cb(char *msg, size_t len)
{printf(%s\n, msg);
}
调用wpa_cli_msg_cb()函数时传递的mes参数为reply所以该函数的功能是打印wpa_supplicant回复的消息
在终端显示的对命令sudo ./wpa_cli -i wlan0 scan的回复为ok
最后整个程序结束
1.4 wpa_supplicant的main函数
wpa_supplicant程序的入口为wpa_supplicant\main.c下的main()函数
//wpa_supplicant\main.c
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;os_memset(params, 0, sizeof(params));params.wpa_debug_level MSG_INFO;iface ifaces os_zalloc(sizeof(struct wpa_interface));iface_count 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 c:iface-confname optarg;break;case D:iface-driver optarg;break;case i:iface-ifname optarg;break;}}exitcode 0;global wpa_supplicant_init(params);wpa_printf(MSG_INFO, Successfully initialized wpa_supplicant);for (i 0; exitcode 0 i iface_count; i) {struct wpa_supplicant *wpa_s;wpa_s wpa_supplicant_add_iface(global, ifaces[i], NULL);}if (exitcode 0)exitcode wpa_supplicant_run(global);return exitcode;
}
在main()函数中关于调试级别的设置语句为
params.wpa_debug_level MSG_INFO;
将调试级别设置为MSG_DEBUG可以增加调试信息修改如下
params.wpa_debug_level MSG_DEBUG;
在main()函数中主要调用了以下函数 1wpa_supplicant_init 2wpa_supplicant_add_iface 3wpa_supplicant_run
1.4.1 wpa_supplicant_add_iface函数
对关键的wpa_supplicant_add_iface()函数分析如下
//wpa_supplicant\wpa_supplicant.c
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;wpa_s wpa_supplicant_alloc(parent);wpa_s-global global;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;}wpa_s-next global-ifaces;global-ifaces wpa_s;return wpa_s;
}//wpa_supplicant\wpa_supplicant.c
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,const struct wpa_interface *iface)
{wpa_printf(MSG_DEBUG, Initializing interface %s conf %s driver %s ctrl_interface %s bridge %s, iface-ifname,iface-confname ? iface-confname : N/A,iface-driver ? iface-driver : default,iface-ctrl_interface ? iface-ctrl_interface : N/A,iface-bridge_ifname ? iface-bridge_ifname : N/A);wpa_s-ctrl_iface wpa_supplicant_ctrl_iface_init(wpa_s);return 0;
}
接着会跳转到unix的控制接口文件中
//wpa_supplicant\ctrl_iface_unix.c
struct ctrl_iface_priv *wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{struct ctrl_iface_priv *priv;priv os_zalloc(sizeof(*priv));if (priv NULL)return NULL;dl_list_init(priv-ctrl_dst);dl_list_init(priv-msg_queue);priv-wpa_s wpa_s;priv-sock -1;if (wpas_ctrl_iface_open_sock(wpa_s, priv) 0) {os_free(priv);return NULL;}return priv;
}static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,struct ctrl_iface_priv *priv)
{eloop_register_read_sock(priv-sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
}
所以当wpa_supplicant程序接收到socket消息时就执行wpa_supplicant_ctrl_iface_receive()函数
//wpa_supplicant\ctrl_iface_unix.c
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;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;buf os_malloc(CTRL_IFACE_MAX_LEN 1);res recvfrom(sock, buf, CTRL_IFACE_MAX_LEN 1, 0,(struct sockaddr *) from, fromlen);buf[res] \0;reply_buf wpa_supplicant_ctrl_iface_process(wpa_s, buf, reply_len);reply reply_buf;os_memset(buf, 0, res);if (!reply reply_len 1) {reply FAIL\n;reply_len 5;} else if (!reply reply_len 2) {reply OK\n;reply_len 3;}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));}}os_free(reply_buf);os_free(buf);
}
在该函数主要中进行以下处理 调用recvfrom()函数接收来自wpa_cli的命令将接收数据保存在字符指针buf里 调用wpa_supplicant_ctrl_iface_process()函数处理命令返回结果保存在字符指针reply_buf和字符指针reply中 调用sendto()函数向socket发送回复reply
进一步分析wpa_supplicant接收wpa_cli的消息入口为wpa_supplicant_ctrl_iface_process()函数
//wpa_supplicant\ctrl_iface.c
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,char *buf,size_t *resp_len)
{char *reply;const int reply_size 4096;int reply_len;int level wpas_ctrl_cmd_debug_level(buf);wpa_dbg(wpa_s, level, Control interface command %s, buf);reply os_malloc(reply_size);os_memcpy(reply, OK\n, 3);reply_len 3;if (os_strcmp(buf, SCAN) 0) {wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, reply_len);} else if (os_strncmp(buf, SCAN , 5) 0) {wpas_ctrl_scan(wpa_s, buf 5, reply, reply_size, reply_len);}if (reply_len 0) {os_memcpy(reply, FAIL\n, 5);reply_len 5;}*resp_len reply_len;return reply;
}//wpa_supplicant\ctrl_iface.c
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,char *reply, int reply_size, int *reply_len)
{if (!wpa_s-sched_scanning !wpa_s-scanning ((wpa_s-wpa_state WPA_SCANNING) ||(wpa_s-wpa_state WPA_COMPLETED))) {wpa_supplicant_req_scan(wpa_s, 0, 0);} else if (wpa_s-sched_scanning) {wpa_supplicant_req_scan(wpa_s, 0, 0);}
}
最终执行到scan.c文件中的wpa_supplicant_req_scan()函数发起扫描请求
//wpa_supplicant\scan.c
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
{int res;res eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,NULL);wpa_dbg(wpa_s, MSG_DEBUG, Setting scan request: %d.%06d sec, sec, usec);eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}
nl80211向底层驱动发送触发扫描的NL80211_CMD_TRIGGER_SCAN命令
wpa_driver_nl80211_scan()函数最后调用send_and_recv_msgs()函数
在该函数内继续调用send_and_recv()函数在该函数内继续调用libnl库的nl_send_auto_complete()函数、nl_recvmsgs()函数向内核驱动发送和接收消息
其中libnlLinux Netlink库是一个用于处理Linux内核通信机制Netlink的C库
调用到nl80211驱动接收到底层驱动返回给nl80211驱动接口的NL80211_CMD_TRIGGER_SCAN触发扫描驱动事件
nl80211驱动上报给wpa_supplicant的event事件