Home | History | Annotate | Download | only in iw
      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. */
     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 
     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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    130 static void _print_u8(struct nlattr *a)
    131 {
    132 	printf("%d", nla_get_u8(a));
    133 }
    134 
    135 static void _print_u16(struct nlattr *a)
    136 {
    137 	printf("%d", nla_get_u16(a));
    138 }
    139 
    140 static void _print_u16_timeout(struct nlattr *a)
    141 {
    142 	printf("%d milliseconds", nla_get_u16(a));
    143 }
    144 
    145 static void _print_u16_in_TUs(struct nlattr *a)
    146 {
    147 	printf("%d TUs", nla_get_u16(a));
    148 }
    149 
    150 static void _print_u32(struct nlattr *a)
    151 {
    152 	printf("%d", nla_get_u32(a));
    153 }
    154 
    155 static void _print_u32_timeout(struct nlattr *a)
    156 {
    157 	printf("%u milliseconds", nla_get_u32(a));
    158 }
    159 
    160 static void _print_u32_in_seconds(struct nlattr *a)
    161 {
    162 	printf("%d seconds", nla_get_u32(a));
    163 }
    164 
    165 static void _print_u32_in_TUs(struct nlattr *a)
    166 {
    167 	printf("%d TUs", nla_get_u32(a));
    168 }
    169 
    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 
    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 
    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 
    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 */
    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 */
    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 
    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 
    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 
    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