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 14 SECTION(mesh); 15 16 17 typedef struct _any_t { 18 union { 19 uint32_t as_32; 20 int32_t as_s32; 21 uint16_t as_16; 22 uint8_t as_8; 23 } u; 24 } _any; 25 26 /* describes a mesh parameter */ 27 struct mesh_param_descr { 28 const char *name; 29 enum nl80211_meshconf_params mesh_param_num; 30 int (*nla_put_fn)(struct nl_msg*, int, _any*); 31 uint32_t (*parse_fn)(const char*, _any*); 32 void (*nla_print_fn)(struct nlattr *); 33 }; 34 35 /* utility functions for manipulating and printing u8/u16/u32 values and 36 * timesouts. */ 37 static int _my_nla_put_u8(struct nl_msg *n, int mesh_param_num, _any *value) 38 { 39 return nla_put(n, mesh_param_num, sizeof(uint8_t), &value->u.as_8); 40 } 41 42 static int _my_nla_put_u16(struct nl_msg *n, int mesh_param_num, _any *value) 43 { 44 return nla_put(n, mesh_param_num, sizeof(uint16_t), &value->u.as_16); 45 } 46 47 static int _my_nla_put_u32(struct nl_msg *n, int mesh_param_num, _any *value) 48 { 49 return nla_put(n, mesh_param_num, sizeof(uint32_t), &value->u.as_32); 50 } 51 52 static uint32_t _parse_u8(const char *str, _any *ret) 53 { 54 char *endptr = NULL; 55 unsigned long int v = strtoul(str, &endptr, 10); 56 if (*endptr != '\0') 57 return 0xff; 58 if (v > 0xff) 59 return 0xff; 60 ret->u.as_8 = (uint8_t)v; 61 return 0; 62 } 63 64 static uint32_t _parse_u8_as_bool(const char *str, _any *ret) 65 { 66 char *endptr = NULL; 67 unsigned long int v = strtoul(str, &endptr, 10); 68 if (*endptr != '\0') 69 return 0x1; 70 if (v > 0x1) 71 return 0x1; 72 ret->u.as_8 = (uint8_t)v; 73 return 0; 74 } 75 76 static uint32_t _parse_u16(const char *str, _any *ret) 77 { 78 char *endptr = NULL; 79 long int v = strtol(str, &endptr, 10); 80 if (*endptr != '\0') 81 return 0xffff; 82 if ((v < 0) || (v > 0xffff)) 83 return 0xffff; 84 ret->u.as_16 = (uint16_t)v; 85 return 0; 86 } 87 88 static uint32_t _parse_u32(const char *str, _any *ret) 89 { 90 char *endptr = NULL; 91 long long int v = strtoll(str, &endptr, 10); 92 if (*endptr != '\0') 93 return 0xffffffff; 94 if ((v < 0) || (v > 0xffffffff)) 95 return 0xffffffff; 96 ret->u.as_32 = (uint32_t)v; 97 return 0; 98 } 99 100 static uint32_t _parse_s32(const char *str, _any *ret) 101 { 102 char *endptr = NULL; 103 long int v = strtol(str, &endptr, 10); 104 if (*endptr != '\0') 105 return 0xffffffff; 106 if (v > 0xff) 107 return 0xffffffff; 108 ret->u.as_s32 = (int32_t)v; 109 return 0; 110 } 111 112 static uint32_t _parse_u32_power_mode(const char *str, _any *ret) 113 { 114 unsigned long int v; 115 116 /* Parse attribute for the name of power mode */ 117 if (!strcmp(str, "active")) 118 v = NL80211_MESH_POWER_ACTIVE; 119 else if (!strcmp(str, "light")) 120 v = NL80211_MESH_POWER_LIGHT_SLEEP; 121 else if (!strcmp(str, "deep")) 122 v = NL80211_MESH_POWER_DEEP_SLEEP; 123 else 124 return 0xff; 125 126 ret->u.as_32 = (uint32_t)v; 127 return 0; 128 } 129 130 static void _print_u8(struct nlattr *a) 131 { 132 printf("%d", nla_get_u8(a)); 133 } 134 135 static void _print_u16(struct nlattr *a) 136 { 137 printf("%d", nla_get_u16(a)); 138 } 139 140 static void _print_u16_timeout(struct nlattr *a) 141 { 142 printf("%d milliseconds", nla_get_u16(a)); 143 } 144 145 static void _print_u16_in_TUs(struct nlattr *a) 146 { 147 printf("%d TUs", nla_get_u16(a)); 148 } 149 150 static void _print_u32(struct nlattr *a) 151 { 152 printf("%d", nla_get_u32(a)); 153 } 154 155 static void _print_u32_timeout(struct nlattr *a) 156 { 157 printf("%u milliseconds", nla_get_u32(a)); 158 } 159 160 static void _print_u32_in_seconds(struct nlattr *a) 161 { 162 printf("%d seconds", nla_get_u32(a)); 163 } 164 165 static void _print_u32_in_TUs(struct nlattr *a) 166 { 167 printf("%d TUs", nla_get_u32(a)); 168 } 169 170 static void _print_u32_power_mode(struct nlattr *a) 171 { 172 unsigned long v = nla_get_u32(a); 173 174 switch (v) { 175 case NL80211_MESH_POWER_ACTIVE: 176 printf("active"); 177 break; 178 case NL80211_MESH_POWER_LIGHT_SLEEP: 179 printf("light"); 180 break; 181 case NL80211_MESH_POWER_DEEP_SLEEP: 182 printf("deep"); 183 break; 184 default: 185 printf("undefined"); 186 break; 187 } 188 } 189 190 static void _print_s32_in_dBm(struct nlattr *a) 191 { 192 printf("%d dBm", (int32_t) nla_get_u32(a)); 193 } 194 195 196 /* The current mesh parameters */ 197 const static struct mesh_param_descr _mesh_param_descrs[] = 198 { 199 {"mesh_retry_timeout", 200 NL80211_MESHCONF_RETRY_TIMEOUT, 201 _my_nla_put_u16, _parse_u16, _print_u16_timeout}, 202 {"mesh_confirm_timeout", 203 NL80211_MESHCONF_CONFIRM_TIMEOUT, 204 _my_nla_put_u16, _parse_u16, _print_u16_timeout}, 205 {"mesh_holding_timeout", 206 NL80211_MESHCONF_HOLDING_TIMEOUT, 207 _my_nla_put_u16, _parse_u16, _print_u16_timeout}, 208 {"mesh_max_peer_links", 209 NL80211_MESHCONF_MAX_PEER_LINKS, 210 _my_nla_put_u16, _parse_u16, _print_u16}, 211 {"mesh_max_retries", 212 NL80211_MESHCONF_MAX_RETRIES, 213 _my_nla_put_u8, _parse_u8, _print_u8}, 214 {"mesh_ttl", 215 NL80211_MESHCONF_TTL, 216 _my_nla_put_u8, _parse_u8, _print_u8}, 217 {"mesh_element_ttl", 218 NL80211_MESHCONF_ELEMENT_TTL, 219 _my_nla_put_u8, _parse_u8, _print_u8}, 220 {"mesh_auto_open_plinks", 221 NL80211_MESHCONF_AUTO_OPEN_PLINKS, 222 _my_nla_put_u8, _parse_u8_as_bool, _print_u8}, 223 {"mesh_hwmp_max_preq_retries", 224 NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, 225 _my_nla_put_u8, _parse_u8, _print_u8}, 226 {"mesh_path_refresh_time", 227 NL80211_MESHCONF_PATH_REFRESH_TIME, 228 _my_nla_put_u32, _parse_u32, _print_u32_timeout}, 229 {"mesh_min_discovery_timeout", 230 NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, 231 _my_nla_put_u16, _parse_u16, _print_u16_timeout}, 232 {"mesh_hwmp_active_path_timeout", 233 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, 234 _my_nla_put_u32, _parse_u32, _print_u32_in_TUs}, 235 {"mesh_hwmp_preq_min_interval", 236 NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, 237 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, 238 {"mesh_hwmp_net_diameter_traversal_time", 239 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, 240 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, 241 {"mesh_hwmp_rootmode", NL80211_MESHCONF_HWMP_ROOTMODE, 242 _my_nla_put_u8, _parse_u8, _print_u8}, 243 {"mesh_hwmp_rann_interval", NL80211_MESHCONF_HWMP_RANN_INTERVAL, 244 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, 245 {"mesh_gate_announcements", NL80211_MESHCONF_GATE_ANNOUNCEMENTS, 246 _my_nla_put_u8, _parse_u8, _print_u8}, 247 {"mesh_fwding", NL80211_MESHCONF_FORWARDING, 248 _my_nla_put_u8, _parse_u8_as_bool, _print_u8}, 249 {"mesh_sync_offset_max_neighor", 250 NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, 251 _my_nla_put_u32, _parse_u32, _print_u32}, 252 {"mesh_rssi_threshold", NL80211_MESHCONF_RSSI_THRESHOLD, 253 _my_nla_put_u32, _parse_s32, _print_s32_in_dBm}, 254 {"mesh_hwmp_active_path_to_root_timeout", 255 NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, 256 _my_nla_put_u32, _parse_u32, _print_u32_in_TUs}, 257 {"mesh_hwmp_root_interval", NL80211_MESHCONF_HWMP_ROOT_INTERVAL, 258 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, 259 {"mesh_hwmp_confirmation_interval", 260 NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, 261 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, 262 {"mesh_power_mode", NL80211_MESHCONF_POWER_MODE, 263 _my_nla_put_u32, _parse_u32_power_mode, _print_u32_power_mode}, 264 {"mesh_awake_window", NL80211_MESHCONF_AWAKE_WINDOW, 265 _my_nla_put_u16, _parse_u16, _print_u16_in_TUs}, 266 {"mesh_plink_timeout", NL80211_MESHCONF_PLINK_TIMEOUT, 267 _my_nla_put_u32, _parse_u32, _print_u32_in_seconds}, 268 }; 269 270 static void print_all_mesh_param_descr(void) 271 { 272 int i; 273 274 printf("Possible mesh parameters are:\n"); 275 276 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) 277 printf(" - %s\n", _mesh_param_descrs[i].name); 278 } 279 280 static const struct mesh_param_descr *find_mesh_param(const char *name) 281 { 282 int i; 283 284 /* Find out what mesh parameter we want to change. */ 285 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) { 286 if (strcmp(_mesh_param_descrs[i].name, name) == 0) 287 return _mesh_param_descrs + i; 288 } 289 290 print_all_mesh_param_descr(); 291 return NULL; 292 } 293 294 /* Setter */ 295 static int set_interface_meshparam(struct nl80211_state *state, 296 struct nl_cb *cb, 297 struct nl_msg *msg, 298 int argc, char **argv, 299 enum id_input id) 300 { 301 const struct mesh_param_descr *mdescr; 302 struct nlattr *container; 303 uint32_t ret; 304 int err; 305 306 container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS); 307 if (!container) 308 return -ENOBUFS; 309 310 if (!argc) 311 return 1; 312 313 while (argc) { 314 const char *name; 315 char *value; 316 _any any; 317 318 memset(&any, 0, sizeof(_any)); 319 320 name = argv[0]; 321 value = strchr(name, '='); 322 if (value) { 323 *value = '\0'; 324 value++; 325 argc--; 326 argv++; 327 } else { 328 /* backward compat -- accept w/o '=' */ 329 if (argc < 2) { 330 printf("Must specify a value for %s.\n", name); 331 return 2; 332 } 333 value = argv[1]; 334 argc -= 2; 335 argv += 2; 336 } 337 338 mdescr = find_mesh_param(name); 339 if (!mdescr) 340 return 2; 341 342 /* Parse the new value */ 343 ret = mdescr->parse_fn(value, &any); 344 if (ret != 0) { 345 if (mdescr->mesh_param_num 346 == NL80211_MESHCONF_POWER_MODE) 347 printf("%s must be set to active, light or " 348 "deep.\n", mdescr->name); 349 else 350 printf("%s must be set to a number " 351 "between 0 and %u\n", 352 mdescr->name, ret); 353 354 return 2; 355 } 356 357 err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any); 358 if (err) 359 return err; 360 } 361 nla_nest_end(msg, container); 362 363 return err; 364 } 365 366 COMMAND(set, mesh_param, "<param>=<value> [<param>=<value>]*", 367 NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam, 368 "Set mesh parameter (run command without any to see available ones)."); 369 370 /* Getter */ 371 static int print_mesh_param_handler(struct nl_msg *msg, void *arg) 372 { 373 const struct mesh_param_descr *mdescr = arg; 374 struct nlattr *attrs[NL80211_ATTR_MAX + 1]; 375 struct nlattr *parent_attr; 376 struct nlattr *mesh_params[NL80211_MESHCONF_ATTR_MAX + 1]; 377 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 378 379 /* locate NL80211_ATTR_MESH_PARAMS */ 380 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 381 genlmsg_attrlen(gnlh, 0), NULL); 382 parent_attr = attrs[NL80211_ATTR_MESH_PARAMS]; 383 if (!parent_attr) 384 return -EINVAL; 385 386 /* unpack the mesh parameters */ 387 if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX, 388 parent_attr, NULL)) 389 return -EINVAL; 390 391 if (!mdescr) { 392 int i; 393 394 for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) { 395 mdescr = &_mesh_param_descrs[i]; 396 printf("%s = ", mdescr->name); 397 mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]); 398 printf("\n"); 399 } 400 return NL_SKIP; 401 } 402 403 /* print out the mesh parameter */ 404 mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]); 405 printf("\n"); 406 return NL_SKIP; 407 } 408 409 static int get_interface_meshparam(struct nl80211_state *state, 410 struct nl_cb *cb, 411 struct nl_msg *msg, 412 int argc, char **argv, 413 enum id_input id) 414 { 415 const struct mesh_param_descr *mdescr = NULL; 416 417 if (argc > 1) 418 return 1; 419 420 if (argc == 1) { 421 mdescr = find_mesh_param(argv[0]); 422 if (!mdescr) 423 return 2; 424 } 425 426 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, 427 print_mesh_param_handler, (void *)mdescr); 428 return 0; 429 } 430 431 COMMAND(get, mesh_param, "[<param>]", 432 NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam, 433 "Retrieve mesh parameter (run command without any to see available ones)."); 434 435 static int join_mesh(struct nl80211_state *state, struct nl_cb *cb, 436 struct nl_msg *msg, int argc, char **argv, 437 enum id_input id) 438 { 439 struct nlattr *container; 440 float rate; 441 unsigned char rates[NL80211_MAX_SUPP_RATES]; 442 int bintval, dtim_period, i, n_rates = 0; 443 char *end, *value = NULL, *sptr = NULL; 444 unsigned long freq = 0; 445 static const struct { 446 const char *name; 447 unsigned int width; 448 int freq1_diff; 449 int chantype; /* for older kernel */ 450 } *chanmode_selected = NULL, chanmode[] = { 451 { .name = "HT20", 452 .width = NL80211_CHAN_WIDTH_20, 453 .freq1_diff = 0, 454 .chantype = NL80211_CHAN_HT20 }, 455 { .name = "HT40+", 456 .width = NL80211_CHAN_WIDTH_40, 457 .freq1_diff = 10, 458 .chantype = NL80211_CHAN_HT40PLUS }, 459 { .name = "HT40-", 460 .width = NL80211_CHAN_WIDTH_40, 461 .freq1_diff = -10, 462 .chantype = NL80211_CHAN_HT40MINUS }, 463 { .name = "NOHT", 464 .width = NL80211_CHAN_WIDTH_20_NOHT, 465 .freq1_diff = 0, 466 .chantype = NL80211_CHAN_NO_HT }, 467 }; 468 469 if (argc < 1) 470 return 1; 471 472 NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(argv[0]), argv[0]); 473 argc--; 474 argv++; 475 476 /* freq */ 477 if (argc > 1 && strcmp(argv[0], "freq") == 0) { 478 argv++; 479 argc--; 480 481 freq = strtoul(argv[0], &end, 10); 482 if (*end != '\0') 483 return 1; 484 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); 485 486 argv++; 487 argc--; 488 } 489 490 /* channel type */ 491 if (argc) { 492 for (i = 0; i < ARRAY_SIZE(chanmode); i++) { 493 if (strcasecmp(chanmode[i].name, argv[0]) == 0) { 494 chanmode_selected = &chanmode[i]; 495 break; 496 } 497 } 498 499 if (chanmode_selected) { 500 NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, 501 chanmode_selected->width); 502 NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, 503 freq + chanmode_selected->freq1_diff); 504 if (chanmode_selected->chantype != -1) 505 NLA_PUT_U32(msg, 506 NL80211_ATTR_WIPHY_CHANNEL_TYPE, 507 chanmode_selected->chantype); 508 509 argv++; 510 argc--; 511 } 512 } 513 514 /* basic rates */ 515 if (argc > 1 && strcmp(argv[0], "basic-rates") == 0) { 516 argv++; 517 argc--; 518 519 value = strtok_r(argv[0], ",", &sptr); 520 521 while (value && n_rates < NL80211_MAX_SUPP_RATES) { 522 rate = strtod(value, &end); 523 rates[n_rates] = rate * 2; 524 525 /* filter out suspicious values */ 526 if (*end != '\0' || !rates[n_rates] || 527 rate*2 != rates[n_rates]) 528 return 1; 529 530 n_rates++; 531 value = strtok_r(NULL, ",", &sptr); 532 } 533 534 NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, n_rates, rates); 535 argv++; 536 argc--; 537 } 538 539 /* multicast rate */ 540 if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) { 541 argv++; 542 argc--; 543 544 rate = strtod(argv[0], &end); 545 if (*end != '\0') 546 return 1; 547 548 NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10)); 549 argv++; 550 argc--; 551 } 552 553 if (argc > 1 && strcmp(argv[0], "beacon-interval") == 0) { 554 argc--; 555 argv++; 556 557 bintval = strtoul(argv[0], &end, 10); 558 if (*end != '\0') 559 return 1; 560 561 NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, bintval); 562 argv++; 563 argc--; 564 } 565 566 if (argc > 1 && strcmp(argv[0], "dtim-period") == 0) { 567 argc--; 568 argv++; 569 570 dtim_period = strtoul(argv[0], &end, 10); 571 if (*end != '\0') 572 return 1; 573 574 NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); 575 argv++; 576 argc--; 577 } 578 579 container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP); 580 if (!container) 581 return -ENOBUFS; 582 583 if (argc > 1 && strcmp(argv[0], "vendor_sync") == 0) { 584 argv++; 585 argc--; 586 if (strcmp(argv[0], "on") == 0) 587 NLA_PUT_U8(msg, 588 NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 1); 589 else 590 NLA_PUT_U8(msg, 591 NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 0); 592 argv++; 593 argc--; 594 } 595 /* parse and put other NL80211_ATTR_MESH_SETUP elements here */ 596 597 nla_nest_end(msg, container); 598 599 if (!argc) 600 return 0; 601 return set_interface_meshparam(state, cb, msg, argc, argv, id); 602 nla_put_failure: 603 return -ENOBUFS; 604 } 605 COMMAND(mesh, join, "<mesh ID> [[freq <freq in MHz> <HT20|HT40+|HT40-|NOHT>]" 606 " [basic-rates <rate in Mbps,rate2,...>]], [mcast-rate <rate in Mbps>]" 607 " [beacon-interval <time in TUs>] [dtim-period <value>]" 608 " [vendor_sync on|off] [<param>=<value>]*", 609 NL80211_CMD_JOIN_MESH, 0, CIB_NETDEV, join_mesh, 610 "Join a mesh with the given mesh ID with frequency, basic-rates,\n" 611 "mcast-rate and mesh parameters. Basic-rates are applied only if\n" 612 "frequency is provided."); 613 614 static int leave_mesh(struct nl80211_state *state, struct nl_cb *cb, 615 struct nl_msg *msg, int argc, char **argv, 616 enum id_input id) 617 { 618 if (argc) 619 return 1; 620 621 return 0; 622 } 623 COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh, 624 "Leave a mesh."); 625