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