]> git.ipfire.org Git - thirdparty/iw.git/blob - phy.c
phy: Use common freqchan helper for setting the operating channel
[thirdparty/iw.git] / phy.c
1 #include <stdbool.h>
2 #include <errno.h>
3 #include <strings.h>
4 #include <sys/param.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 #include <netlink/genl/genl.h>
9 #include <netlink/genl/family.h>
10 #include <netlink/genl/ctrl.h>
11 #include <netlink/msg.h>
12 #include <netlink/attr.h>
13
14 #include "nl80211.h"
15 #include "iw.h"
16
17 struct channels_ctx {
18 int last_band;
19 bool width_40;
20 bool width_80;
21 bool width_160;
22 };
23
24 static char *dfs_state_name(enum nl80211_dfs_state state)
25 {
26 switch (state) {
27 case NL80211_DFS_USABLE:
28 return "usable";
29 case NL80211_DFS_AVAILABLE:
30 return "available";
31 case NL80211_DFS_UNAVAILABLE:
32 return "unavailable";
33 default:
34 return "unknown";
35 }
36 }
37
38 static int print_channels_handler(struct nl_msg *msg, void *arg)
39 {
40 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
41 struct channels_ctx *ctx = arg;
42 struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
43 struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
44 struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
45 struct nlattr *nl_band;
46 struct nlattr *nl_freq;
47 int rem_band, rem_freq;
48
49 nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
50
51 if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) {
52 nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
53 if (ctx->last_band != nl_band->nla_type) {
54 printf("Band %d:\n", nl_band->nla_type + 1);
55 ctx->width_40 = false;
56 ctx->width_80 = false;
57 ctx->width_160 = false;
58 ctx->last_band = nl_band->nla_type;
59 }
60
61 nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL);
62
63 if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
64 __u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
65
66 if (cap & BIT(1))
67 ctx->width_40 = true;
68 }
69
70 if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
71 __u32 capa;
72
73 ctx->width_80 = true;
74
75 capa = nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
76 switch ((capa >> 2) & 3) {
77 case 2:
78 /* width_80p80 = true; */
79 /* fall through */
80 case 1:
81 ctx->width_160 = true;
82 break;
83 }
84 }
85
86 if (tb_band[NL80211_BAND_ATTR_FREQS]) {
87 nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
88 uint32_t freq;
89
90 nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), NULL);
91
92 if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
93 continue;
94 freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
95 printf("\t* %d MHz [%d] ", freq, ieee80211_frequency_to_channel(freq));
96
97 if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
98 printf("(disabled)\n");
99 continue;
100 }
101 printf("\n");
102
103 if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
104 printf("\t Maximum TX power: %.1f dBm\n", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
105
106 /* If both flags are set assume an new kernel */
107 if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR] && tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) {
108 printf("\t No IR\n");
109 } else if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) {
110 printf("\t Passive scan\n");
111 } else if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]){
112 printf("\t No IBSS\n");
113 }
114
115 if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
116 printf("\t Radar detection\n");
117
118 printf("\t Channel widths:");
119 if (!tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
120 printf(" 20MHz");
121 if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
122 printf(" HT40-");
123 if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
124 printf(" HT40+");
125 if (ctx->width_80 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
126 printf(" VHT80");
127 if (ctx->width_160 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
128 printf(" VHT160");
129 printf("\n");
130
131 if (!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED] && tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
132 enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
133 unsigned long time;
134
135 printf("\t DFS state: %s", dfs_state_name(state));
136 if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) {
137 time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]);
138 printf(" (for %lu sec)", time / 1000);
139 }
140 printf("\n");
141 if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME])
142 printf("\t DFS CAC time: %u ms\n",
143 nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]));
144 }
145 }
146 }
147 }
148 }
149
150 return NL_SKIP;
151 }
152
153 static int handle_channels(struct nl80211_state *state, struct nl_msg *msg,
154 int argc, char **argv, enum id_input id)
155 {
156 static struct channels_ctx ctx = {
157 .last_band = -1,
158 };
159
160 nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
161 nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
162
163 register_handler(print_channels_handler, &ctx);
164
165 return 0;
166 }
167 TOPLEVEL(channels, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_channels, "Show available channels.");
168
169 static int handle_name(struct nl80211_state *state,
170 struct nl_msg *msg,
171 int argc, char **argv,
172 enum id_input id)
173 {
174 if (argc != 1)
175 return 1;
176
177 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);
178
179 return 0;
180 nla_put_failure:
181 return -ENOBUFS;
182 }
183 COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
184 "Rename this wireless device.");
185
186 static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
187 int argc, char **argv,
188 enum id_input id)
189 {
190 struct chandef chandef;
191 int res;
192
193 res = parse_freqchan(&chandef, false, argc, argv, NULL);
194 if (res)
195 return res;
196
197 return put_chandef(msg, &chandef);
198 }
199
200 COMMAND(set, freq,
201 "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
202 "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
203 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
204 "Set frequency/channel the hardware is using, including HT\n"
205 "configuration.");
206 COMMAND(set, freq,
207 "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
208 "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
209 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
210
211 static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
212 int argc, char **argv,
213 enum id_input id)
214 {
215 struct chandef chandef;
216 int res;
217
218 res = parse_freqchan(&chandef, true, argc, argv, NULL);
219 if (res)
220 return res;
221
222 return put_chandef(msg, &chandef);
223 }
224 COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]",
225 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
226 COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]",
227 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
228
229 static int handle_fragmentation(struct nl80211_state *state,
230 struct nl_msg *msg,
231 int argc, char **argv,
232 enum id_input id)
233 {
234 unsigned int frag;
235
236 if (argc != 1)
237 return 1;
238
239 if (strcmp("off", argv[0]) == 0)
240 frag = -1;
241 else {
242 char *end;
243
244 if (!*argv[0])
245 return 1;
246 frag = strtoul(argv[0], &end, 10);
247 if (*end != '\0')
248 return 1;
249 }
250
251 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);
252
253 return 0;
254 nla_put_failure:
255 return -ENOBUFS;
256 }
257 COMMAND(set, frag, "<fragmentation threshold|off>",
258 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
259 "Set fragmentation threshold.");
260
261 static int handle_rts(struct nl80211_state *state,
262 struct nl_msg *msg,
263 int argc, char **argv,
264 enum id_input id)
265 {
266 unsigned int rts;
267
268 if (argc != 1)
269 return 1;
270
271 if (strcmp("off", argv[0]) == 0)
272 rts = -1;
273 else {
274 char *end;
275
276 if (!*argv[0])
277 return 1;
278 rts = strtoul(argv[0], &end, 10);
279 if (*end != '\0')
280 return 1;
281 }
282
283 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);
284
285 return 0;
286 nla_put_failure:
287 return -ENOBUFS;
288 }
289 COMMAND(set, rts, "<rts threshold|off>",
290 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
291 "Set rts threshold.");
292
293 static int handle_retry(struct nl80211_state *state,
294 struct nl_msg *msg,
295 int argc, char **argv, enum id_input id)
296 {
297 unsigned int retry_short = 0, retry_long = 0;
298 bool have_retry_s = false, have_retry_l = false;
299 int i;
300 enum {
301 S_NONE,
302 S_SHORT,
303 S_LONG,
304 } parser_state = S_NONE;
305
306 if (!argc || (argc != 2 && argc != 4))
307 return 1;
308
309 for (i = 0; i < argc; i++) {
310 char *end;
311 unsigned int tmpul;
312
313 if (strcmp(argv[i], "short") == 0) {
314 if (have_retry_s)
315 return 1;
316 parser_state = S_SHORT;
317 have_retry_s = true;
318 } else if (strcmp(argv[i], "long") == 0) {
319 if (have_retry_l)
320 return 1;
321 parser_state = S_LONG;
322 have_retry_l = true;
323 } else {
324 tmpul = strtoul(argv[i], &end, 10);
325 if (*end != '\0')
326 return 1;
327 if (!tmpul || tmpul > 255)
328 return -EINVAL;
329 switch (parser_state) {
330 case S_SHORT:
331 retry_short = tmpul;
332 break;
333 case S_LONG:
334 retry_long = tmpul;
335 break;
336 default:
337 return 1;
338 }
339 }
340 }
341
342 if (!have_retry_s && !have_retry_l)
343 return 1;
344 if (have_retry_s)
345 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short);
346 if (have_retry_l)
347 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long);
348
349 return 0;
350 nla_put_failure:
351 return -ENOBUFS;
352 }
353 COMMAND(set, retry, "[short <limit>] [long <limit>]",
354 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry,
355 "Set retry limit.");
356
357 #ifndef NETNS_RUN_DIR
358 #define NETNS_RUN_DIR "/var/run/netns"
359 #endif
360 static int netns_get_fd(const char *name)
361 {
362 char pathbuf[MAXPATHLEN];
363 const char *path, *ptr;
364
365 path = name;
366 ptr = strchr(name, '/');
367 if (!ptr) {
368 snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
369 NETNS_RUN_DIR, name );
370 path = pathbuf;
371 }
372 return open(path, O_RDONLY);
373 }
374
375 static int handle_netns(struct nl80211_state *state,
376 struct nl_msg *msg,
377 int argc, char **argv,
378 enum id_input id)
379 {
380 char *end;
381 int fd;
382
383 if (argc < 1 || !*argv[0])
384 return 1;
385
386 if (argc == 1) {
387 NLA_PUT_U32(msg, NL80211_ATTR_PID,
388 strtoul(argv[0], &end, 10));
389 if (*end != '\0') {
390 printf("Invalid parameter: pid(%s)\n", argv[0]);
391 return 1;
392 }
393 return 0;
394 }
395
396 if (argc != 2 || strcmp(argv[0], "name"))
397 return 1;
398
399 if ((fd = netns_get_fd(argv[1])) >= 0) {
400 NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd);
401 return 0;
402 } else {
403 printf("Invalid parameter: nsname(%s)\n", argv[0]);
404 }
405
406 return 1;
407
408 nla_put_failure:
409 return -ENOBUFS;
410 }
411 COMMAND(set, netns, "{ <pid> | name <nsname> }",
412 NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
413 "Put this wireless device into a different network namespace:\n"
414 " <pid> - change network namespace by process id\n"
415 " <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n"
416 " or by absolute path (man ip-netns)\n");
417
418 static int handle_coverage(struct nl80211_state *state,
419 struct nl_msg *msg,
420 int argc, char **argv,
421 enum id_input id)
422 {
423 char *end;
424 unsigned int coverage;
425
426 if (argc != 1)
427 return 1;
428
429 if (!*argv[0])
430 return 1;
431 coverage = strtoul(argv[0], &end, 10);
432 if (coverage > 255)
433 return 1;
434
435 if (*end)
436 return 1;
437
438 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
439
440 return 0;
441 nla_put_failure:
442 return -ENOBUFS;
443 }
444 COMMAND(set, coverage, "<coverage class>",
445 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
446 "Set coverage class (1 for every 3 usec of air propagation time).\n"
447 "Valid values: 0 - 255.");
448
449 static int handle_distance(struct nl80211_state *state,
450 struct nl_msg *msg,
451 int argc, char **argv,
452 enum id_input id)
453 {
454 if (argc != 1)
455 return 1;
456
457 if (!*argv[0])
458 return 1;
459
460 if (strcmp("auto", argv[0]) == 0) {
461 NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK);
462 } else {
463 char *end;
464 unsigned int distance, coverage;
465
466 distance = strtoul(argv[0], &end, 10);
467
468 if (*end)
469 return 1;
470
471 /*
472 * Divide double the distance by the speed of light
473 * in m/usec (300) to get round-trip time in microseconds
474 * and then divide the result by three to get coverage class
475 * as specified in IEEE 802.11-2007 table 7-27.
476 * Values are rounded upwards.
477 */
478 coverage = (distance + 449) / 450;
479 if (coverage > 255)
480 return 1;
481
482 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
483 }
484
485 return 0;
486 nla_put_failure:
487 return -ENOBUFS;
488 }
489 COMMAND(set, distance, "<auto|distance>",
490 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
491 "Enable ACK timeout estimation algorithm (dynack) or set appropriate\n"
492 "coverage class for given link distance in meters.\n"
493 "To disable dynack set valid value for coverage class.\n"
494 "Valid values: 0 - 114750");
495
496 static int handle_txpower(struct nl80211_state *state,
497 struct nl_msg *msg,
498 int argc, char **argv,
499 enum id_input id)
500 {
501 enum nl80211_tx_power_setting type;
502 int mbm;
503
504 /* get the required args */
505 if (argc != 1 && argc != 2)
506 return 1;
507
508 if (!strcmp(argv[0], "auto"))
509 type = NL80211_TX_POWER_AUTOMATIC;
510 else if (!strcmp(argv[0], "fixed"))
511 type = NL80211_TX_POWER_FIXED;
512 else if (!strcmp(argv[0], "limit"))
513 type = NL80211_TX_POWER_LIMITED;
514 else {
515 printf("Invalid parameter: %s\n", argv[0]);
516 return 2;
517 }
518
519 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
520
521 if (type != NL80211_TX_POWER_AUTOMATIC) {
522 char *endptr;
523 if (argc != 2) {
524 printf("Missing TX power level argument.\n");
525 return 2;
526 }
527
528 mbm = strtol(argv[1], &endptr, 10);
529 if (*endptr)
530 return 2;
531 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
532 } else if (argc != 1)
533 return 1;
534
535 return 0;
536
537 nla_put_failure:
538 return -ENOBUFS;
539 }
540 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
541 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
542 "Specify transmit power level and setting type.");
543 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
544 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
545 "Specify transmit power level and setting type.");
546
547 static int handle_antenna(struct nl80211_state *state,
548 struct nl_msg *msg,
549 int argc, char **argv,
550 enum id_input id)
551 {
552 char *end;
553 uint32_t tx_ant = 0, rx_ant = 0;
554
555 if (argc == 1 && strcmp(argv[0], "all") == 0) {
556 tx_ant = 0xffffffff;
557 rx_ant = 0xffffffff;
558 } else if (argc == 1) {
559 tx_ant = rx_ant = strtoul(argv[0], &end, 0);
560 if (*end)
561 return 1;
562 }
563 else if (argc == 2) {
564 tx_ant = strtoul(argv[0], &end, 0);
565 if (*end)
566 return 1;
567 rx_ant = strtoul(argv[1], &end, 0);
568 if (*end)
569 return 1;
570 } else
571 return 1;
572
573 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
574 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
575
576 return 0;
577
578 nla_put_failure:
579 return -ENOBUFS;
580 }
581 COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
582 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
583 "Set a bitmap of allowed antennas to use for TX and RX.\n"
584 "The driver may reject antenna configurations it cannot support.");