1 /*
2 * This is a stripped down version of the iw tool designed for
3 * programmatically checking driver/hw capabilities.
4 *
5 * Copyright 2007, 2008 Johannes Berg <johannes@sipsolutions.net>
6 */
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <net/if.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <stdbool.h>
17
18 #include <netlink/netlink.h>
19 #include <netlink/genl/genl.h>
20 #include <netlink/genl/family.h>
21 #include <netlink/genl/ctrl.h>
22 #include <netlink/msg.h>
23 #include <netlink/attr.h>
24
25 #include "nl80211.h"
26
27 #ifndef CONFIG_LIBNL20
28 /* libnl 2.0 compatibility code */
29
30 # define nl_sock nl_handle
31
nl_socket_alloc(void)32 static inline struct nl_handle *nl_socket_alloc(void)
33 {
34 return nl_handle_alloc();
35 }
36
nl_socket_free(struct nl_sock * h)37 static inline void nl_socket_free(struct nl_sock *h)
38 {
39 nl_handle_destroy(h);
40 }
41
__genl_ctrl_alloc_cache(struct nl_sock * h,struct nl_cache ** cache)42 static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache)
43 {
44 struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
45 if (!tmp)
46 return -ENOMEM;
47 *cache = tmp;
48 return 0;
49 }
50 #define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
51 #endif /* CONFIG_LIBNL20 */
52
53 struct nl80211_state {
54 struct nl_sock *nl_sock;
55 struct nl_cache *nl_cache;
56 struct genl_family *nl80211;
57 };
58
nl80211_init(struct nl80211_state * state)59 static int nl80211_init(struct nl80211_state *state)
60 {
61 int err;
62
63 state->nl_sock = nl_socket_alloc();
64 if (!state->nl_sock) {
65 fprintf(stderr, "Failed to allocate netlink socket.\n");
66 return -ENOMEM;
67 }
68
69 if (genl_connect(state->nl_sock)) {
70 fprintf(stderr, "Failed to connect to generic netlink.\n");
71 err = -ENOLINK;
72 goto out_handle_destroy;
73 }
74
75 if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) {
76 fprintf(stderr, "Failed to allocate generic netlink cache.\n");
77 err = -ENOMEM;
78 goto out_handle_destroy;
79 }
80
81 state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
82 if (!state->nl80211) {
83 fprintf(stderr, "nl80211 not found.\n");
84 err = -ENOENT;
85 goto out_cache_free;
86 }
87
88 return 0;
89
90 out_cache_free:
91 nl_cache_free(state->nl_cache);
92 out_handle_destroy:
93 nl_socket_free(state->nl_sock);
94 return err;
95 }
96
nl80211_cleanup(struct nl80211_state * state)97 static void nl80211_cleanup(struct nl80211_state *state)
98 {
99 genl_family_put(state->nl80211);
100 nl_cache_free(state->nl_cache);
101 nl_socket_free(state->nl_sock);
102 }
103
104 static const char *argv0;
105
phy_lookup(char * name)106 static int phy_lookup(char *name)
107 {
108 char buf[200];
109 int fd, pos;
110
111 snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
112
113 fd = open(buf, O_RDONLY);
114 if (fd < 0)
115 return -1;
116 pos = read(fd, buf, sizeof(buf) - 1);
117 if (pos < 0)
118 return -1;
119 buf[pos] = '\0';
120 return atoi(buf);
121 }
122
123 enum {
124 CHECK_IS_HT20 = 0x00000001,
125 CHECK_IS_HT40 = 0x00000002,
126 CHECK_IS_PSMP = 0x00000004,
127 CHECK_IS_AMPDU = 0x00000008,
128 CHECK_IS_AMSDU = 0x00000010,
129 CHECK_IS_SMPS = 0x00000020,
130 CHECK_IS_STA = 0x00000040,
131 CHECK_IS_AP = 0x00000080,
132 CHECK_IS_IBSS = 0x00000100,
133 CHECK_IS_MBSS = 0x00000200,
134 CHECK_IS_MONITOR = 0x00000400,
135 CHECK_BANDS = 0x00000800,
136 CHECK_FREQS = 0x00001000,
137 CHECK_RATES = 0x00002000,
138 CHECK_MCS = 0x00004000,
139 CHECK_AMPDU_DENS = 0x00008000,
140 CHECK_AMPDU_FACT = 0x00010000,
141 CHECK_AMSDU_LEN = 0x00020000,
142 CHECK_IS_LPDC = 0x00040000,
143 CHECK_IS_GREENFIELD = 0x00080000,
144 CHECK_IS_SGI20 = 0x00100000,
145 CHECK_IS_SGI40 = 0x00200000,
146 CHECK_IS_TXSTBC = 0x00400000,
147 CHECK_RXSTBC = 0x00800000,
148 CHECK_IS_DELBA = 0x01000000,
149 /* NB: must be in upper 16-bits to avoid HT caps */
150 CHECK_IS_24GHZ = 0x02000000,
151 CHECK_IS_5GHZ = 0x04000000,
152 CHECK_IS_11B = 0x08000000,
153 CHECK_IS_11G = 0x10000000,
154 CHECK_IS_11A = 0x20000000,
155 CHECK_IS_11N = 0x40000000,
156 };
157
158 struct check {
159 const char *name;
160 int namelen;
161 int bits;
162 };
163 static const struct check checks[] = {
164 { "24ghz", 5, CHECK_IS_24GHZ },
165 { "5ghz", 4, CHECK_IS_5GHZ },
166 { "11b", 3, CHECK_IS_11B },
167 { "11g", 3, CHECK_IS_11G },
168 { "11a", 3, CHECK_IS_11A },
169 { "11n", 3, CHECK_IS_11N },
170 { "ht20", 4, CHECK_IS_HT20 },
171 { "ht40", 4, CHECK_IS_HT40 },
172 { "psmp", 5, CHECK_IS_PSMP },
173 { "ampdu", 5, CHECK_IS_AMPDU },
174 { "amsdu", 5, CHECK_IS_AMSDU },
175 { "smps", 4, CHECK_IS_SMPS },
176 { "sta", 3, CHECK_IS_STA },
177 { "ap", 2, CHECK_IS_AP },
178 { "ibss", 4, CHECK_IS_IBSS },
179 { "mbss", 4, CHECK_IS_MBSS },
180 { "mon", 3, CHECK_IS_MONITOR },
181 { "bands", 4, CHECK_BANDS },
182 { "freqs", 4, CHECK_FREQS },
183 { "rates", 4, CHECK_RATES },
184 { "mcs", 3, CHECK_MCS },
185 { "ampdu_dens", 10, CHECK_AMPDU_DENS },
186 { "ampdu_fact", 10, CHECK_AMPDU_FACT },
187 { "amsdu_len", 9, CHECK_AMSDU_LEN },
188 { "lpdc", 4, CHECK_IS_LPDC },
189 { "green", 5, CHECK_IS_GREENFIELD },
190 { "sgi20", 5, CHECK_IS_SGI20 },
191 { "sgi40", 5, CHECK_IS_SGI40 },
192 { "txstbc", 6, CHECK_IS_TXSTBC },
193 { "rxstbc", 6, CHECK_RXSTBC },
194 { "delba", 5, CHECK_IS_DELBA },
195 { "all", 3, -1 },
196 { NULL }
197 };
198
find_check_byname(const char * name)199 static const struct check *find_check_byname(const char *name)
200 {
201 const struct check *p;
202
203 for (p = checks; p->name != NULL; p++)
204 if (strncasecmp(p->name, name, p->namelen) == 0)
205 return p;
206 return NULL;
207 }
208
209 #if 0
210 static const struct check *find_check_bybits(int bits)
211 {
212 const struct check *p;
213
214 for (p = checks; p->name != NULL; p++)
215 if (p->bits == bits)
216 return p;
217 return NULL;
218 }
219 #endif
220
check_iftype(struct nlattr * tb_msg[],int nl_type)221 static int check_iftype(struct nlattr *tb_msg[], int nl_type)
222 {
223 struct nlattr *nl_mode;
224 int rem_mode;
225
226 if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
227 return 0;
228
229 nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
230 if (nl_mode->nla_type == nl_type)
231 return 1;
232 return 0;
233 }
234
get_max_mcs(unsigned char * mcs)235 static unsigned int get_max_mcs(unsigned char *mcs)
236 {
237 unsigned int mcs_bit, max;
238
239 max = 0;
240 for (mcs_bit = 0; mcs_bit <= 76; mcs_bit++) {
241 unsigned int mcs_octet = mcs_bit/8;
242 unsigned int MCS_RATE_BIT = 1 << mcs_bit % 8;
243 bool mcs_rate_idx_set;
244
245 mcs_rate_idx_set = !!(mcs[mcs_octet] & MCS_RATE_BIT);
246
247 if (!mcs_rate_idx_set)
248 continue;
249
250 if (mcs_bit > max)
251 max = mcs_bit;
252 }
253 return max;
254 }
255
pbool(const char * tag,int v)256 static void pbool(const char *tag, int v)
257 {
258 printf("%s: %s\n", tag, v ? "true" : "false");
259 }
260
pint(const char * tag,int v)261 static void pint(const char *tag, int v)
262 {
263 printf("%s: %d\n", tag, v);
264 }
265
prate(const char * tag,int v)266 static void prate(const char *tag, int v)
267 {
268 printf("%s: %2.1f\n", tag, 0.1*v);
269 }
270
check_phy_handler(struct nl_msg * msg,void * arg)271 static int check_phy_handler(struct nl_msg *msg, void *arg)
272 {
273 struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
274 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
275 uintptr_t checks = (uintptr_t) arg;
276
277 struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
278
279 struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
280 static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
281 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
282 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
283 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
284 [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
285 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
286 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
287 };
288
289 struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
290 static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
291 [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
292 [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
293 };
294
295 struct nlattr *nl_band;
296 struct nlattr *nl_freq;
297 struct nlattr *nl_rate;
298 int rem_band, rem_freq, rem_rate, phy_caps;
299 int amsdu_len, ampdu_fact, ampdu_dens, max_mcs, max_rate;
300
301 nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
302 genlmsg_attrlen(gnlh, 0), NULL);
303
304 if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
305 return NL_SKIP;
306
307 phy_caps = 0;
308 amsdu_len = 0;
309 ampdu_fact = 0;
310 ampdu_dens = 0;
311 max_mcs = 0;
312 max_rate = 0;
313 /* NB: merge each band's findings; this stuff is silly */
314 nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
315 nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
316 nla_len(nl_band), NULL);
317
318 if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
319 unsigned short caps = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
320 int len;
321
322 /* XXX not quite right but close enough */
323 phy_caps |= CHECK_IS_11N | caps;
324 len = 0xeff + ((caps & 0x0800) << 1);
325 if (len > amsdu_len)
326 amsdu_len = len;
327 }
328 if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
329 unsigned char factor = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]);
330 int fact = (1<<(13+factor))-1;
331 if (fact > ampdu_fact)
332 ampdu_fact = fact;
333 }
334 if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
335 unsigned char dens = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]);
336 if (dens > ampdu_dens)
337 ampdu_dens = dens;
338 }
339 if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
340 nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16) {
341 /* As defined in 7.3.2.57.4 Supported MCS Set field */
342 unsigned char *mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
343 int max = get_max_mcs(&mcs[0]);
344 if (max > max_mcs)
345 max_mcs = max;
346 }
347
348 nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
349 uint32_t freq;
350
351 nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
352 nla_data(nl_freq), nla_len(nl_freq),
353 freq_policy);
354 if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
355 continue;
356 #if 0
357 /* NB: we care about device caps, not regulatory */
358 if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
359 continue;
360 #endif
361 freq = nla_get_u32(
362 tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
363 if (checks & CHECK_FREQS)
364 pint("freq", freq);
365
366 /* NB: approximate band boundaries, we get no help */
367 if (2000 <= freq && freq <= 3000)
368 phy_caps |= CHECK_IS_24GHZ;
369 else if (4000 <= freq && freq <= 6000)
370 phy_caps |= CHECK_IS_5GHZ;
371 }
372
373 nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
374 int rate;
375
376 nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
377 nla_len(nl_rate), rate_policy);
378 if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
379 continue;
380 rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
381 if (rate > max_rate)
382 max_rate = rate;
383 }
384 }
385 #if 0
386 /* NB: 11n =>'s legacy support */
387 if (phy_caps & CHECK_IS_11N) {
388 if (phy_caps & CHECK_IS_24GHZ)
389 phy_caps |= CHECK_IS_11B | CHECK_IS_11G;
390 if (phy_caps & CHECK_IS_5GHZ)
391 phy_caps |= CHECK_IS_11A;
392 }
393 #else
394 /* XXX no way to figure this out; just force 'em */
395 if (phy_caps & CHECK_IS_24GHZ)
396 phy_caps |= CHECK_IS_11B | CHECK_IS_11G;
397 if (phy_caps & CHECK_IS_5GHZ)
398 phy_caps |= CHECK_IS_11A;
399 #endif
400
401 #define PBOOL(c, b, name) if (checks & (c)) pbool(name, phy_caps & (b))
402 PBOOL(CHECK_IS_24GHZ, CHECK_IS_24GHZ, "24ghz");
403 PBOOL(CHECK_IS_5GHZ, CHECK_IS_5GHZ, "5ghz");
404 PBOOL(CHECK_IS_11B, CHECK_IS_11B, "11b");
405 PBOOL(CHECK_IS_11G, CHECK_IS_11G, "11g");
406 PBOOL(CHECK_IS_11A, CHECK_IS_11A, "11a");
407 PBOOL(CHECK_IS_11N, CHECK_IS_11N, "11n");
408 PBOOL(CHECK_IS_LPDC, 0x1, "lpdc");
409 PBOOL(CHECK_IS_HT20, CHECK_IS_11N, "ht20");
410 PBOOL(CHECK_IS_HT40, 0x2, "ht40");
411 if (checks & CHECK_IS_SMPS)
412 pbool("smps", ((phy_caps & 0x000c) >> 2) < 2);
413 PBOOL(CHECK_IS_GREENFIELD, 0x10, "green");
414 PBOOL(CHECK_IS_SGI20, 0x20, "sgi20");
415 PBOOL(CHECK_IS_SGI40, 0x40, "sgi40");
416 PBOOL(CHECK_IS_TXSTBC, 0x40, "txstbc");
417 PBOOL(CHECK_RXSTBC, 0x300, "rxstbc");
418 PBOOL(CHECK_IS_DELBA, 0x400, "delba");
419 #if 0
420 PBOOL(CHECK_IS_DSSCCK, 0x1000, "dsscck");
421 #endif
422 PBOOL(CHECK_IS_PSMP, 0x2000, "psmp");
423 #if 0
424 PBOOL(CHECK_IS_INTOL, 0x4000, "intol");
425 #endif
426 #if 0
427 PBOOL(CHECK_IS_LSIGTXOP, 0x8000, "lsigtxop");
428 #endif
429 #undef PBOOL
430 if (checks & CHECK_AMSDU_LEN)
431 pint("amsdu_len", amsdu_len);
432 if (checks & CHECK_AMPDU_FACT)
433 pint("ampdu_fact", ampdu_fact);
434 if (checks & CHECK_AMPDU_DENS)
435 pint("ampdu_dens", ampdu_dens);
436 if (checks & CHECK_RATES)
437 prate("rate", max_rate);
438 if (checks & CHECK_MCS)
439 pint("mcs", max_mcs);
440
441 if (checks & CHECK_IS_STA)
442 pbool("sta", check_iftype(tb_msg, NL80211_IFTYPE_STATION));
443 if (checks & CHECK_IS_IBSS)
444 pbool("ibss", check_iftype(tb_msg, NL80211_IFTYPE_ADHOC));
445 if (checks & CHECK_IS_AP)
446 pbool("ap", check_iftype(tb_msg, NL80211_IFTYPE_AP));
447 if (checks & CHECK_IS_MBSS)
448 pbool("mbss", check_iftype(tb_msg, NL80211_IFTYPE_MESH_POINT));
449 if (checks & CHECK_IS_MONITOR)
450 pbool("mon", check_iftype(tb_msg, NL80211_IFTYPE_MONITOR));
451
452 return NL_SKIP;
453 }
454
check_phy_caps(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv)455 static int check_phy_caps(struct nl80211_state *state,
456 struct nl_cb *cb,
457 struct nl_msg *msg,
458 int argc, char **argv)
459 {
460 int checks = 0;
461 for (; argc > 0; argc--, argv++) {
462 const struct check *p = find_check_byname(argv[0]);
463 if (p == NULL) {
464 fprintf(stderr, "invalid check %s\n", argv[0]);
465 return 3; /* XXX whatever? */
466 }
467 checks |= p->bits;
468 }
469 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, check_phy_handler,
470 (void *)(uintptr_t) checks);
471 return 0;
472 }
473
error_handler(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)474 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
475 void *arg)
476 {
477 int *ret = arg;
478 *ret = err->error;
479 return NL_STOP;
480 }
481
finish_handler(struct nl_msg * msg,void * arg)482 static int finish_handler(struct nl_msg *msg, void *arg)
483 {
484 int *ret = arg;
485 *ret = 0;
486 return NL_SKIP;
487 }
488
ack_handler(struct nl_msg * msg,void * arg)489 static int ack_handler(struct nl_msg *msg, void *arg)
490 {
491 int *ret = arg;
492 *ret = 0;
493 return NL_STOP;
494 }
495
__handle_cmd(struct nl80211_state * state,int argc,char ** argv)496 static int __handle_cmd(struct nl80211_state *state, int argc, char **argv)
497 {
498 struct nl_cb *cb;
499 struct nl_msg *msg;
500 int devidx, err;
501
502 if (argc <= 1)
503 return 1;
504
505 devidx = phy_lookup(*argv);
506 if (devidx < 0)
507 return -errno;
508 argc--, argv++;
509
510 msg = nlmsg_alloc();
511 if (!msg) {
512 fprintf(stderr, "failed to allocate netlink message\n");
513 return 2;
514 }
515
516 cb = nl_cb_alloc(NL_CB_DEFAULT);
517 if (!cb) {
518 fprintf(stderr, "failed to allocate netlink callbacks\n");
519 err = 2;
520 goto out_free_msg;
521 }
522
523 genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
524 0, NL80211_CMD_GET_WIPHY, 0);
525 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
526
527 err = check_phy_caps(state, cb, msg, argc, argv);
528 if (err)
529 goto out;
530
531 err = nl_send_auto_complete(state->nl_sock, msg);
532 if (err < 0)
533 goto out;
534
535 err = 1;
536
537 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
538 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
539 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
540
541 while (err > 0)
542 nl_recvmsgs(state->nl_sock, cb);
543 out:
544 nl_cb_put(cb);
545 out_free_msg:
546 nlmsg_free(msg);
547 return err;
548 nla_put_failure:
549 fprintf(stderr, "building message failed\n");
550 return 2;
551 }
552
main(int argc,char ** argv)553 int main(int argc, char **argv)
554 {
555 struct nl80211_state nlstate;
556 int err;
557
558 argc--;
559 argv0 = *argv++;
560
561 err = nl80211_init(&nlstate);
562 if (err == 0) {
563 if (argc > 1 && strncmp(*argv, "phy", 3) == 0) {
564 err = __handle_cmd(&nlstate, argc, argv);
565 if (err < 0)
566 fprintf(stderr, "command failed: %s (%d)\n",
567 strerror(-err), err);
568 else if (err)
569 fprintf(stderr, "command failed: err %d\n", err);
570 } else {
571 fprintf(stderr, "usage: %s phyX [args]\n", argv0);
572 err = 1;
573 }
574 nl80211_cleanup(&nlstate);
575 }
576 return err;
577 }
578