]> git.ipfire.org Git - thirdparty/iw.git/blob - mesh.c
iw: add and fix -Wsign-compare
[thirdparty/iw.git] / mesh.c
1 #include <errno.h>
2 #include <string.h>
3
4 #include <netlink/genl/genl.h>
5 #include <netlink/genl/family.h>
6 #include <netlink/genl/ctrl.h>
7 #include <netlink/msg.h>
8 #include <netlink/attr.h>
9
10 #include "nl80211.h"
11 #include "iw.h"
12
13 SECTION(mesh);
14
15
16 typedef struct _any_t {
17 union {
18 uint32_t as_32;
19 int32_t as_s32;
20 uint16_t as_16;
21 uint8_t as_8;
22 } u;
23 } _any;
24
25 /* describes a mesh parameter */
26 struct mesh_param_descr {
27 const char *name;
28 enum nl80211_meshconf_params mesh_param_num;
29 int (*nla_put_fn)(struct nl_msg*, int, _any*);
30 uint32_t (*parse_fn)(const char*, _any*);
31 void (*nla_print_fn)(struct nlattr *);
32 };
33
34 /* utility functions for manipulating and printing u8/u16/u32 values and
35 * timesouts. */
36 static int _my_nla_put_u8(struct nl_msg *n, int mesh_param_num, _any *value)
37 {
38 return nla_put(n, mesh_param_num, sizeof(uint8_t), &value->u.as_8);
39 }
40
41 static int _my_nla_put_u16(struct nl_msg *n, int mesh_param_num, _any *value)
42 {
43 return nla_put(n, mesh_param_num, sizeof(uint16_t), &value->u.as_16);
44 }
45
46 static int _my_nla_put_u32(struct nl_msg *n, int mesh_param_num, _any *value)
47 {
48 return nla_put(n, mesh_param_num, sizeof(uint32_t), &value->u.as_32);
49 }
50
51 static uint32_t _parse_u8(const char *str, _any *ret)
52 {
53 char *endptr = NULL;
54 unsigned long int v = strtoul(str, &endptr, 10);
55 if (*endptr != '\0')
56 return 0xff;
57 if (v > 0xff)
58 return 0xff;
59 ret->u.as_8 = (uint8_t)v;
60 return 0;
61 }
62
63 static uint32_t _parse_u8_as_bool(const char *str, _any *ret)
64 {
65 char *endptr = NULL;
66 unsigned long int v = strtoul(str, &endptr, 10);
67 if (*endptr != '\0')
68 return 0x1;
69 if (v > 0x1)
70 return 0x1;
71 ret->u.as_8 = (uint8_t)v;
72 return 0;
73 }
74
75 static uint32_t _parse_u16(const char *str, _any *ret)
76 {
77 char *endptr = NULL;
78 long int v = strtol(str, &endptr, 10);
79 if (*endptr != '\0')
80 return 0xffff;
81 if ((v < 0) || (v > 0xffff))
82 return 0xffff;
83 ret->u.as_16 = (uint16_t)v;
84 return 0;
85 }
86
87 static uint32_t _parse_u32(const char *str, _any *ret)
88 {
89 char *endptr = NULL;
90 long long int v = strtoll(str, &endptr, 10);
91 if (*endptr != '\0')
92 return 0xffffffff;
93 if ((v < 0) || (v > 0xffffffff))
94 return 0xffffffff;
95 ret->u.as_32 = (uint32_t)v;
96 return 0;
97 }
98
99 static uint32_t _parse_s32(const char *str, _any *ret)
100 {
101 char *endptr = NULL;
102 long int v = strtol(str, &endptr, 10);
103 if (*endptr != '\0')
104 return 0xffffffff;
105 if (v > 0xff)
106 return 0xffffffff;
107 ret->u.as_s32 = (int32_t)v;
108 return 0;
109 }
110
111 static uint32_t _parse_u32_power_mode(const char *str, _any *ret)
112 {
113 unsigned long int v;
114
115 /* Parse attribute for the name of power mode */
116 if (!strcmp(str, "active"))
117 v = NL80211_MESH_POWER_ACTIVE;
118 else if (!strcmp(str, "light"))
119 v = NL80211_MESH_POWER_LIGHT_SLEEP;
120 else if (!strcmp(str, "deep"))
121 v = NL80211_MESH_POWER_DEEP_SLEEP;
122 else
123 return 0xff;
124
125 ret->u.as_32 = (uint32_t)v;
126 return 0;
127 }
128
129 static void _print_u8(struct nlattr *a)
130 {
131 printf("%d", nla_get_u8(a));
132 }
133
134 static void _print_u16(struct nlattr *a)
135 {
136 printf("%d", nla_get_u16(a));
137 }
138
139 static void _print_u16_timeout(struct nlattr *a)
140 {
141 printf("%d milliseconds", nla_get_u16(a));
142 }
143
144 static void _print_u16_in_TUs(struct nlattr *a)
145 {
146 printf("%d TUs", nla_get_u16(a));
147 }
148
149 static void _print_u32(struct nlattr *a)
150 {
151 printf("%d", nla_get_u32(a));
152 }
153
154 static void _print_u32_timeout(struct nlattr *a)
155 {
156 printf("%u milliseconds", nla_get_u32(a));
157 }
158
159 static void _print_u32_in_seconds(struct nlattr *a)
160 {
161 printf("%d seconds", nla_get_u32(a));
162 }
163
164 static void _print_u32_in_TUs(struct nlattr *a)
165 {
166 printf("%d TUs", nla_get_u32(a));
167 }
168
169 static void _print_u32_power_mode(struct nlattr *a)
170 {
171 unsigned long v = nla_get_u32(a);
172
173 switch (v) {
174 case NL80211_MESH_POWER_ACTIVE:
175 printf("active");
176 break;
177 case NL80211_MESH_POWER_LIGHT_SLEEP:
178 printf("light");
179 break;
180 case NL80211_MESH_POWER_DEEP_SLEEP:
181 printf("deep");
182 break;
183 default:
184 printf("undefined");
185 break;
186 }
187 }
188
189 static void _print_s32_in_dBm(struct nlattr *a)
190 {
191 printf("%d dBm", (int32_t) nla_get_u32(a));
192 }
193
194
195 /* The current mesh parameters */
196 const static struct mesh_param_descr _mesh_param_descrs[] =
197 {
198 {"mesh_retry_timeout",
199 NL80211_MESHCONF_RETRY_TIMEOUT,
200 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
201 {"mesh_confirm_timeout",
202 NL80211_MESHCONF_CONFIRM_TIMEOUT,
203 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
204 {"mesh_holding_timeout",
205 NL80211_MESHCONF_HOLDING_TIMEOUT,
206 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
207 {"mesh_max_peer_links",
208 NL80211_MESHCONF_MAX_PEER_LINKS,
209 _my_nla_put_u16, _parse_u16, _print_u16},
210 {"mesh_max_retries",
211 NL80211_MESHCONF_MAX_RETRIES,
212 _my_nla_put_u8, _parse_u8, _print_u8},
213 {"mesh_ttl",
214 NL80211_MESHCONF_TTL,
215 _my_nla_put_u8, _parse_u8, _print_u8},
216 {"mesh_element_ttl",
217 NL80211_MESHCONF_ELEMENT_TTL,
218 _my_nla_put_u8, _parse_u8, _print_u8},
219 {"mesh_auto_open_plinks",
220 NL80211_MESHCONF_AUTO_OPEN_PLINKS,
221 _my_nla_put_u8, _parse_u8_as_bool, _print_u8},
222 {"mesh_hwmp_max_preq_retries",
223 NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
224 _my_nla_put_u8, _parse_u8, _print_u8},
225 {"mesh_path_refresh_time",
226 NL80211_MESHCONF_PATH_REFRESH_TIME,
227 _my_nla_put_u32, _parse_u32, _print_u32_timeout},
228 {"mesh_min_discovery_timeout",
229 NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
230 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
231 {"mesh_hwmp_active_path_timeout",
232 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
233 _my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
234 {"mesh_hwmp_preq_min_interval",
235 NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
236 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
237 {"mesh_hwmp_net_diameter_traversal_time",
238 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
239 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
240 {"mesh_hwmp_rootmode", NL80211_MESHCONF_HWMP_ROOTMODE,
241 _my_nla_put_u8, _parse_u8, _print_u8},
242 {"mesh_hwmp_rann_interval", NL80211_MESHCONF_HWMP_RANN_INTERVAL,
243 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
244 {"mesh_gate_announcements", NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
245 _my_nla_put_u8, _parse_u8, _print_u8},
246 {"mesh_fwding", NL80211_MESHCONF_FORWARDING,
247 _my_nla_put_u8, _parse_u8_as_bool, _print_u8},
248 {"mesh_sync_offset_max_neighor",
249 NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
250 _my_nla_put_u32, _parse_u32, _print_u32},
251 {"mesh_rssi_threshold", NL80211_MESHCONF_RSSI_THRESHOLD,
252 _my_nla_put_u32, _parse_s32, _print_s32_in_dBm},
253 {"mesh_hwmp_active_path_to_root_timeout",
254 NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
255 _my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
256 {"mesh_hwmp_root_interval", NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
257 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
258 {"mesh_hwmp_confirmation_interval",
259 NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
260 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
261 {"mesh_power_mode", NL80211_MESHCONF_POWER_MODE,
262 _my_nla_put_u32, _parse_u32_power_mode, _print_u32_power_mode},
263 {"mesh_awake_window", NL80211_MESHCONF_AWAKE_WINDOW,
264 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
265 {"mesh_plink_timeout", NL80211_MESHCONF_PLINK_TIMEOUT,
266 _my_nla_put_u32, _parse_u32, _print_u32_in_seconds},
267 };
268
269 static void print_all_mesh_param_descr(void)
270 {
271 unsigned int i;
272
273 printf("Possible mesh parameters are:\n");
274
275 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++)
276 printf(" - %s\n", _mesh_param_descrs[i].name);
277 }
278
279 static const struct mesh_param_descr *find_mesh_param(const char *name)
280 {
281 unsigned int i;
282
283 /* Find out what mesh parameter we want to change. */
284 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
285 if (strcmp(_mesh_param_descrs[i].name, name) == 0)
286 return _mesh_param_descrs + i;
287 }
288
289 print_all_mesh_param_descr();
290 return NULL;
291 }
292
293 /* Setter */
294 static int set_interface_meshparam(struct nl80211_state *state,
295 struct nl_msg *msg,
296 int argc, char **argv,
297 enum id_input id)
298 {
299 const struct mesh_param_descr *mdescr;
300 struct nlattr *container;
301 uint32_t ret;
302 int err;
303
304 container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
305 if (!container)
306 return -ENOBUFS;
307
308 if (!argc)
309 return 1;
310
311 while (argc) {
312 const char *name;
313 char *value;
314 _any any;
315
316 memset(&any, 0, sizeof(_any));
317
318 name = argv[0];
319 value = strchr(name, '=');
320 if (value) {
321 *value = '\0';
322 value++;
323 argc--;
324 argv++;
325 } else {
326 /* backward compat -- accept w/o '=' */
327 if (argc < 2) {
328 printf("Must specify a value for %s.\n", name);
329 return 2;
330 }
331 value = argv[1];
332 argc -= 2;
333 argv += 2;
334 }
335
336 mdescr = find_mesh_param(name);
337 if (!mdescr)
338 return 2;
339
340 /* Parse the new value */
341 ret = mdescr->parse_fn(value, &any);
342 if (ret != 0) {
343 if (mdescr->mesh_param_num
344 == NL80211_MESHCONF_POWER_MODE)
345 printf("%s must be set to active, light or "
346 "deep.\n", mdescr->name);
347 else
348 printf("%s must be set to a number "
349 "between 0 and %u\n",
350 mdescr->name, ret);
351
352 return 2;
353 }
354
355 err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any);
356 if (err)
357 return err;
358 }
359 nla_nest_end(msg, container);
360
361 return err;
362 }
363
364 COMMAND(set, mesh_param, "<param>=<value> [<param>=<value>]*",
365 NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam,
366 "Set mesh parameter (run command without any to see available ones).");
367
368 /* Getter */
369 static int print_mesh_param_handler(struct nl_msg *msg, void *arg)
370 {
371 const struct mesh_param_descr *mdescr = arg;
372 struct nlattr *attrs[NL80211_ATTR_MAX + 1];
373 struct nlattr *parent_attr;
374 struct nlattr *mesh_params[NL80211_MESHCONF_ATTR_MAX + 1];
375 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
376
377 /* locate NL80211_ATTR_MESH_PARAMS */
378 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
379 genlmsg_attrlen(gnlh, 0), NULL);
380 parent_attr = attrs[NL80211_ATTR_MESH_PARAMS];
381 if (!parent_attr)
382 return -EINVAL;
383
384 /* unpack the mesh parameters */
385 if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX,
386 parent_attr, NULL))
387 return -EINVAL;
388
389 if (!mdescr) {
390 unsigned int i;
391
392 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
393 mdescr = &_mesh_param_descrs[i];
394 printf("%s = ", mdescr->name);
395 mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
396 printf("\n");
397 }
398 return NL_SKIP;
399 }
400
401 /* print out the mesh parameter */
402 mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
403 printf("\n");
404 return NL_SKIP;
405 }
406
407 static int get_interface_meshparam(struct nl80211_state *state,
408 struct nl_msg *msg,
409 int argc, char **argv,
410 enum id_input id)
411 {
412 const struct mesh_param_descr *mdescr = NULL;
413
414 if (argc > 1)
415 return 1;
416
417 if (argc == 1) {
418 mdescr = find_mesh_param(argv[0]);
419 if (!mdescr)
420 return 2;
421 }
422
423 register_handler(print_mesh_param_handler, (void *)mdescr);
424 return 0;
425 }
426
427 COMMAND(get, mesh_param, "[<param>]",
428 NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam,
429 "Retrieve mesh parameter (run command without any to see available ones).");
430
431 static int join_mesh(struct nl80211_state *state,
432 struct nl_msg *msg, int argc, char **argv,
433 enum id_input id)
434 {
435 struct nlattr *container;
436 float rate;
437 unsigned char rates[NL80211_MAX_SUPP_RATES];
438 int bintval, dtim_period, n_rates = 0;
439 char *end, *value = NULL, *sptr = NULL;
440 unsigned int i;
441 unsigned long freq = 0;
442 static const struct {
443 const char *name;
444 unsigned int width;
445 int freq1_diff;
446 int chantype; /* for older kernel */
447 } *chanmode_selected = NULL, chanmode[] = {
448 { .name = "HT20",
449 .width = NL80211_CHAN_WIDTH_20,
450 .freq1_diff = 0,
451 .chantype = NL80211_CHAN_HT20 },
452 { .name = "HT40+",
453 .width = NL80211_CHAN_WIDTH_40,
454 .freq1_diff = 10,
455 .chantype = NL80211_CHAN_HT40PLUS },
456 { .name = "HT40-",
457 .width = NL80211_CHAN_WIDTH_40,
458 .freq1_diff = -10,
459 .chantype = NL80211_CHAN_HT40MINUS },
460 { .name = "NOHT",
461 .width = NL80211_CHAN_WIDTH_20_NOHT,
462 .freq1_diff = 0,
463 .chantype = NL80211_CHAN_NO_HT },
464 };
465
466 if (argc < 1)
467 return 1;
468
469 NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(argv[0]), argv[0]);
470 argc--;
471 argv++;
472
473 /* freq */
474 if (argc > 1 && strcmp(argv[0], "freq") == 0) {
475 argv++;
476 argc--;
477
478 freq = strtoul(argv[0], &end, 10);
479 if (*end != '\0')
480 return 1;
481 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
482
483 argv++;
484 argc--;
485 }
486
487 /* channel type */
488 if (argc) {
489 for (i = 0; i < ARRAY_SIZE(chanmode); i++) {
490 if (strcasecmp(chanmode[i].name, argv[0]) == 0) {
491 chanmode_selected = &chanmode[i];
492 break;
493 }
494 }
495
496 if (chanmode_selected) {
497 NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
498 chanmode_selected->width);
499 NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1,
500 freq + chanmode_selected->freq1_diff);
501 if (chanmode_selected->chantype != -1)
502 NLA_PUT_U32(msg,
503 NL80211_ATTR_WIPHY_CHANNEL_TYPE,
504 chanmode_selected->chantype);
505
506 argv++;
507 argc--;
508 }
509 }
510
511 /* basic rates */
512 if (argc > 1 && strcmp(argv[0], "basic-rates") == 0) {
513 argv++;
514 argc--;
515
516 value = strtok_r(argv[0], ",", &sptr);
517
518 while (value && n_rates < NL80211_MAX_SUPP_RATES) {
519 rate = strtod(value, &end);
520 rates[n_rates] = rate * 2;
521
522 /* filter out suspicious values */
523 if (*end != '\0' || !rates[n_rates] ||
524 rate*2 != rates[n_rates])
525 return 1;
526
527 n_rates++;
528 value = strtok_r(NULL, ",", &sptr);
529 }
530
531 NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, n_rates, rates);
532 argv++;
533 argc--;
534 }
535
536 /* multicast rate */
537 if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) {
538 argv++;
539 argc--;
540
541 rate = strtod(argv[0], &end);
542 if (*end != '\0')
543 return 1;
544
545 NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10));
546 argv++;
547 argc--;
548 }
549
550 if (argc > 1 && strcmp(argv[0], "beacon-interval") == 0) {
551 argc--;
552 argv++;
553
554 bintval = strtoul(argv[0], &end, 10);
555 if (*end != '\0')
556 return 1;
557
558 NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, bintval);
559 argv++;
560 argc--;
561 }
562
563 if (argc > 1 && strcmp(argv[0], "dtim-period") == 0) {
564 argc--;
565 argv++;
566
567 dtim_period = strtoul(argv[0], &end, 10);
568 if (*end != '\0')
569 return 1;
570
571 NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
572 argv++;
573 argc--;
574 }
575
576 container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
577 if (!container)
578 return -ENOBUFS;
579
580 if (argc > 1 && strcmp(argv[0], "vendor_sync") == 0) {
581 argv++;
582 argc--;
583 if (strcmp(argv[0], "on") == 0)
584 NLA_PUT_U8(msg,
585 NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 1);
586 else
587 NLA_PUT_U8(msg,
588 NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 0);
589 argv++;
590 argc--;
591 }
592 /* parse and put other NL80211_ATTR_MESH_SETUP elements here */
593
594 nla_nest_end(msg, container);
595
596 if (!argc)
597 return 0;
598 return set_interface_meshparam(state, msg, argc, argv, id);
599 nla_put_failure:
600 return -ENOBUFS;
601 }
602 COMMAND(mesh, join, "<mesh ID> [[freq <freq in MHz> <HT20|HT40+|HT40-|NOHT>]"
603 " [basic-rates <rate in Mbps,rate2,...>]], [mcast-rate <rate in Mbps>]"
604 " [beacon-interval <time in TUs>] [dtim-period <value>]"
605 " [vendor_sync on|off] [<param>=<value>]*",
606 NL80211_CMD_JOIN_MESH, 0, CIB_NETDEV, join_mesh,
607 "Join a mesh with the given mesh ID with frequency, basic-rates,\n"
608 "mcast-rate and mesh parameters. Basic-rates are applied only if\n"
609 "frequency is provided.");
610
611 static int leave_mesh(struct nl80211_state *state,
612 struct nl_msg *msg, int argc, char **argv,
613 enum id_input id)
614 {
615 if (argc)
616 return 1;
617
618 return 0;
619 }
620 COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh,
621 "Leave a mesh.");