]> git.ipfire.org Git - thirdparty/iw.git/blob - phy.c
add a few new commands/events
[thirdparty/iw.git] / phy.c
1 #include <stdbool.h>
2 #include <errno.h>
3 #include <net/if.h>
4 #include <strings.h>
5 #include <sys/param.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8
9 #include <netlink/genl/genl.h>
10 #include <netlink/genl/family.h>
11 #include <netlink/genl/ctrl.h>
12 #include <netlink/msg.h>
13 #include <netlink/attr.h>
14
15 #include "nl80211.h"
16 #include "iw.h"
17
18 static int handle_name(struct nl80211_state *state,
19 struct nl_msg *msg,
20 int argc, char **argv,
21 enum id_input id)
22 {
23 if (argc != 1)
24 return 1;
25
26 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);
27
28 return 0;
29 nla_put_failure:
30 return -ENOBUFS;
31 }
32 COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
33 "Rename this wireless device.");
34
35 static int handle_freqs(struct nl_msg *msg, int argc, char **argv)
36 {
37 static const struct {
38 const char *name;
39 unsigned int val;
40 } bwmap[] = {
41 { .name = "20", .val = NL80211_CHAN_WIDTH_20, },
42 { .name = "40", .val = NL80211_CHAN_WIDTH_40, },
43 { .name = "80", .val = NL80211_CHAN_WIDTH_80, },
44 { .name = "80+80", .val = NL80211_CHAN_WIDTH_80P80, },
45 { .name = "160", .val = NL80211_CHAN_WIDTH_160, },
46 };
47 uint32_t freq;
48 int i, bwval = NL80211_CHAN_WIDTH_20_NOHT;
49 char *end;
50
51 if (argc < 1)
52 return 1;
53
54 for (i = 0; i < ARRAY_SIZE(bwmap); i++) {
55 if (strcasecmp(bwmap[i].name, argv[0]) == 0) {
56 bwval = bwmap[i].val;
57 break;
58 }
59 }
60
61 if (bwval == NL80211_CHAN_WIDTH_20_NOHT)
62 return 1;
63
64 NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, bwval);
65
66 if (argc == 1)
67 return 0;
68
69 /* center freq 1 */
70 if (!*argv[1])
71 return 1;
72 freq = strtoul(argv[1], &end, 10);
73 if (*end)
74 return 1;
75 NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq);
76
77 if (argc == 2)
78 return 0;
79
80 /* center freq 2 */
81 if (!*argv[2])
82 return 1;
83 freq = strtoul(argv[2], &end, 10);
84 if (*end)
85 return 1;
86 NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, freq);
87
88 return 0;
89 nla_put_failure:
90 return -ENOBUFS;
91 }
92
93 static int handle_freqchan(struct nl_msg *msg, bool chan,
94 int argc, char **argv)
95 {
96 char *end;
97 static const struct {
98 const char *name;
99 unsigned int val;
100 } htmap[] = {
101 { .name = "HT20", .val = NL80211_CHAN_HT20, },
102 { .name = "HT40+", .val = NL80211_CHAN_HT40PLUS, },
103 { .name = "HT40-", .val = NL80211_CHAN_HT40MINUS, },
104 };
105 unsigned int htval = NL80211_CHAN_NO_HT;
106 unsigned int freq;
107 int i;
108
109 if (!argc || argc > 4)
110 return 1;
111
112 if (!*argv[0])
113 return 1;
114 freq = strtoul(argv[0], &end, 10);
115 if (*end)
116 return 1;
117
118 if (chan) {
119 enum nl80211_band band;
120 band = freq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
121 freq = ieee80211_channel_to_frequency(freq, band);
122 }
123
124 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
125
126 if (argc > 2) {
127 return handle_freqs(msg, argc - 1, argv + 1);
128 } else if (argc == 2) {
129 for (i = 0; i < ARRAY_SIZE(htmap); i++) {
130 if (strcasecmp(htmap[i].name, argv[1]) == 0) {
131 htval = htmap[i].val;
132 break;
133 }
134 }
135 if (htval == NL80211_CHAN_NO_HT)
136 return handle_freqs(msg, argc - 1, argv + 1);
137 }
138
139 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, htval);
140
141 return 0;
142 nla_put_failure:
143 return -ENOBUFS;
144 }
145
146 static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
147 int argc, char **argv,
148 enum id_input id)
149 {
150 return handle_freqchan(msg, false, argc, argv);
151 }
152 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]",
153 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
154 "Set frequency/channel the hardware is using, including HT\n"
155 "configuration.");
156 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]\n"
157 "<control freq> [20|40|80|80+80|160] [<center freq 1>] [<center freq 2>]",
158 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
159
160 static int handle_chan(struct nl80211_state *state, 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);
170
171 static int handle_fragmentation(struct nl80211_state *state,
172 struct nl_msg *msg,
173 int argc, char **argv,
174 enum id_input id)
175 {
176 unsigned int frag;
177
178 if (argc != 1)
179 return 1;
180
181 if (strcmp("off", argv[0]) == 0)
182 frag = -1;
183 else {
184 char *end;
185
186 if (!*argv[0])
187 return 1;
188 frag = strtoul(argv[0], &end, 10);
189 if (*end != '\0')
190 return 1;
191 }
192
193 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);
194
195 return 0;
196 nla_put_failure:
197 return -ENOBUFS;
198 }
199 COMMAND(set, frag, "<fragmentation threshold|off>",
200 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
201 "Set fragmentation threshold.");
202
203 static int handle_rts(struct nl80211_state *state,
204 struct nl_msg *msg,
205 int argc, char **argv,
206 enum id_input id)
207 {
208 unsigned int rts;
209
210 if (argc != 1)
211 return 1;
212
213 if (strcmp("off", argv[0]) == 0)
214 rts = -1;
215 else {
216 char *end;
217
218 if (!*argv[0])
219 return 1;
220 rts = strtoul(argv[0], &end, 10);
221 if (*end != '\0')
222 return 1;
223 }
224
225 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);
226
227 return 0;
228 nla_put_failure:
229 return -ENOBUFS;
230 }
231 COMMAND(set, rts, "<rts threshold|off>",
232 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
233 "Set rts threshold.");
234
235 static int handle_retry(struct nl80211_state *state,
236 struct nl_msg *msg,
237 int argc, char **argv, enum id_input id)
238 {
239 unsigned int retry_short = 0, retry_long = 0;
240 bool have_retry_s = false, have_retry_l = false;
241 int i;
242 enum {
243 S_NONE,
244 S_SHORT,
245 S_LONG,
246 } parser_state = S_NONE;
247
248 if (!argc || (argc != 2 && argc != 4))
249 return 1;
250
251 for (i = 0; i < argc; i++) {
252 char *end;
253 unsigned int tmpul;
254
255 if (strcmp(argv[i], "short") == 0) {
256 if (have_retry_s)
257 return 1;
258 parser_state = S_SHORT;
259 have_retry_s = true;
260 } else if (strcmp(argv[i], "long") == 0) {
261 if (have_retry_l)
262 return 1;
263 parser_state = S_LONG;
264 have_retry_l = true;
265 } else {
266 tmpul = strtoul(argv[i], &end, 10);
267 if (*end != '\0')
268 return 1;
269 if (!tmpul || tmpul > 255)
270 return -EINVAL;
271 switch (parser_state) {
272 case S_SHORT:
273 retry_short = tmpul;
274 break;
275 case S_LONG:
276 retry_long = tmpul;
277 break;
278 default:
279 return 1;
280 }
281 }
282 }
283
284 if (!have_retry_s && !have_retry_l)
285 return 1;
286 if (have_retry_s)
287 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short);
288 if (have_retry_l)
289 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long);
290
291 return 0;
292 nla_put_failure:
293 return -ENOBUFS;
294 }
295 COMMAND(set, retry, "[short <limit>] [long <limit>]",
296 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry,
297 "Set retry limit.");
298
299 #ifndef NETNS_RUN_DIR
300 #define NETNS_RUN_DIR "/var/run/netns"
301 #endif
302 static int netns_get_fd(const char *name)
303 {
304 char pathbuf[MAXPATHLEN];
305 const char *path, *ptr;
306
307 path = name;
308 ptr = strchr(name, '/');
309 if (!ptr) {
310 snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
311 NETNS_RUN_DIR, name );
312 path = pathbuf;
313 }
314 return open(path, O_RDONLY);
315 }
316
317 static int handle_netns(struct nl80211_state *state,
318 struct nl_msg *msg,
319 int argc, char **argv,
320 enum id_input id)
321 {
322 char *end;
323 int fd;
324
325 if (argc < 1 || !*argv[0])
326 return 1;
327
328 if (argc == 1) {
329 NLA_PUT_U32(msg, NL80211_ATTR_PID,
330 strtoul(argv[0], &end, 10));
331 if (*end != '\0') {
332 printf("Invalid parameter: pid(%s)\n", argv[0]);
333 return 1;
334 }
335 return 0;
336 }
337
338 if (argc != 2 || strcmp(argv[0], "name"))
339 return 1;
340
341 if ((fd = netns_get_fd(argv[1])) >= 0) {
342 NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd);
343 return 0;
344 } else {
345 printf("Invalid parameter: nsname(%s)\n", argv[0]);
346 }
347
348 return 1;
349
350 nla_put_failure:
351 return -ENOBUFS;
352 }
353 COMMAND(set, netns, "{ <pid> | name <nsname> }",
354 NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
355 "Put this wireless device into a different network namespace:\n"
356 " <pid> - change network namespace by process id\n"
357 " <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n"
358 " or by absolute path (man ip-netns)\n");
359
360 static int handle_coverage(struct nl80211_state *state,
361 struct nl_msg *msg,
362 int argc, char **argv,
363 enum id_input id)
364 {
365 char *end;
366 unsigned int coverage;
367
368 if (argc != 1)
369 return 1;
370
371 if (!*argv[0])
372 return 1;
373 coverage = strtoul(argv[0], &end, 10);
374 if (coverage > 255)
375 return 1;
376
377 if (*end)
378 return 1;
379
380 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
381
382 return 0;
383 nla_put_failure:
384 return -ENOBUFS;
385 }
386 COMMAND(set, coverage, "<coverage class>",
387 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
388 "Set coverage class (1 for every 3 usec of air propagation time).\n"
389 "Valid values: 0 - 255.");
390
391 static int handle_distance(struct nl80211_state *state,
392 struct nl_msg *msg,
393 int argc, char **argv,
394 enum id_input id)
395 {
396 if (argc != 1)
397 return 1;
398
399 if (!*argv[0])
400 return 1;
401
402 if (strcmp("auto", argv[0]) == 0) {
403 NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK);
404 } else {
405 char *end;
406 unsigned int distance, coverage;
407
408 distance = strtoul(argv[0], &end, 10);
409
410 if (*end)
411 return 1;
412
413 /*
414 * Divide double the distance by the speed of light
415 * in m/usec (300) to get round-trip time in microseconds
416 * and then divide the result by three to get coverage class
417 * as specified in IEEE 802.11-2007 table 7-27.
418 * Values are rounded upwards.
419 */
420 coverage = (distance + 449) / 450;
421 if (coverage > 255)
422 return 1;
423
424 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
425 }
426
427 return 0;
428 nla_put_failure:
429 return -ENOBUFS;
430 }
431 COMMAND(set, distance, "<auto|distance>",
432 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
433 "Enable ACK timeout estimation algorithm (dynack) or set appropriate\n"
434 "coverage class for given link distance in meters.\n"
435 "To disable dynack set valid value for coverage class.\n"
436 "Valid values: 0 - 114750");
437
438 static int handle_txpower(struct nl80211_state *state,
439 struct nl_msg *msg,
440 int argc, char **argv,
441 enum id_input id)
442 {
443 enum nl80211_tx_power_setting type;
444 int mbm;
445
446 /* get the required args */
447 if (argc != 1 && argc != 2)
448 return 1;
449
450 if (!strcmp(argv[0], "auto"))
451 type = NL80211_TX_POWER_AUTOMATIC;
452 else if (!strcmp(argv[0], "fixed"))
453 type = NL80211_TX_POWER_FIXED;
454 else if (!strcmp(argv[0], "limit"))
455 type = NL80211_TX_POWER_LIMITED;
456 else {
457 printf("Invalid parameter: %s\n", argv[0]);
458 return 2;
459 }
460
461 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
462
463 if (type != NL80211_TX_POWER_AUTOMATIC) {
464 char *endptr;
465 if (argc != 2) {
466 printf("Missing TX power level argument.\n");
467 return 2;
468 }
469
470 mbm = strtol(argv[1], &endptr, 10);
471 if (*endptr)
472 return 2;
473 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
474 } else if (argc != 1)
475 return 1;
476
477 return 0;
478
479 nla_put_failure:
480 return -ENOBUFS;
481 }
482 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
483 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
484 "Specify transmit power level and setting type.");
485 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
486 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
487 "Specify transmit power level and setting type.");
488
489 static int handle_antenna(struct nl80211_state *state,
490 struct nl_msg *msg,
491 int argc, char **argv,
492 enum id_input id)
493 {
494 char *end;
495 uint32_t tx_ant = 0, rx_ant = 0;
496
497 if (argc == 1 && strcmp(argv[0], "all") == 0) {
498 tx_ant = 0xffffffff;
499 rx_ant = 0xffffffff;
500 } else if (argc == 1) {
501 tx_ant = rx_ant = strtoul(argv[0], &end, 0);
502 if (*end)
503 return 1;
504 }
505 else if (argc == 2) {
506 tx_ant = strtoul(argv[0], &end, 0);
507 if (*end)
508 return 1;
509 rx_ant = strtoul(argv[1], &end, 0);
510 if (*end)
511 return 1;
512 } else
513 return 1;
514
515 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
516 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
517
518 return 0;
519
520 nla_put_failure:
521 return -ENOBUFS;
522 }
523 COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
524 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
525 "Set a bitmap of allowed antennas to use for TX and RX.\n"
526 "The driver may reject antenna configurations it cannot support.");