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 
14 SECTION(mesh);
15 
16 
17 typedef 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 */
27 struct 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. */
_my_nla_put_u8(struct nl_msg * n,int mesh_param_num,_any * value)37 static 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 
_my_nla_put_u16(struct nl_msg * n,int mesh_param_num,_any * value)42 static 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 
_my_nla_put_u32(struct nl_msg * n,int mesh_param_num,_any * value)47 static 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 
_parse_u8(const char * str,_any * ret)52 static 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 
_parse_u8_as_bool(const char * str,_any * ret)64 static 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 
_parse_u16(const char * str,_any * ret)76 static 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 
_parse_u32(const char * str,_any * ret)88 static 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 
_parse_s32(const char * str,_any * ret)100 static 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 
_parse_u32_power_mode(const char * str,_any * ret)112 static 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 
_print_u8(struct nlattr * a)130 static void _print_u8(struct nlattr *a)
131 {
132 	printf("%d", nla_get_u8(a));
133 }
134 
_print_u16(struct nlattr * a)135 static void _print_u16(struct nlattr *a)
136 {
137 	printf("%d", nla_get_u16(a));
138 }
139 
_print_u16_timeout(struct nlattr * a)140 static void _print_u16_timeout(struct nlattr *a)
141 {
142 	printf("%d milliseconds", nla_get_u16(a));
143 }
144 
_print_u16_in_TUs(struct nlattr * a)145 static void _print_u16_in_TUs(struct nlattr *a)
146 {
147 	printf("%d TUs", nla_get_u16(a));
148 }
149 
_print_u32(struct nlattr * a)150 static void _print_u32(struct nlattr *a)
151 {
152 	printf("%d", nla_get_u32(a));
153 }
154 
_print_u32_timeout(struct nlattr * a)155 static void _print_u32_timeout(struct nlattr *a)
156 {
157 	printf("%u milliseconds", nla_get_u32(a));
158 }
159 
_print_u32_in_seconds(struct nlattr * a)160 static void _print_u32_in_seconds(struct nlattr *a)
161 {
162 	printf("%d seconds", nla_get_u32(a));
163 }
164 
_print_u32_in_TUs(struct nlattr * a)165 static void _print_u32_in_TUs(struct nlattr *a)
166 {
167 	printf("%d TUs", nla_get_u32(a));
168 }
169 
_print_u32_power_mode(struct nlattr * a)170 static 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 
_print_s32_in_dBm(struct nlattr * a)190 static 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 */
197 const 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 
print_all_mesh_param_descr(void)270 static 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 
find_mesh_param(const char * name)280 static 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 */
set_interface_meshparam(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)295 static 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 
366 COMMAND(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 */
print_mesh_param_handler(struct nl_msg * msg,void * arg)371 static 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 
get_interface_meshparam(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)409 static 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 
431 COMMAND(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 
join_mesh(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)435 static 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 }
605 COMMAND(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 
leave_mesh(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv,enum id_input id)614 static 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 }
623 COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh,
624 	"Leave a mesh.");
625