You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
15 KiB
625 lines
15 KiB
#include <net/if.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include <netlink/genl/genl.h>
|
|
#include <netlink/genl/family.h>
|
|
#include <netlink/genl/ctrl.h>
|
|
#include <netlink/msg.h>
|
|
#include <netlink/attr.h>
|
|
|
|
#include "nl80211.h"
|
|
#include "iw.h"
|
|
|
|
SECTION(mesh);
|
|
|
|
|
|
typedef struct _any_t {
|
|
union {
|
|
uint32_t as_32;
|
|
int32_t as_s32;
|
|
uint16_t as_16;
|
|
uint8_t as_8;
|
|
} u;
|
|
} _any;
|
|
|
|
/* describes a mesh parameter */
|
|
struct mesh_param_descr {
|
|
const char *name;
|
|
enum nl80211_meshconf_params mesh_param_num;
|
|
int (*nla_put_fn)(struct nl_msg*, int, _any*);
|
|
uint32_t (*parse_fn)(const char*, _any*);
|
|
void (*nla_print_fn)(struct nlattr *);
|
|
};
|
|
|
|
/* utility functions for manipulating and printing u8/u16/u32 values and
|
|
* timesouts. */
|
|
static int _my_nla_put_u8(struct nl_msg *n, int mesh_param_num, _any *value)
|
|
{
|
|
return nla_put(n, mesh_param_num, sizeof(uint8_t), &value->u.as_8);
|
|
}
|
|
|
|
static int _my_nla_put_u16(struct nl_msg *n, int mesh_param_num, _any *value)
|
|
{
|
|
return nla_put(n, mesh_param_num, sizeof(uint16_t), &value->u.as_16);
|
|
}
|
|
|
|
static int _my_nla_put_u32(struct nl_msg *n, int mesh_param_num, _any *value)
|
|
{
|
|
return nla_put(n, mesh_param_num, sizeof(uint32_t), &value->u.as_32);
|
|
}
|
|
|
|
static uint32_t _parse_u8(const char *str, _any *ret)
|
|
{
|
|
char *endptr = NULL;
|
|
unsigned long int v = strtoul(str, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
return 0xff;
|
|
if (v > 0xff)
|
|
return 0xff;
|
|
ret->u.as_8 = (uint8_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t _parse_u8_as_bool(const char *str, _any *ret)
|
|
{
|
|
char *endptr = NULL;
|
|
unsigned long int v = strtoul(str, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
return 0x1;
|
|
if (v > 0x1)
|
|
return 0x1;
|
|
ret->u.as_8 = (uint8_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t _parse_u16(const char *str, _any *ret)
|
|
{
|
|
char *endptr = NULL;
|
|
long int v = strtol(str, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
return 0xffff;
|
|
if ((v < 0) || (v > 0xffff))
|
|
return 0xffff;
|
|
ret->u.as_16 = (uint16_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t _parse_u32(const char *str, _any *ret)
|
|
{
|
|
char *endptr = NULL;
|
|
long long int v = strtoll(str, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
return 0xffffffff;
|
|
if ((v < 0) || (v > 0xffffffff))
|
|
return 0xffffffff;
|
|
ret->u.as_32 = (uint32_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t _parse_s32(const char *str, _any *ret)
|
|
{
|
|
char *endptr = NULL;
|
|
long int v = strtol(str, &endptr, 10);
|
|
if (*endptr != '\0')
|
|
return 0xffffffff;
|
|
if (v > 0xff)
|
|
return 0xffffffff;
|
|
ret->u.as_s32 = (int32_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t _parse_u32_power_mode(const char *str, _any *ret)
|
|
{
|
|
unsigned long int v;
|
|
|
|
/* Parse attribute for the name of power mode */
|
|
if (!strcmp(str, "active"))
|
|
v = NL80211_MESH_POWER_ACTIVE;
|
|
else if (!strcmp(str, "light"))
|
|
v = NL80211_MESH_POWER_LIGHT_SLEEP;
|
|
else if (!strcmp(str, "deep"))
|
|
v = NL80211_MESH_POWER_DEEP_SLEEP;
|
|
else
|
|
return 0xff;
|
|
|
|
ret->u.as_32 = (uint32_t)v;
|
|
return 0;
|
|
}
|
|
|
|
static void _print_u8(struct nlattr *a)
|
|
{
|
|
printf("%d", nla_get_u8(a));
|
|
}
|
|
|
|
static void _print_u16(struct nlattr *a)
|
|
{
|
|
printf("%d", nla_get_u16(a));
|
|
}
|
|
|
|
static void _print_u16_timeout(struct nlattr *a)
|
|
{
|
|
printf("%d milliseconds", nla_get_u16(a));
|
|
}
|
|
|
|
static void _print_u16_in_TUs(struct nlattr *a)
|
|
{
|
|
printf("%d TUs", nla_get_u16(a));
|
|
}
|
|
|
|
static void _print_u32(struct nlattr *a)
|
|
{
|
|
printf("%d", nla_get_u32(a));
|
|
}
|
|
|
|
static void _print_u32_timeout(struct nlattr *a)
|
|
{
|
|
printf("%u milliseconds", nla_get_u32(a));
|
|
}
|
|
|
|
static void _print_u32_in_seconds(struct nlattr *a)
|
|
{
|
|
printf("%d seconds", nla_get_u32(a));
|
|
}
|
|
|
|
static void _print_u32_in_TUs(struct nlattr *a)
|
|
{
|
|
printf("%d TUs", nla_get_u32(a));
|
|
}
|
|
|
|
static void _print_u32_power_mode(struct nlattr *a)
|
|
{
|
|
unsigned long v = nla_get_u32(a);
|
|
|
|
switch (v) {
|
|
case NL80211_MESH_POWER_ACTIVE:
|
|
printf("active");
|
|
break;
|
|
case NL80211_MESH_POWER_LIGHT_SLEEP:
|
|
printf("light");
|
|
break;
|
|
case NL80211_MESH_POWER_DEEP_SLEEP:
|
|
printf("deep");
|
|
break;
|
|
default:
|
|
printf("undefined");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void _print_s32_in_dBm(struct nlattr *a)
|
|
{
|
|
printf("%d dBm", (int32_t) nla_get_u32(a));
|
|
}
|
|
|
|
|
|
/* The current mesh parameters */
|
|
const static struct mesh_param_descr _mesh_param_descrs[] =
|
|
{
|
|
{"mesh_retry_timeout",
|
|
NL80211_MESHCONF_RETRY_TIMEOUT,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
|
|
{"mesh_confirm_timeout",
|
|
NL80211_MESHCONF_CONFIRM_TIMEOUT,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
|
|
{"mesh_holding_timeout",
|
|
NL80211_MESHCONF_HOLDING_TIMEOUT,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
|
|
{"mesh_max_peer_links",
|
|
NL80211_MESHCONF_MAX_PEER_LINKS,
|
|
_my_nla_put_u16, _parse_u16, _print_u16},
|
|
{"mesh_max_retries",
|
|
NL80211_MESHCONF_MAX_RETRIES,
|
|
_my_nla_put_u8, _parse_u8, _print_u8},
|
|
{"mesh_ttl",
|
|
NL80211_MESHCONF_TTL,
|
|
_my_nla_put_u8, _parse_u8, _print_u8},
|
|
{"mesh_element_ttl",
|
|
NL80211_MESHCONF_ELEMENT_TTL,
|
|
_my_nla_put_u8, _parse_u8, _print_u8},
|
|
{"mesh_auto_open_plinks",
|
|
NL80211_MESHCONF_AUTO_OPEN_PLINKS,
|
|
_my_nla_put_u8, _parse_u8_as_bool, _print_u8},
|
|
{"mesh_hwmp_max_preq_retries",
|
|
NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
|
|
_my_nla_put_u8, _parse_u8, _print_u8},
|
|
{"mesh_path_refresh_time",
|
|
NL80211_MESHCONF_PATH_REFRESH_TIME,
|
|
_my_nla_put_u32, _parse_u32, _print_u32_timeout},
|
|
{"mesh_min_discovery_timeout",
|
|
NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
|
|
{"mesh_hwmp_active_path_timeout",
|
|
NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
|
|
_my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
|
|
{"mesh_hwmp_preq_min_interval",
|
|
NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
|
|
{"mesh_hwmp_net_diameter_traversal_time",
|
|
NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
|
|
{"mesh_hwmp_rootmode", NL80211_MESHCONF_HWMP_ROOTMODE,
|
|
_my_nla_put_u8, _parse_u8, _print_u8},
|
|
{"mesh_hwmp_rann_interval", NL80211_MESHCONF_HWMP_RANN_INTERVAL,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
|
|
{"mesh_gate_announcements", NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
|
|
_my_nla_put_u8, _parse_u8, _print_u8},
|
|
{"mesh_fwding", NL80211_MESHCONF_FORWARDING,
|
|
_my_nla_put_u8, _parse_u8_as_bool, _print_u8},
|
|
{"mesh_sync_offset_max_neighor",
|
|
NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
|
|
_my_nla_put_u32, _parse_u32, _print_u32},
|
|
{"mesh_rssi_threshold", NL80211_MESHCONF_RSSI_THRESHOLD,
|
|
_my_nla_put_u32, _parse_s32, _print_s32_in_dBm},
|
|
{"mesh_hwmp_active_path_to_root_timeout",
|
|
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
|
|
_my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
|
|
{"mesh_hwmp_root_interval", NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
|
|
{"mesh_hwmp_confirmation_interval",
|
|
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
|
|
{"mesh_power_mode", NL80211_MESHCONF_POWER_MODE,
|
|
_my_nla_put_u32, _parse_u32_power_mode, _print_u32_power_mode},
|
|
{"mesh_awake_window", NL80211_MESHCONF_AWAKE_WINDOW,
|
|
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
|
|
{"mesh_plink_timeout", NL80211_MESHCONF_PLINK_TIMEOUT,
|
|
_my_nla_put_u32, _parse_u32, _print_u32_in_seconds},
|
|
};
|
|
|
|
static void print_all_mesh_param_descr(void)
|
|
{
|
|
int i;
|
|
|
|
printf("Possible mesh parameters are:\n");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++)
|
|
printf(" - %s\n", _mesh_param_descrs[i].name);
|
|
}
|
|
|
|
static const struct mesh_param_descr *find_mesh_param(const char *name)
|
|
{
|
|
int i;
|
|
|
|
/* Find out what mesh parameter we want to change. */
|
|
for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
|
|
if (strcmp(_mesh_param_descrs[i].name, name) == 0)
|
|
return _mesh_param_descrs + i;
|
|
}
|
|
|
|
print_all_mesh_param_descr();
|
|
return NULL;
|
|
}
|
|
|
|
/* Setter */
|
|
static int set_interface_meshparam(struct nl80211_state *state,
|
|
struct nl_cb *cb,
|
|
struct nl_msg *msg,
|
|
int argc, char **argv,
|
|
enum id_input id)
|
|
{
|
|
const struct mesh_param_descr *mdescr;
|
|
struct nlattr *container;
|
|
uint32_t ret;
|
|
int err;
|
|
|
|
container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
|
|
if (!container)
|
|
return -ENOBUFS;
|
|
|
|
if (!argc)
|
|
return 1;
|
|
|
|
while (argc) {
|
|
const char *name;
|
|
char *value;
|
|
_any any;
|
|
|
|
memset(&any, 0, sizeof(_any));
|
|
|
|
name = argv[0];
|
|
value = strchr(name, '=');
|
|
if (value) {
|
|
*value = '\0';
|
|
value++;
|
|
argc--;
|
|
argv++;
|
|
} else {
|
|
/* backward compat -- accept w/o '=' */
|
|
if (argc < 2) {
|
|
printf("Must specify a value for %s.\n", name);
|
|
return 2;
|
|
}
|
|
value = argv[1];
|
|
argc -= 2;
|
|
argv += 2;
|
|
}
|
|
|
|
mdescr = find_mesh_param(name);
|
|
if (!mdescr)
|
|
return 2;
|
|
|
|
/* Parse the new value */
|
|
ret = mdescr->parse_fn(value, &any);
|
|
if (ret != 0) {
|
|
if (mdescr->mesh_param_num
|
|
== NL80211_MESHCONF_POWER_MODE)
|
|
printf("%s must be set to active, light or "
|
|
"deep.\n", mdescr->name);
|
|
else
|
|
printf("%s must be set to a number "
|
|
"between 0 and %u\n",
|
|
mdescr->name, ret);
|
|
|
|
return 2;
|
|
}
|
|
|
|
err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any);
|
|
if (err)
|
|
return err;
|
|
}
|
|
nla_nest_end(msg, container);
|
|
|
|
return err;
|
|
}
|
|
|
|
COMMAND(set, mesh_param, "<param>=<value> [<param>=<value>]*",
|
|
NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam,
|
|
"Set mesh parameter (run command without any to see available ones).");
|
|
|
|
/* Getter */
|
|
static int print_mesh_param_handler(struct nl_msg *msg, void *arg)
|
|
{
|
|
const struct mesh_param_descr *mdescr = arg;
|
|
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
|
|
struct nlattr *parent_attr;
|
|
struct nlattr *mesh_params[NL80211_MESHCONF_ATTR_MAX + 1];
|
|
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
|
|
|
/* locate NL80211_ATTR_MESH_PARAMS */
|
|
nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
|
genlmsg_attrlen(gnlh, 0), NULL);
|
|
parent_attr = attrs[NL80211_ATTR_MESH_PARAMS];
|
|
if (!parent_attr)
|
|
return -EINVAL;
|
|
|
|
/* unpack the mesh parameters */
|
|
if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX,
|
|
parent_attr, NULL))
|
|
return -EINVAL;
|
|
|
|
if (!mdescr) {
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
|
|
mdescr = &_mesh_param_descrs[i];
|
|
printf("%s = ", mdescr->name);
|
|
mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
|
|
printf("\n");
|
|
}
|
|
return NL_SKIP;
|
|
}
|
|
|
|
/* print out the mesh parameter */
|
|
mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
|
|
printf("\n");
|
|
return NL_SKIP;
|
|
}
|
|
|
|
static int get_interface_meshparam(struct nl80211_state *state,
|
|
struct nl_cb *cb,
|
|
struct nl_msg *msg,
|
|
int argc, char **argv,
|
|
enum id_input id)
|
|
{
|
|
const struct mesh_param_descr *mdescr = NULL;
|
|
|
|
if (argc > 1)
|
|
return 1;
|
|
|
|
if (argc == 1) {
|
|
mdescr = find_mesh_param(argv[0]);
|
|
if (!mdescr)
|
|
return 2;
|
|
}
|
|
|
|
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
|
|
print_mesh_param_handler, (void *)mdescr);
|
|
return 0;
|
|
}
|
|
|
|
COMMAND(get, mesh_param, "[<param>]",
|
|
NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam,
|
|
"Retrieve mesh parameter (run command without any to see available ones).");
|
|
|
|
static int join_mesh(struct nl80211_state *state, struct nl_cb *cb,
|
|
struct nl_msg *msg, int argc, char **argv,
|
|
enum id_input id)
|
|
{
|
|
struct nlattr *container;
|
|
float rate;
|
|
unsigned char rates[NL80211_MAX_SUPP_RATES];
|
|
int bintval, dtim_period, i, n_rates = 0;
|
|
char *end, *value = NULL, *sptr = NULL;
|
|
unsigned long freq = 0;
|
|
static const struct {
|
|
const char *name;
|
|
unsigned int width;
|
|
int freq1_diff;
|
|
int chantype; /* for older kernel */
|
|
} *chanmode_selected = NULL, chanmode[] = {
|
|
{ .name = "HT20",
|
|
.width = NL80211_CHAN_WIDTH_20,
|
|
.freq1_diff = 0,
|
|
.chantype = NL80211_CHAN_HT20 },
|
|
{ .name = "HT40+",
|
|
.width = NL80211_CHAN_WIDTH_40,
|
|
.freq1_diff = 10,
|
|
.chantype = NL80211_CHAN_HT40PLUS },
|
|
{ .name = "HT40-",
|
|
.width = NL80211_CHAN_WIDTH_40,
|
|
.freq1_diff = -10,
|
|
.chantype = NL80211_CHAN_HT40MINUS },
|
|
{ .name = "NOHT",
|
|
.width = NL80211_CHAN_WIDTH_20_NOHT,
|
|
.freq1_diff = 0,
|
|
.chantype = NL80211_CHAN_NO_HT },
|
|
};
|
|
|
|
if (argc < 1)
|
|
return 1;
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(argv[0]), argv[0]);
|
|
argc--;
|
|
argv++;
|
|
|
|
/* freq */
|
|
if (argc > 1 && strcmp(argv[0], "freq") == 0) {
|
|
argv++;
|
|
argc--;
|
|
|
|
freq = strtoul(argv[0], &end, 10);
|
|
if (*end != '\0')
|
|
return 1;
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
|
|
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
/* channel type */
|
|
if (argc) {
|
|
for (i = 0; i < ARRAY_SIZE(chanmode); i++) {
|
|
if (strcasecmp(chanmode[i].name, argv[0]) == 0) {
|
|
chanmode_selected = &chanmode[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (chanmode_selected) {
|
|
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
|
|
chanmode_selected->width);
|
|
NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1,
|
|
freq + chanmode_selected->freq1_diff);
|
|
if (chanmode_selected->chantype != -1)
|
|
NLA_PUT_U32(msg,
|
|
NL80211_ATTR_WIPHY_CHANNEL_TYPE,
|
|
chanmode_selected->chantype);
|
|
|
|
argv++;
|
|
argc--;
|
|
}
|
|
}
|
|
|
|
/* basic rates */
|
|
if (argc > 1 && strcmp(argv[0], "basic-rates") == 0) {
|
|
argv++;
|
|
argc--;
|
|
|
|
value = strtok_r(argv[0], ",", &sptr);
|
|
|
|
while (value && n_rates < NL80211_MAX_SUPP_RATES) {
|
|
rate = strtod(value, &end);
|
|
rates[n_rates] = rate * 2;
|
|
|
|
/* filter out suspicious values */
|
|
if (*end != '\0' || !rates[n_rates] ||
|
|
rate*2 != rates[n_rates])
|
|
return 1;
|
|
|
|
n_rates++;
|
|
value = strtok_r(NULL, ",", &sptr);
|
|
}
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, n_rates, rates);
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
/* multicast rate */
|
|
if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) {
|
|
argv++;
|
|
argc--;
|
|
|
|
rate = strtod(argv[0], &end);
|
|
if (*end != '\0')
|
|
return 1;
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10));
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
if (argc > 1 && strcmp(argv[0], "beacon-interval") == 0) {
|
|
argc--;
|
|
argv++;
|
|
|
|
bintval = strtoul(argv[0], &end, 10);
|
|
if (*end != '\0')
|
|
return 1;
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, bintval);
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
if (argc > 1 && strcmp(argv[0], "dtim-period") == 0) {
|
|
argc--;
|
|
argv++;
|
|
|
|
dtim_period = strtoul(argv[0], &end, 10);
|
|
if (*end != '\0')
|
|
return 1;
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
|
|
if (!container)
|
|
return -ENOBUFS;
|
|
|
|
if (argc > 1 && strcmp(argv[0], "vendor_sync") == 0) {
|
|
argv++;
|
|
argc--;
|
|
if (strcmp(argv[0], "on") == 0)
|
|
NLA_PUT_U8(msg,
|
|
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 1);
|
|
else
|
|
NLA_PUT_U8(msg,
|
|
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 0);
|
|
argv++;
|
|
argc--;
|
|
}
|
|
/* parse and put other NL80211_ATTR_MESH_SETUP elements here */
|
|
|
|
nla_nest_end(msg, container);
|
|
|
|
if (!argc)
|
|
return 0;
|
|
return set_interface_meshparam(state, cb, msg, argc, argv, id);
|
|
nla_put_failure:
|
|
return -ENOBUFS;
|
|
}
|
|
COMMAND(mesh, join, "<mesh ID> [[freq <freq in MHz> <HT20|HT40+|HT40-|NOHT>]"
|
|
" [basic-rates <rate in Mbps,rate2,...>]], [mcast-rate <rate in Mbps>]"
|
|
" [beacon-interval <time in TUs>] [dtim-period <value>]"
|
|
" [vendor_sync on|off] [<param>=<value>]*",
|
|
NL80211_CMD_JOIN_MESH, 0, CIB_NETDEV, join_mesh,
|
|
"Join a mesh with the given mesh ID with frequency, basic-rates,\n"
|
|
"mcast-rate and mesh parameters. Basic-rates are applied only if\n"
|
|
"frequency is provided.");
|
|
|
|
static int leave_mesh(struct nl80211_state *state, struct nl_cb *cb,
|
|
struct nl_msg *msg, int argc, char **argv,
|
|
enum id_input id)
|
|
{
|
|
if (argc)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh,
|
|
"Leave a mesh.");
|