Two new top-level commands: iw dev <devname> get mesh_param <param>, and iw dev <devname> set mesh_param <param> <value>. These can be used to configure the 802.11s mesh networking stack. These use the new %NL80211_CMD_GET_MESH_PARAMS and %NL80211_CMD_SET_MESH_PARAMS netlink commands. We check the user input to make sure that the values given fall in the valid range for each parameter. Signed-off-by: Colin McCabe <colin@xxxxxxxxxxx> --- Makefile | 2 +- mesh.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nl80211.h | 86 ++++++++++++++++++ 3 files changed, 384 insertions(+), 1 deletions(-) create mode 100644 mesh.c diff --git a/Makefile b/Makefile index 18e89ae..6a9010e 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ CFLAGS += -O2 -g LDFLAGS += `pkg-config --libs libnl-1` NLVERSION = 1.0 -OBJS = iw.o info.o phy.o interface.o station.o util.o mpath.o reg.o +OBJS = iw.o info.o phy.o interface.o station.o util.o mpath.o reg.o mesh.o ALL = iw ifeq ($(V),1) diff --git a/mesh.c b/mesh.c new file mode 100644 index 0000000..741a662 --- /dev/null +++ b/mesh.c @@ -0,0 +1,297 @@ +#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" + +typedef struct _any_t { + union { + uint32_t as_32; + 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; +} + +void _print_u8(struct nlattr *a) +{ + printf("%d", nla_get_u8(a)); +} + +void _print_u16(struct nlattr *a) +{ + printf("%d", nla_get_u16(a)); +} + +void _print_u16_timeout(struct nlattr *a) +{ + printf("%d milliseconds", nla_get_u16(a)); +} + +void _print_u16_in_TUs(struct nlattr *a) +{ + printf("%d TUs", nla_get_u16(a)); +} + +void _print_u32_timeout(struct nlattr *a) +{ + printf("%u milliseconds", nla_get_u32(a)); +} + +void _print_u32_in_TUs(struct nlattr *a) +{ + printf("%d TUs", 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_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}, +}; + +static void print_all_mesh_param_descr(void) +{ + int i; + const char *comma = ""; + + for (i = 0; + i < sizeof(_mesh_param_descrs)/sizeof(_mesh_param_descrs[0]); + ++i) { + printf("%s%s", comma, _mesh_param_descrs[i].name); + comma = ", "; + } +} + +static const struct mesh_param_descr* find_mesh_param(int argc, char **argv, + const char *action_name) +{ + int i; + const struct mesh_param_descr *mdescr; + + if (argc < 1) { + printf("You must specify which mesh parameter to %s.\n", + action_name); + return NULL; + } + + /* Find out what mesh parameter we want to change. */ + mdescr = NULL; + for (i = 0; + i < sizeof(_mesh_param_descrs)/sizeof(_mesh_param_descrs[0]); + ++i) { + if (!strcmp(_mesh_param_descrs[i].name, argv[0])) + return _mesh_param_descrs + i; + } + + if (!mdescr) { + printf("Mesh_param must be one of: "); + print_all_mesh_param_descr(); + printf("\n"); + return NULL; + } + return mdescr; +} + +/* Setter */ +static int set_interface_meshparam(struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv) +{ + int err; + uint32_t ret; + const struct mesh_param_descr *mdescr; + _any any; + + mdescr = find_mesh_param(argc, argv, "change"); + if (!mdescr) + return 2; + if (argc != 2) { + printf("Must specify a value for %s.\n", mdescr->name); + return 2; + } + + /* Parse the new value */ + memset(&any, 0, sizeof(_any)); + ret = mdescr->parse_fn(argv[1], &any); + if (ret != 0) { + printf("%s must be set to a number " + "between 0 and %u\n", mdescr->name, ret); + return 2; + } + + /* Construct a netlink message */ + struct nlattr *container = + nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS); + if (!container) + return -ENOBUFS; + err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any); + nla_nest_end(msg, container); + + return err; +} + +COMMAND(set, mesh_param, "<param> <value>", + NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam); + +/* 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; + + /* 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 nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv) +{ + const struct mesh_param_descr *mdescr; + + mdescr = find_mesh_param(argc, argv, "get"); + 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); diff --git a/nl80211.h b/nl80211.h index 9bad654..40d7b27 100644 --- a/nl80211.h +++ b/nl80211.h @@ -106,6 +106,12 @@ * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will * store this as a valid request and then query userspace for it. * + * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -148,6 +154,9 @@ enum nl80211_commands { NL80211_CMD_SET_REG, NL80211_CMD_REQ_SET_REG, + NL80211_CMD_GET_MESH_PARAMS, + NL80211_CMD_SET_MESH_PARAMS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -296,6 +305,8 @@ enum nl80211_attrs { NL80211_ATTR_REG_ALPHA2, NL80211_ATTR_REG_RULES, + NL80211_ATTR_MESH_PARAMS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -594,4 +605,79 @@ enum nl80211_mntr_flags { NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + #endif /* __LINUX_NL80211_H */ -- 1.5.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html