#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.");