]> git.ipfire.org Git - thirdparty/iw.git/blobdiff - mesh.c
add a few new commands/events
[thirdparty/iw.git] / mesh.c
diff --git a/mesh.c b/mesh.c
index 39a24b156c922234b84d6cbc49c5acdc82f256b2..0090530569154482ecd5ec1006f3fd7a57804414 100644 (file)
--- a/mesh.c
+++ b/mesh.c
 #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;
@@ -93,6 +97,36 @@ static uint32_t _parse_u32(const char *str, _any *ret)
        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));
@@ -113,16 +147,52 @@ 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[] =
 {
@@ -144,6 +214,9 @@ const static struct mesh_param_descr _mesh_param_descrs[] =
        {"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},
@@ -165,6 +238,33 @@ const static struct mesh_param_descr _mesh_param_descrs[] =
        {"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)
@@ -177,74 +277,94 @@ static void print_all_mesh_param_descr(void)
                printf(" - %s\n", _mesh_param_descrs[i].name);
 }
 
-static const struct mesh_param_descr* find_mesh_param(int argc, char **argv,
-                                               const char *action_name)
+static const struct mesh_param_descr *find_mesh_param(const char *name)
 {
        int i;
-       const struct mesh_param_descr *mdescr;
-
-       if (argc < 1) {
-               printf("You must specify which mesh parameter to %s.\n",
-                      action_name);
-               print_all_mesh_param_descr();
-               return NULL;
-       }
 
        /* Find out what mesh parameter we want to change. */
-       mdescr = NULL;
        for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
-               if (!strcmp(_mesh_param_descrs[i].name, argv[0]))
+               if (strcmp(_mesh_param_descrs[i].name, name) == 0)
                        return _mesh_param_descrs + i;
        }
 
-       if (!mdescr) {
-               print_all_mesh_param_descr();
-               return NULL;
-       }
-       return mdescr;
+       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)
+                                  int argc, char **argv,
+                                  enum id_input id)
 {
-       int err;
-       uint32_t ret;
        const struct mesh_param_descr *mdescr;
        struct nlattr *container;
-       _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;
-       }
+       uint32_t ret;
+       int err;
 
-       /* Construct a netlink message */
        container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
        if (!container)
                return -ENOBUFS;
-       err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any);
+
+       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>",
-       NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam);
+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)
@@ -264,9 +384,21 @@ static int print_mesh_param_handler(struct nl_msg *msg, void *arg)
 
        /* unpack the mesh parameters */
        if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX,
-                               parent_attr, NULL))
+                            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");
@@ -274,20 +406,216 @@ static int print_mesh_param_handler(struct nl_msg *msg, void *arg)
 }
 
 static int get_interface_meshparam(struct nl80211_state *state,
-                                  struct nl_cb *cb,
                                   struct nl_msg *msg,
-                                  int argc, char **argv)
+                                  int argc, char **argv,
+                                  enum id_input id)
 {
-       const struct mesh_param_descr *mdescr;
+       const struct mesh_param_descr *mdescr = NULL;
 
-       mdescr = find_mesh_param(argc, argv, "get");
-       if (!mdescr)
-               return 2;
+       if (argc > 1)
+               return 1;
 
-       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
-                 print_mesh_param_handler, (void *)mdescr);
+       if (argc == 1) {
+               mdescr = find_mesh_param(argv[0]);
+               if (!mdescr)
+                       return 2;
+       }
+
+       register_handler(print_mesh_param_handler, (void *)mdescr);
        return 0;
 }
 
-COMMAND(get, mesh_param, "<param>",
-       NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam);
+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_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, 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_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.");