iw
iw主要用于显示和操作无线设备及其配置,其通过netlink实现用户空间和内核空间的交互。下面来开始分析一下iw的代码,目前的代码版本选择得失iw-3.15。
目录结构
首先看看iw-3.15中所包含的文件:[scott@SDW iw-3.15]$ tree
.
├── Android.mk
├── android-nl.c
├── bitrate.c
├── coalesce.c
├── connect.c
├── CONTRIBUTING
├── COPYING
├── cqm.c
├── event.c
├── genl.c
├── hwsim.c
├── ibss.c
├── ieee80211.h
├── info.c
├── interface.c
├── iw.8
├── iw.c
├── iw.h
├── link.c
├── Makefile
├── mesh.c
├── mpath.c
├── nl80211.h
├── offch.c
├── p2p.c
├── phy.c
├── ps.c
├── README
├── reason.c
├── reg.c
├── roc.c
├── scan.c
├── sections.c
├── station.c
├── status.c
├── survey.c
├── util.c
├── version.sh
└── wowlan.c
0 directories, 39 files
[scott@SDW iw-3.15]$
主框架
下面开始进行代码分析。[iw.c +495]
494
495 int main(int argc, char **argv)
496 {
497 struct nl80211_state nlstate;
498 int err;
499 const struct cmd *cmd = NULL;
500
501 /* calculate command size including padding */
502 cmd_size = abs((long)&__section_set - (long)&__section_get);
503 /* strip off self */
504 argc--;
505 argv0 = *argv++;
506
507 if (argc > 0 && strcmp(*argv, "--debug") == 0) {
508 iw_debug = 1;
509 argc--;
510 argv++;
511 }
512
513 if (argc > 0 && strcmp(*argv, "--version") == 0) {
514 version();
515 return 0;
516 }
517
518 /* need to treat "help" command specially so it works w/o nl80211 */
519 if (argc == 0 || strcmp(*argv, "help") == 0) {
520 usage(argc - 1, argv + 1);
521 return 0;
522 }
523
在这里,先不管497行的nl80211_state结构体。在第502行出现了一个奇怪的几个“变量”:__section_set和__section_get,而这两个变量在当前的代码中并没有搜索到对应的定义,不过将这两个变量的形式进行拆分,会发现有意思的事情了,拆分成“__section”和另一半部。此时,可以找到在iw.h中进行了定义:
[iw.h +103]
103 #define SECTION(_name) \
104 struct cmd __section ## _ ## _name \
105 __attribute__((used)) __attribute__((section("__cmd"))) = { \
106 .name = (#_name), \
107 .hidden = 1, \
108 }
109
110 #define DECLARE_SECTION(_name) \
111 extern struct cmd __section ## _ ## _name;
112
从104行可以知道,__section和_name进行连接,形成了一个新的符号。105行的作用是,将这新的变量(或符号)存放到自定义名称为“__cmd”的段中,用SECTION宏声明的变量,其中的.name域和.hidden域被赋值了,其他的域没有被赋有效值。而什么地方使用了SECTION宏呢?通过搜索代码发现,使用这个宏的地方还不少,但是回归到main函数中,在iw.c的第502行,与我们有关的只是“get”和“set”两个字符串。所以,界定main函数的502行使用的是section.c中的内容:
[section.c]
1 #include "iw.h"
2
3 SECTION(get);
4 SECTION(set);
注意,在这里,先定义的是__section_get,后定义的是__section_set。其内容可以看一下下面命令的输出:
[scott@SDW iw-3.15]$ objdump --section=__cmd -d iw| grep "<__"
000000000061ca20 <__section_help>:
000000000061ca60 <__section_event>:
000000000061caa0 <__section_list>:
……
000000000061dc60 <__cmd_bitrates_handle_bitrates_NL80211_CMD_SET_TX_BITRATE_MASK_CIB_NETDEV_0>:
000000000061dca0 <__section_get>:
000000000061dce0 <__section_set>:
[scott@SDW iw-3.15]$
从这里可以看出,在最终生成的目标文件中,__section_set的位置是大于__section_get的位置的,而它们两个均是cmd类型的结构体变量,所以这两个连续变量的差值,应该就是cmd结构体成员在__cmd段中所占空间的大小了。
为何要将cmd放置在__cmd段中,这个目前还不清楚,不过可以通过对比一般的实现方法,获得一些信息:
将所有的cmd放置到内存中,最简单的一个实现是用数组来保存这些命令,但是,可能就需要比较大的空间了,后续的扩展性就不是很好了。当然了,也可以动态的申请内存,此时,考虑到分层设计,就需要提供存储管理(最简单的也得给一个恰好的空间)、函数注册、调用等一堆方法。若通过新的数据段来实现,对于程序来说,就不需要额外申请内存了,还得承担内存泄漏的风险,另外,也避免了初始化,而直接将__cmd这个段当成保存cmd的静态数组来使用,由于省掉了内存管理的开销,程序的内存占用会更少一些。
当然了,在section.c中定义了__section_get和__section_set两个全局变量,其外部声明却在iw.h文件中:
[iw.h]
174
175 DECLARE_SECTION(set);
176 DECLARE_SECTION(get);
177
DECLARE_SECTION()宏的定义,是在SECTION()定义后完成的,见上文iw.h中的110行。这样,__section_set和__setction_get两个全局变量就可以正常使用了。
回归到main函数中。
从507行到522行,可以看出来,iw首先处理了三个参数:--debug、--version和help,这三个参数的处理比较简单,没什么可以说的,只是要注意一下504和505行对argc和argv的处理。
下面是iw的对netlink的使用部分了。额,netlink是一个比较复杂的用于内核空间和用户空间的通信方法,这个可以在后续进行补充,目前暂时先回归到对iw代码逻辑的理解这条主线上来。
[iw.c +528]
528 if (strcmp(*argv, "dev") == 0 && argc > 1) {
529 argc--;
530 argv++;
531 err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd);
532 } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) {
533 if (strlen(*argv) == 3) {
534 argc--;
535 argv++;
536 err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd);
537 } else if (*(*argv + 3) == '#')
538 err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd);
539 else
540 goto detect;
541 } else if (strcmp(*argv, "wdev") == 0 && argc > 1) {
542 argc--;
543 argv++;
544 err = __handle_cmd(&nlstate, II_WDEV, argc, argv, &cmd);
545 } else {
546 int idx;
547 enum id_input idby = II_NONE;
548 detect:
549 if ((idx = if_nametoindex(argv[0])) != 0)
550 idby = II_NETDEV;
551 else if ((idx = phy_lookup(argv[0])) >= 0)
552 idby = II_PHY_NAME;
553 err = __handle_cmd(&nlstate, idby, argc, argv, &cmd);
554 }
555
528~554行,就是iw进行处理的最上层逻辑的封装。从代码结构上来看,其主要区分成四部分:dev、phy、wdev和其他。这里的“其他”,先不用管,dev一般指的是在802.11无线网卡上创建出来一般的网络设备,这个网络设备能够被ifconfig一类的命令查看到;phy设备么,就是与802.11 Radio紧密相关的设备,一般是一块802.11无线网卡,驱动层就创建出一个与之对应的phy设备;wdev设备,这个还不清楚,但是通过搜索代码发现,这个设备是与p2p有关的,这是一个WiFi Direct规范,但是iw-3.15并没有提供对应的实现,这个功能也不用关心。一般,用到最多的也就是dev和phy两个命令参数。
下面就用个例子来跟踪一下iw的执行过程,选用的这个命令是:
iw dev mon0 set channel 11
当然啦,mon0设备此前通过iw命令给创建出来了。
分析命令发现,这个命令的第一个参数是dev,所以,在main函数中,应该进入531行,作为II_NETDEV方式调用__handle_cmd()。
__handle_cmd()函数比较长,有一些部分在理解上并没有难度,现在就其中比较困难的部分进行说明。
[iw.c]
295
296 static int __handle_cmd(struct nl80211_state *state, enum id_input idby,
297 int argc, char **argv, const struct cmd **cmdout)
298 {
299 const struct cmd *cmd, *match = NULL, *sectcmd;
300 struct nl_cb *cb;
301 struct nl_cb *s_cb;
302 struct nl_msg *msg;
303 signed long long devidx = 0;
304 int err, o_argc;
305 const char *command, *section;
306 char *tmp, **o_argv;
307 enum command_identify_by command_idby = CIB_NONE;
308
309 if (argc <= 1 && idby != II_NONE)
310 return 1;
311
312 o_argc = argc;
313 o_argv = argv;
314
315 switch (idby) {
316 case II_PHY_IDX:
317 command_idby = CIB_PHY;
318 devidx = strtoul(*argv + 4, &tmp, 0);
319 if (*tmp != '\0')
320 return 1;
321 argc--;
322 argv++;
323 break;
……
351
352 section = *argv;
353 argc--;
354 argv++;
355
356 for_each_cmd(sectcmd) {
357 if (sectcmd->parent)
358 continue;
359 /* ok ... bit of a hack for the dupe 'info' section */
360 if (match && sectcmd->idby != command_idby)
361 continue;
362 if (strcmp(sectcmd->name, section) == 0)
363 match = sectcmd;
364 }
365
366 sectcmd = match;
367 match = NULL;
368 if (!sectcmd)
369 return 1;
这段代码,一直到351行的理解都不是很难,唯一需要注意的是,在switch语句中,对argc和argv进行了操作。
执行命令 iw dev mon0 set channel 11,当到达352行的时候,section的值就已经指向了set字符串。
下面就重点看看这个for_each_cmd()宏。这个宏同样定义在iw.c中:
[iw.c]
88
89 #define for_each_cmd(_cmd) \
90 for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \
91 _cmd = (const struct cmd *)((char *)_cmd + cmd_size))
92
93
这里的cmd_size,已经在上面介绍过了。__start___cmd和__stop___cmd是如何来的,在前奏中也能够非常清楚的知道。这个宏,就可以非常方便地遍历在__cmd段中保存的所有_cmd信息,并在代码中得到比较恰当的应用。总之,对于这个宏,清楚了__start___cmd和cmd_size,其他的,都是比较一般的内容了。对于保存在段__cmd中的命令,后面将会给出具体的介绍。
__handle_cmd()的357行说明,sectcmd的parent必须为非空,也就是,sectcmd必须存在一个parent。其第360行说明,其idby必须与command_idby相同,也就是说,对于测试命令,idby必须是II_NETDEV了。第362行的功能,是根据操作名称set来查找匹配的命令。从代码的结构和严谨程度上来看,在执行363行的时候,代码就可以跳出for循环了,因为若后续还有命令匹配362行,但是在下一次循环的第360行就已经跳过了本次循环,即,356~364行的功能是在__cmd段找到第一个满足对于测试命令来说的“set”操作的命令。
第366行将查找到的match进行保存。
[iw.c]
370
371 if (argc > 0) {
372 command = *argv;
373
374 for_each_cmd(cmd) {
375 if (!cmd->handler)
376 continue;
377 if (cmd->parent != sectcmd)
378 continue;
379 /*
380 * ignore mismatch id by, but allow WDEV
381 * in place of NETDEV
382 */
383 if (cmd->idby != command_idby &&
384 !(cmd->idby == CIB_NETDEV &&
385 command_idby == CIB_WDEV))
386 continue;
387 if (strcmp(cmd->name, command))
388 continue;
389 if (argc > 1 && !cmd->args)
390 continue;
391 match = cmd;
392 break;
393 }
394
395 if (match) {
396 argc--;
397 argv++;
398 }
399 }
400
这一段代码的查找结果是父命令是上一次查找的sectcmd,命令标识符与期望相同,并且命令名称(对于当前测试来说,这个名称是channel)是期望的cmd指针。从这个for循环来看,这里的代码就没有上一次循环中的浪费问题了,在找到匹配结果之后,代码就乖乖地break出来了。
[iw.c]
400
401 if (match)
402 cmd = match;
403 else {
404 /* Use the section itself, if possible. */
405 cmd = sectcmd;
406 if (argc && !cmd->args)
407 return 1;
408 if (cmd->idby != command_idby &&
409 !(cmd->idby == CIB_NETDEV && command_idby == CIB_WDEV))
410 return 1;
411 if (!cmd->handler)
412 return 1;
413 }
414
415 if (cmd->selector) {
416 cmd = cmd->selector(argc, argv);
417 if (!cmd)
418 return 1;
419 }
420
421 if (cmdout)
422 *cmdout = cmd;
423
424 if (!cmd->cmd) {
425 argc = o_argc;
426 argv = o_argv;
427 return cmd->handler(state, NULL, NULL, argc, argv, idby);
428 }
429
这部分代码比较简单,不多说明了。
[iw.c]
429
430 msg = nlmsg_alloc();
431 if (!msg) {
432 fprintf(stderr, "failed to allocate netlink message\n");
433 return 2;
434 }
435
436 cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
437 s_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
438 if (!cb || !s_cb) {
439 fprintf(stderr, "failed to allocate netlink callbacks\n");
440 err = 2;
441 goto out_free_msg;
442 }
443
444 genlmsg_put(msg, 0, 0, state->nl80211_id, 0,
445 cmd->nl_msg_flags, cmd->cmd, 0);
446
447 switch (command_idby) {
448 case CIB_PHY:
449 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
450 break;
451 case CIB_NETDEV:
452 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
453 break;
454 case CIB_WDEV:
455 NLA_PUT_U64(msg, NL80211_ATTR_WDEV, devidx);
456 break;
457 default:
458 break;
459 }
460
461 err = cmd->handler(state, cb, msg, argc, argv, idby);
462 if (err)
463 goto out;
464
465 nl_socket_set_cb(state->nl_sock, s_cb);
466
467 err = nl_send_auto_complete(state->nl_sock, msg);
468 if (err < 0)
469 goto out;
470
471 err = 1;
472
473 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
474 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
475 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
476
477 while (err > 0)
478 nl_recvmsgs(state->nl_sock, cb);
479 out:
480 nl_cb_put(cb);
481 out_free_msg:
482 nlmsg_free(msg);
483 return err;
484 nla_put_failure:
485 fprintf(stderr, "building message failed\n");
486 return 2;
487 }
这段代码主体上是在为执行netlink做准备,忽略与netlink有关的部分,重点就剩下461行的handler的执行了。
到目前为止,iw的主体执行框架应该比较清楚了,下面来分析一下handler指针来源和针对示例(iw dev mon0 set channel 11命令)的后续执行部分。
cmd结构体及其相关的操作
cmd结构体,应该来说,是iw的核心部分了,它实现了框架与具体功能的衔接,借助于一堆宏和其实现的技巧,其完美的完成了上面的目标。[iw.h]
40
41 struct cmd {
42 const char *name;
43 const char *args;
44 const char *help;
45 const enum nl80211_commands cmd;
46 int nl_msg_flags;
47 int hidden;
48 const enum command_identify_by idby;
49 /*
50 * The handler should return a negative error code,
51 * zero on success, 1 if the arguments were wrong
52 * and the usage message should and 2 otherwise.
53 */
54 int (*handler)(struct nl80211_state *state,
55 struct nl_cb *cb,
56 struct nl_msg *msg,
57 int argc, char **argv,
58 enum id_input id);
59 const struct cmd *(*selector)(int argc, char **argv);
60 const struct cmd *parent;
61 };
62
63 #define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0]))
64 #define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y))
65
66 #define __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel)\
67 static struct cmd \
68 __cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden\
69 __attribute__((used)) __attribute__((section("__cmd"))) = { \
70 .name = (_name), \
71 .args = (_args), \
72 .cmd = (_nlcmd), \
73 .nl_msg_flags = (_flags), \
74 .hidden = (_hidden), \
75 .idby = (_idby), \
76 .handler = (_handler), \
77 .help = (_help), \
78 .parent = _section, \
79 .selector = (_sel), \
80 }
81 #define __ACMD(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel, _alias)\
82 __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel);\
83 static const struct cmd *_alias = &__cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden
84 #define COMMAND(section, name, args, cmd, flags, idby, handler, help) \
85 __COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, NULL)
86 #define COMMAND_ALIAS(section, name, args, cmd, flags, idby, handler, help, selector, alias)\
87 __ACMD(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, selector, alias)
88 #define HIDDEN(section, name, args, cmd, flags, idby, handler) \
89 __COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 1, idby, handler, NULL, NULL)
90
91 #define TOPLEVEL(_name, _args, _nlcmd, _flags, _idby, _handler, _help) \
92 struct cmd \
93 __section ## _ ## _name \
94 __attribute__((used)) __attribute__((section("__cmd"))) = { \
95 .name = (#_name), \
96 .args = (_args), \
97 .cmd = (_nlcmd), \
98 .nl_msg_flags = (_flags), \
99 .idby = (_idby), \
100 .handler = (_handler), \
101 .help = (_help), \
102 }
103 #define SECTION(_name) \
104 struct cmd __section ## _ ## _name \
105 __attribute__((used)) __attribute__((section("__cmd"))) = { \
106 .name = (#_name), \
107 .hidden = 1, \
108 }
109
110 #define DECLARE_SECTION(_name) \
111 extern struct cmd __section ## _ ## _name;
在这里,不对cmd结构体的成员进行说明,这里重点阐述一下这里面的几个宏。
1. __COMMAND宏
这个宏是在几个宏里面,算是比较基础的一个宏了,它接收并重置了cmd结构体的所有成员。创造出这个宏的目的,不在于对成员进行初始化,请看68行和69行。第68行,它使用##创建一个“比较”唯一的变量。第69行是iw中使用的一个重点编程技巧,used属性没什么可说的,就是让编译器不再提供对应的警告罢了,section属性比较有意思,这一属性的作用是将这个属性修饰的变量存放在指定的段中,当然啦,section属性只对全局变量有意义,对于局部变量,当然没用的啦。
2. __ACMD宏
这个宏没有什么实际含义,调用__COMMAND宏,并创建一个指向对应空间的新的指针。
3. COMMAND宏
这个宏在代码中使用的最多。这个宏没什么特别之处,只是对cmd结构体中的parent进行了一次封装。
对于COMMAND_ALIAS、HIDDEN、TOPLEVEL的理解,也没有什么难点,这里就不再一一说明了。
测试用的命令在phy.c中进行了定义,
[iw.c->phy.c]
90
91 static int handle_freqchan(struct nl_msg *msg, bool chan,
92 int argc, char **argv)
93 {
94 char *end;
95 static const struct {
96 const char *name;
97 unsigned int val;
98 } htmap[] = {
99 { .name = "HT20", .val = NL80211_CHAN_HT20, },
100 { .name = "HT40+", .val = NL80211_CHAN_HT40PLUS, },
101 { .name = "HT40-", .val = NL80211_CHAN_HT40MINUS, },
102 };
103 unsigned int htval = NL80211_CHAN_NO_HT;
104 unsigned int freq;
105 int i;
106
107 if (!argc || argc > 4)
108 return 1;
109
110 if (!*argv[0])
111 return 1;
112 freq = strtoul(argv[0], &end, 10);
113 if (*end)
114 return 1;
115
116 if (chan) {
117 enum nl80211_band band;
118 band = freq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
119 freq = ieee80211_channel_to_frequency(freq, band);
120 }
121
122 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
123
124 if (argc > 2) {
125 return handle_freqs(msg, argc - 1, argv + 1);
126 } else if (argc == 2) {
127 for (i = 0; i < ARRAY_SIZE(htmap); i++) {
128 if (strcasecmp(htmap[i].name, argv[1]) == 0) {
129 htval = htmap[i].val;
130 break;
131 }
132 }
133 if (htval == NL80211_CHAN_NO_HT)
134 return handle_freqs(msg, argc - 1, argv + 1);
135 }
136
137 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, htval);
138
139 return 0;
140 nla_put_failure:
141 return -ENOBUFS;
142 }
143
144 static int handle_freq(struct nl80211_state *state,
145 struct nl_cb *cb, struct nl_msg *msg,
146 int argc, char **argv,
147 enum id_input id)
148 {
149 return handle_freqchan(msg, false, argc, argv);
150 }
151 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]",
152 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
153 "Set frequency/channel the hardware is using, including HT\n"
154 "configuration.");
155 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]\n"
156 "<control freq> [20|40|80|80+80|160] [<center freq 1>] [<center freq 2>]",
157 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
158
159 static int handle_chan(struct nl80211_state *state,
160 struct nl_cb *cb, struct nl_msg *msg,
161 int argc, char **argv,
162 enum id_input id)
163 {
164 return handle_freqchan(msg, true, argc, argv);
165 }
166 COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
167 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
168 COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
169 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
上面的代码是处理修改channel部分的代码,从中可以知道,修改channel也可以通过freq参数实现。在166行和168行使用COMMAND宏定义的这个命令中,这个命令的parent是__section_set,命令的名称叫做channel,后续的字符串是其需要的参数,回调函数是handle_chan,这个回调函数,也就是在__handle_cmd()函数中第461行进行调用使用的。
通过阅读handle_freqchan()函数,不难发现,这个函数只是对nl_msg进行了必要的设置处理,并没有与其他模块存在实质性的沟通,亦即,cmd结构体的hander成员完成的是想nl_msg填充必要的信息,接着由__handle_cmd()完成后续的工作。
至此,iw的执行过程应该比较清楚了。具体内核部分与之匹配的响应,将会在驱动代码的解读中进行详细介绍。