]> git.ipfire.org Git - thirdparty/iw.git/blame - mesh.c
iw: allow user to enable vendor synchronization when joining mesh
[thirdparty/iw.git] / mesh.c
CommitLineData
46c1ad1f 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
400efd3e
JB
14SECTION(mesh);
15
16
46c1ad1f 17typedef struct _any_t {
18 union {
19 uint32_t as_32;
20 uint16_t as_16;
21 uint8_t as_8;
22 } u;
23} _any;
24
25/* describes a mesh parameter */
26struct mesh_param_descr {
27 const char *name;
28 enum nl80211_meshconf_params mesh_param_num;
29 int (*nla_put_fn)(struct nl_msg*, int, _any*);
30 uint32_t (*parse_fn)(const char*, _any*);
31 void (*nla_print_fn)(struct nlattr *);
32};
33
34/* utility functions for manipulating and printing u8/u16/u32 values and
35 * timesouts. */
36static int _my_nla_put_u8(struct nl_msg *n, int mesh_param_num, _any *value)
37{
38 return nla_put(n, mesh_param_num, sizeof(uint8_t), &value->u.as_8);
39}
40
41static int _my_nla_put_u16(struct nl_msg *n, int mesh_param_num, _any *value)
42{
43 return nla_put(n, mesh_param_num, sizeof(uint16_t), &value->u.as_16);
44}
45
46static int _my_nla_put_u32(struct nl_msg *n, int mesh_param_num, _any *value)
47{
48 return nla_put(n, mesh_param_num, sizeof(uint32_t), &value->u.as_32);
49}
50
51static uint32_t _parse_u8(const char *str, _any *ret)
52{
53 char *endptr = NULL;
54 unsigned long int v = strtoul(str, &endptr, 10);
55 if (*endptr != '\0')
56 return 0xff;
57 if (v > 0xff)
58 return 0xff;
59 ret->u.as_8 = (uint8_t)v;
60 return 0;
61}
62
63static uint32_t _parse_u8_as_bool(const char *str, _any *ret)
64{
65 char *endptr = NULL;
66 unsigned long int v = strtoul(str, &endptr, 10);
67 if (*endptr != '\0')
68 return 0x1;
69 if (v > 0x1)
70 return 0x1;
71 ret->u.as_8 = (uint8_t)v;
72 return 0;
73}
74
75static uint32_t _parse_u16(const char *str, _any *ret)
76{
77 char *endptr = NULL;
78 long int v = strtol(str, &endptr, 10);
79 if (*endptr != '\0')
80 return 0xffff;
81 if ((v < 0) || (v > 0xffff))
82 return 0xffff;
83 ret->u.as_16 = (uint16_t)v;
84 return 0;
85}
86
87static uint32_t _parse_u32(const char *str, _any *ret)
88{
89 char *endptr = NULL;
90 long long int v = strtoll(str, &endptr, 10);
91 if (*endptr != '\0')
92 return 0xffffffff;
93 if ((v < 0) || (v > 0xffffffff))
94 return 0xffffffff;
95 ret->u.as_32 = (uint32_t)v;
96 return 0;
97}
98
656aa246 99static void _print_u8(struct nlattr *a)
46c1ad1f 100{
101 printf("%d", nla_get_u8(a));
102}
103
656aa246 104static void _print_u16(struct nlattr *a)
46c1ad1f 105{
106 printf("%d", nla_get_u16(a));
107}
108
656aa246 109static void _print_u16_timeout(struct nlattr *a)
46c1ad1f 110{
111 printf("%d milliseconds", nla_get_u16(a));
112}
113
656aa246 114static void _print_u16_in_TUs(struct nlattr *a)
46c1ad1f 115{
116 printf("%d TUs", nla_get_u16(a));
117}
118
656aa246 119static void _print_u32_timeout(struct nlattr *a)
46c1ad1f 120{
121 printf("%u milliseconds", nla_get_u32(a));
122}
123
656aa246 124static void _print_u32_in_TUs(struct nlattr *a)
46c1ad1f 125{
126 printf("%d TUs", nla_get_u32(a));
127}
128
129/* The current mesh parameters */
130const static struct mesh_param_descr _mesh_param_descrs[] =
131{
132 {"mesh_retry_timeout",
133 NL80211_MESHCONF_RETRY_TIMEOUT,
134 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
135 {"mesh_confirm_timeout",
136 NL80211_MESHCONF_CONFIRM_TIMEOUT,
137 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
138 {"mesh_holding_timeout",
139 NL80211_MESHCONF_HOLDING_TIMEOUT,
140 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
141 {"mesh_max_peer_links",
142 NL80211_MESHCONF_MAX_PEER_LINKS,
143 _my_nla_put_u16, _parse_u16, _print_u16},
144 {"mesh_max_retries",
145 NL80211_MESHCONF_MAX_RETRIES,
146 _my_nla_put_u8, _parse_u8, _print_u8},
147 {"mesh_ttl",
148 NL80211_MESHCONF_TTL,
149 _my_nla_put_u8, _parse_u8, _print_u8},
247beb99
JB
150 {"mesh_element_ttl",
151 NL80211_MESHCONF_ELEMENT_TTL,
152 _my_nla_put_u8, _parse_u8, _print_u8},
46c1ad1f 153 {"mesh_auto_open_plinks",
154 NL80211_MESHCONF_AUTO_OPEN_PLINKS,
155 _my_nla_put_u8, _parse_u8_as_bool, _print_u8},
156 {"mesh_hwmp_max_preq_retries",
157 NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
158 _my_nla_put_u8, _parse_u8, _print_u8},
159 {"mesh_path_refresh_time",
160 NL80211_MESHCONF_PATH_REFRESH_TIME,
161 _my_nla_put_u32, _parse_u32, _print_u32_timeout},
162 {"mesh_min_discovery_timeout",
163 NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
164 _my_nla_put_u16, _parse_u16, _print_u16_timeout},
165 {"mesh_hwmp_active_path_timeout",
166 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
167 _my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
168 {"mesh_hwmp_preq_min_interval",
169 NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
170 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
171 {"mesh_hwmp_net_diameter_traversal_time",
172 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
173 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
cc37218f
RP
174 {"mesh_hwmp_rootmode", NL80211_MESHCONF_HWMP_ROOTMODE,
175 _my_nla_put_u8, _parse_u8, _print_u8},
b9aa711a
JC
176 {"mesh_hwmp_rann_interval", NL80211_MESHCONF_HWMP_RANN_INTERVAL,
177 _my_nla_put_u16, _parse_u16, _print_u16},
178 {"mesh_gate_announcements", NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
179 _my_nla_put_u8, _parse_u8, _print_u8},
accd0d23
CYY
180 {"mesh_fwding", NL80211_MESHCONF_FORWARDING,
181 _my_nla_put_u8, _parse_u8_as_bool, _print_u8},
46c1ad1f 182};
183
184static void print_all_mesh_param_descr(void)
185{
186 int i;
46c1ad1f 187
f089471a
JB
188 printf("Possible mesh parameters are:\n");
189
190 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++)
191 printf(" - %s\n", _mesh_param_descrs[i].name);
46c1ad1f 192}
193
a34f62cf 194static const struct mesh_param_descr *find_mesh_param(const char *name)
46c1ad1f 195{
196 int i;
a34f62cf 197 const struct mesh_param_descr *mdescr = NULL;
46c1ad1f 198
199 /* Find out what mesh parameter we want to change. */
4d0d2ea7 200 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
a34f62cf 201 if (!strcmp(_mesh_param_descrs[i].name, name))
46c1ad1f 202 return _mesh_param_descrs + i;
4d0d2ea7 203 }
46c1ad1f 204
205 if (!mdescr) {
46c1ad1f 206 print_all_mesh_param_descr();
46c1ad1f 207 return NULL;
208 }
209 return mdescr;
210}
211
212/* Setter */
7c37a24d
JB
213static int set_interface_meshparam(struct nl80211_state *state,
214 struct nl_cb *cb,
215 struct nl_msg *msg,
216 int argc, char **argv)
46c1ad1f 217{
46c1ad1f 218 const struct mesh_param_descr *mdescr;
656aa246 219 struct nlattr *container;
a34f62cf
JB
220 uint32_t ret;
221 int err;
46c1ad1f 222
656aa246 223 container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
46c1ad1f 224 if (!container)
225 return -ENOBUFS;
a34f62cf
JB
226
227 if (!argc)
228 return 1;
229
230 while (argc) {
231 const char *name;
232 char *value;
233 _any any;
234
235 memset(&any, 0, sizeof(_any));
236
237 name = argv[0];
238 value = strchr(name, '=');
239 if (value) {
240 *value = '\0';
241 value++;
242 argc--;
243 argv++;
244 } else {
245 /* backward compat -- accept w/o '=' */
246 if (argc < 2) {
247 printf("Must specify a value for %s.\n", name);
248 return 2;
249 }
250 value = argv[1];
251 argc -= 2;
252 argv += 2;
253 }
254
255 mdescr = find_mesh_param(name);
256 if (!mdescr)
257 return 2;
258
259 /* Parse the new value */
260 ret = mdescr->parse_fn(value, &any);
261 if (ret != 0) {
262 printf("%s must be set to a number "
263 "between 0 and %u\n", mdescr->name, ret);
264 return 2;
265 }
266
267 err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any);
268 if (err)
269 return err;
270 }
46c1ad1f 271 nla_nest_end(msg, container);
272
273 return err;
274}
275
a34f62cf 276COMMAND(set, mesh_param, "<param>=<value> [<param>=<value>]*",
70cf4544
JB
277 NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam,
278 "Set mesh parameter (run command without any to see available ones).");
46c1ad1f 279
280/* Getter */
281static int print_mesh_param_handler(struct nl_msg *msg, void *arg)
282{
283 const struct mesh_param_descr *mdescr = arg;
284 struct nlattr *attrs[NL80211_ATTR_MAX + 1];
285 struct nlattr *parent_attr;
286 struct nlattr *mesh_params[NL80211_MESHCONF_ATTR_MAX + 1];
287 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
288
289 /* locate NL80211_ATTR_MESH_PARAMS */
290 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
291 genlmsg_attrlen(gnlh, 0), NULL);
292 parent_attr = attrs[NL80211_ATTR_MESH_PARAMS];
293 if (!parent_attr)
294 return -EINVAL;
295
296 /* unpack the mesh parameters */
297 if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX,
97e21372 298 parent_attr, NULL))
46c1ad1f 299 return -EINVAL;
300
97e21372
JB
301 if (!mdescr) {
302 int i;
303
304 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
305 mdescr = &_mesh_param_descrs[i];
306 printf("%s = ", mdescr->name);
307 mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
308 printf("\n");
309 }
310 return NL_SKIP;
311 }
312
46c1ad1f 313 /* print out the mesh parameter */
314 mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
315 printf("\n");
316 return NL_SKIP;
317}
318
7c37a24d
JB
319static int get_interface_meshparam(struct nl80211_state *state,
320 struct nl_cb *cb,
321 struct nl_msg *msg,
322 int argc, char **argv)
46c1ad1f 323{
97e21372 324 const struct mesh_param_descr *mdescr = NULL;
46c1ad1f 325
97e21372
JB
326 if (argc > 1)
327 return 1;
328
329 if (argc == 1) {
a34f62cf 330 mdescr = find_mesh_param(argv[0]);
97e21372
JB
331 if (!mdescr)
332 return 2;
333 }
46c1ad1f 334
335 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
336 print_mesh_param_handler, (void *)mdescr);
337 return 0;
338}
339
97e21372 340COMMAND(get, mesh_param, "[<param>]",
70cf4544
JB
341 NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam,
342 "Retrieve mesh parameter (run command without any to see available ones).");
400efd3e
JB
343
344static int join_mesh(struct nl80211_state *state, struct nl_cb *cb,
345 struct nl_msg *msg, int argc, char **argv)
346{
9560e43d 347 struct nlattr *container;
7d01a091
CYY
348 float rate;
349 char *end;
350
400efd3e
JB
351 if (argc < 1)
352 return 1;
353
354 NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(argv[0]), argv[0]);
355 argc--;
356 argv++;
357
7d01a091
CYY
358 if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) {
359 argv++;
360 argc--;
361
362 rate = strtod(argv[0], &end);
363 if (*end != '\0')
364 return 1;
365
366 NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10));
367 argv++;
368 argc--;
369 }
370
9560e43d
AN
371 container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
372 if (!container)
373 return -ENOBUFS;
374
375 if (argc > 1 && strcmp(argv[0], "vendor_sync") == 0) {
376 argv++;
377 argc--;
378 if (strcmp(argv[0], "on") == 0)
379 NLA_PUT_U8(msg,
380 NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 1);
381 else
382 NLA_PUT_U8(msg,
383 NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 0);
384 argv++;
385 argc--;
386 }
387 /* parse and put other NL80211_ATTR_MESH_SETUP elements here */
388
389 nla_nest_end(msg, container);
390
400efd3e
JB
391 if (!argc)
392 return 0;
393 return set_interface_meshparam(state, cb, msg, argc, argv);
394 nla_put_failure:
395 return -ENOBUFS;
396}
9560e43d
AN
397COMMAND(mesh, join, "<mesh ID> [mcast-rate <rate in Mbps>] [vendor_sync on|off]"
398 " [<param>=<value>]*",
400efd3e 399 NL80211_CMD_JOIN_MESH, 0, CIB_NETDEV, join_mesh,
7d01a091 400 "Join a mesh with the given mesh ID with mcast-rate and mesh parameters.");
400efd3e
JB
401
402static int leave_mesh(struct nl80211_state *state, struct nl_cb *cb,
403 struct nl_msg *msg, int argc, char **argv)
404{
405 if (argc)
406 return 1;
407
408 return 0;
409}
410COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh,
411 "Leave a mesh.");