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