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(station); 15 16 enum plink_state { 17 LISTEN, 18 OPN_SNT, 19 OPN_RCVD, 20 CNF_RCVD, 21 ESTAB, 22 HOLDING, 23 BLOCKED 24 }; 25 26 static void print_power_mode(struct nlattr *a) 27 { 28 enum nl80211_mesh_power_mode pm = nla_get_u32(a); 29 30 switch (pm) { 31 case NL80211_MESH_POWER_ACTIVE: 32 printf("ACTIVE"); 33 break; 34 case NL80211_MESH_POWER_LIGHT_SLEEP: 35 printf("LIGHT SLEEP"); 36 break; 37 case NL80211_MESH_POWER_DEEP_SLEEP: 38 printf("DEEP SLEEP"); 39 break; 40 default: 41 printf("UNKNOWN"); 42 break; 43 } 44 } 45 46 void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen) 47 { 48 int rate = 0; 49 char *pos = buf; 50 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; 51 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { 52 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, 53 [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 }, 54 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, 55 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, 56 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, 57 }; 58 59 if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, 60 bitrate_attr, rate_policy)) { 61 snprintf(buf, buflen, "failed to parse nested rate attributes!"); 62 return; 63 } 64 65 if (rinfo[NL80211_RATE_INFO_BITRATE32]) 66 rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]); 67 else if (rinfo[NL80211_RATE_INFO_BITRATE]) 68 rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]); 69 if (rate > 0) 70 pos += snprintf(pos, buflen - (pos - buf), 71 "%d.%d MBit/s", rate / 10, rate % 10); 72 73 if (rinfo[NL80211_RATE_INFO_MCS]) 74 pos += snprintf(pos, buflen - (pos - buf), 75 " MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS])); 76 if (rinfo[NL80211_RATE_INFO_VHT_MCS]) 77 pos += snprintf(pos, buflen - (pos - buf), 78 " VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS])); 79 if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH]) 80 pos += snprintf(pos, buflen - (pos - buf), " 40MHz"); 81 if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH]) 82 pos += snprintf(pos, buflen - (pos - buf), " 80MHz"); 83 if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH]) 84 pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz"); 85 if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH]) 86 pos += snprintf(pos, buflen - (pos - buf), " 160MHz"); 87 if (rinfo[NL80211_RATE_INFO_SHORT_GI]) 88 pos += snprintf(pos, buflen - (pos - buf), " short GI"); 89 if (rinfo[NL80211_RATE_INFO_VHT_NSS]) 90 pos += snprintf(pos, buflen - (pos - buf), 91 " VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS])); 92 } 93 94 static char *get_chain_signal(struct nlattr *attr_list) 95 { 96 struct nlattr *attr; 97 static char buf[64]; 98 char *cur = buf; 99 int i = 0, rem; 100 const char *prefix; 101 102 if (!attr_list) 103 return ""; 104 105 nla_for_each_nested(attr, attr_list, rem) { 106 if (i++ > 0) 107 prefix = ", "; 108 else 109 prefix = "["; 110 111 cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix, 112 (int8_t) nla_get_u8(attr)); 113 } 114 115 if (i) 116 snprintf(cur, sizeof(buf) - (cur - buf), "] "); 117 118 return buf; 119 } 120 121 static int print_sta_handler(struct nl_msg *msg, void *arg) 122 { 123 struct nlattr *tb[NL80211_ATTR_MAX + 1]; 124 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 125 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; 126 char mac_addr[20], state_name[10], dev[20]; 127 struct nl80211_sta_flag_update *sta_flags; 128 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { 129 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, 130 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, 131 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, 132 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, 133 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, 134 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, 135 [NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 }, 136 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED }, 137 [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED }, 138 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 }, 139 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 }, 140 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, 141 [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 }, 142 [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, 143 [NL80211_STA_INFO_STA_FLAGS] = 144 { .minlen = sizeof(struct nl80211_sta_flag_update) }, 145 [NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32}, 146 [NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32}, 147 [NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32}, 148 [NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED }, 149 [NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED }, 150 }; 151 char *chain; 152 153 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 154 genlmsg_attrlen(gnlh, 0), NULL); 155 156 /* 157 * TODO: validate the interface and mac address! 158 * Otherwise, there's a race condition as soon as 159 * the kernel starts sending station notifications. 160 */ 161 162 if (!tb[NL80211_ATTR_STA_INFO]) { 163 fprintf(stderr, "sta stats missing!\n"); 164 return NL_SKIP; 165 } 166 if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, 167 tb[NL80211_ATTR_STA_INFO], 168 stats_policy)) { 169 fprintf(stderr, "failed to parse nested attributes!\n"); 170 return NL_SKIP; 171 } 172 173 mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC])); 174 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev); 175 printf("Station %s (on %s)", mac_addr, dev); 176 177 if (sinfo[NL80211_STA_INFO_INACTIVE_TIME]) 178 printf("\n\tinactive time:\t%u ms", 179 nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME])); 180 if (sinfo[NL80211_STA_INFO_RX_BYTES]) 181 printf("\n\trx bytes:\t%u", 182 nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES])); 183 if (sinfo[NL80211_STA_INFO_RX_PACKETS]) 184 printf("\n\trx packets:\t%u", 185 nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS])); 186 if (sinfo[NL80211_STA_INFO_TX_BYTES]) 187 printf("\n\ttx bytes:\t%u", 188 nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES])); 189 if (sinfo[NL80211_STA_INFO_TX_PACKETS]) 190 printf("\n\ttx packets:\t%u", 191 nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS])); 192 if (sinfo[NL80211_STA_INFO_TX_RETRIES]) 193 printf("\n\ttx retries:\t%u", 194 nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES])); 195 if (sinfo[NL80211_STA_INFO_TX_FAILED]) 196 printf("\n\ttx failed:\t%u", 197 nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED])); 198 199 chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]); 200 if (sinfo[NL80211_STA_INFO_SIGNAL]) 201 printf("\n\tsignal: \t%d %sdBm", 202 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]), 203 chain); 204 205 chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]); 206 if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) 207 printf("\n\tsignal avg:\t%d %sdBm", 208 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]), 209 chain); 210 211 if (sinfo[NL80211_STA_INFO_T_OFFSET]) 212 printf("\n\tToffset:\t%lld us", 213 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET])); 214 215 if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { 216 char buf[100]; 217 218 parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf)); 219 printf("\n\ttx bitrate:\t%s", buf); 220 } 221 222 if (sinfo[NL80211_STA_INFO_RX_BITRATE]) { 223 char buf[100]; 224 225 parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf)); 226 printf("\n\trx bitrate:\t%s", buf); 227 } 228 229 if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) { 230 uint32_t thr; 231 232 thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]); 233 /* convert in Mbps but scale by 1000 to save kbps units */ 234 thr = thr * 1000 / 1024; 235 236 printf("\n\texpected throughput:\t%u.%uMbps", 237 thr / 1000, thr % 1000); 238 } 239 240 if (sinfo[NL80211_STA_INFO_LLID]) 241 printf("\n\tmesh llid:\t%d", 242 nla_get_u16(sinfo[NL80211_STA_INFO_LLID])); 243 if (sinfo[NL80211_STA_INFO_PLID]) 244 printf("\n\tmesh plid:\t%d", 245 nla_get_u16(sinfo[NL80211_STA_INFO_PLID])); 246 if (sinfo[NL80211_STA_INFO_PLINK_STATE]) { 247 switch (nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE])) { 248 case LISTEN: 249 strcpy(state_name, "LISTEN"); 250 break; 251 case OPN_SNT: 252 strcpy(state_name, "OPN_SNT"); 253 break; 254 case OPN_RCVD: 255 strcpy(state_name, "OPN_RCVD"); 256 break; 257 case CNF_RCVD: 258 strcpy(state_name, "CNF_RCVD"); 259 break; 260 case ESTAB: 261 strcpy(state_name, "ESTAB"); 262 break; 263 case HOLDING: 264 strcpy(state_name, "HOLDING"); 265 break; 266 case BLOCKED: 267 strcpy(state_name, "BLOCKED"); 268 break; 269 default: 270 strcpy(state_name, "UNKNOWN"); 271 break; 272 } 273 printf("\n\tmesh plink:\t%s", state_name); 274 } 275 if (sinfo[NL80211_STA_INFO_LOCAL_PM]) { 276 printf("\n\tmesh local PS mode:\t"); 277 print_power_mode(sinfo[NL80211_STA_INFO_LOCAL_PM]); 278 } 279 if (sinfo[NL80211_STA_INFO_PEER_PM]) { 280 printf("\n\tmesh peer PS mode:\t"); 281 print_power_mode(sinfo[NL80211_STA_INFO_PEER_PM]); 282 } 283 if (sinfo[NL80211_STA_INFO_NONPEER_PM]) { 284 printf("\n\tmesh non-peer PS mode:\t"); 285 print_power_mode(sinfo[NL80211_STA_INFO_NONPEER_PM]); 286 } 287 288 if (sinfo[NL80211_STA_INFO_STA_FLAGS]) { 289 sta_flags = (struct nl80211_sta_flag_update *) 290 nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]); 291 292 if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { 293 printf("\n\tauthorized:\t"); 294 if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED)) 295 printf("yes"); 296 else 297 printf("no"); 298 } 299 300 if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { 301 printf("\n\tauthenticated:\t"); 302 if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) 303 printf("yes"); 304 else 305 printf("no"); 306 } 307 308 if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { 309 printf("\n\tpreamble:\t"); 310 if (sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) 311 printf("short"); 312 else 313 printf("long"); 314 } 315 316 if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME)) { 317 printf("\n\tWMM/WME:\t"); 318 if (sta_flags->set & BIT(NL80211_STA_FLAG_WME)) 319 printf("yes"); 320 else 321 printf("no"); 322 } 323 324 if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP)) { 325 printf("\n\tMFP:\t\t"); 326 if (sta_flags->set & BIT(NL80211_STA_FLAG_MFP)) 327 printf("yes"); 328 else 329 printf("no"); 330 } 331 332 if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { 333 printf("\n\tTDLS peer:\t"); 334 if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER)) 335 printf("yes"); 336 else 337 printf("no"); 338 } 339 } 340 341 if (sinfo[NL80211_STA_INFO_CONNECTED_TIME]) 342 printf("\n\tconnected time:\t%u seconds", 343 nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME])); 344 345 printf("\n"); 346 return NL_SKIP; 347 } 348 349 static int handle_station_get(struct nl80211_state *state, 350 struct nl_cb *cb, 351 struct nl_msg *msg, 352 int argc, char **argv, 353 enum id_input id) 354 { 355 unsigned char mac_addr[ETH_ALEN]; 356 357 if (argc < 1) 358 return 1; 359 360 if (mac_addr_a2n(mac_addr, argv[0])) { 361 fprintf(stderr, "invalid mac address\n"); 362 return 2; 363 } 364 365 argc--; 366 argv++; 367 368 if (argc) 369 return 1; 370 371 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); 372 373 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL); 374 375 return 0; 376 nla_put_failure: 377 return -ENOBUFS; 378 } 379 COMMAND(station, get, "<MAC address>", 380 NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get, 381 "Get information for a specific station."); 382 COMMAND(station, del, "<MAC address>", 383 NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_get, 384 "Remove the given station entry (use with caution!)"); 385 386 static const struct cmd *station_set_plink; 387 static const struct cmd *station_set_vlan; 388 static const struct cmd *station_set_mesh_power_mode; 389 390 static const struct cmd *select_station_cmd(int argc, char **argv) 391 { 392 if (argc < 2) 393 return NULL; 394 if (strcmp(argv[1], "plink_action") == 0) 395 return station_set_plink; 396 if (strcmp(argv[1], "vlan") == 0) 397 return station_set_vlan; 398 if (strcmp(argv[1], "mesh_power_mode") == 0) 399 return station_set_mesh_power_mode; 400 return NULL; 401 } 402 403 static int handle_station_set_plink(struct nl80211_state *state, 404 struct nl_cb *cb, 405 struct nl_msg *msg, 406 int argc, char **argv, 407 enum id_input id) 408 { 409 unsigned char plink_action; 410 unsigned char mac_addr[ETH_ALEN]; 411 412 if (argc < 3) 413 return 1; 414 415 if (mac_addr_a2n(mac_addr, argv[0])) { 416 fprintf(stderr, "invalid mac address\n"); 417 return 2; 418 } 419 argc--; 420 argv++; 421 422 if (strcmp("plink_action", argv[0]) != 0) 423 return 1; 424 argc--; 425 argv++; 426 427 if (strcmp("open", argv[0]) == 0) 428 plink_action = NL80211_PLINK_ACTION_OPEN; 429 else if (strcmp("block", argv[0]) == 0) 430 plink_action = NL80211_PLINK_ACTION_BLOCK; 431 else { 432 fprintf(stderr, "plink action not supported\n"); 433 return 2; 434 } 435 argc--; 436 argv++; 437 438 if (argc) 439 return 1; 440 441 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); 442 NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action); 443 444 return 0; 445 nla_put_failure: 446 return -ENOBUFS; 447 } 448 COMMAND_ALIAS(station, set, "<MAC address> plink_action <open|block>", 449 NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_plink, 450 "Set mesh peer link action for this station (peer).", 451 select_station_cmd, station_set_plink); 452 453 static int handle_station_set_vlan(struct nl80211_state *state, 454 struct nl_cb *cb, 455 struct nl_msg *msg, 456 int argc, char **argv, 457 enum id_input id) 458 { 459 unsigned char mac_addr[ETH_ALEN]; 460 unsigned long sta_vlan = 0; 461 char *err = NULL; 462 463 if (argc < 3) 464 return 1; 465 466 if (mac_addr_a2n(mac_addr, argv[0])) { 467 fprintf(stderr, "invalid mac address\n"); 468 return 2; 469 } 470 argc--; 471 argv++; 472 473 if (strcmp("vlan", argv[0]) != 0) 474 return 1; 475 argc--; 476 argv++; 477 478 sta_vlan = strtoul(argv[0], &err, 0); 479 if (err && *err) { 480 fprintf(stderr, "invalid vlan id\n"); 481 return 2; 482 } 483 argc--; 484 argv++; 485 486 if (argc) 487 return 1; 488 489 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); 490 NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, sta_vlan); 491 492 return 0; 493 nla_put_failure: 494 return -ENOBUFS; 495 } 496 COMMAND_ALIAS(station, set, "<MAC address> vlan <ifindex>", 497 NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_vlan, 498 "Set an AP VLAN for this station.", 499 select_station_cmd, station_set_vlan); 500 501 static int handle_station_set_mesh_power_mode(struct nl80211_state *state, 502 struct nl_cb *cb, 503 struct nl_msg *msg, 504 int argc, char **argv, 505 enum id_input id) 506 { 507 unsigned char mesh_power_mode; 508 unsigned char mac_addr[ETH_ALEN]; 509 510 if (argc < 3) 511 return 1; 512 513 if (mac_addr_a2n(mac_addr, argv[0])) { 514 fprintf(stderr, "invalid mac address\n"); 515 return 2; 516 } 517 argc--; 518 argv++; 519 520 if (strcmp("mesh_power_mode", argv[0]) != 0) 521 return 1; 522 argc--; 523 argv++; 524 525 if (strcmp("active", argv[0]) == 0) 526 mesh_power_mode = NL80211_MESH_POWER_ACTIVE; 527 else if (strcmp("light", argv[0]) == 0) 528 mesh_power_mode = NL80211_MESH_POWER_LIGHT_SLEEP; 529 else if (strcmp("deep", argv[0]) == 0) 530 mesh_power_mode = NL80211_MESH_POWER_DEEP_SLEEP; 531 else { 532 fprintf(stderr, "unknown mesh power mode\n"); 533 return 2; 534 } 535 argc--; 536 argv++; 537 538 if (argc) 539 return 1; 540 541 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); 542 NLA_PUT_U32(msg, NL80211_ATTR_LOCAL_MESH_POWER_MODE, mesh_power_mode); 543 544 return 0; 545 nla_put_failure: 546 return -ENOBUFS; 547 } 548 COMMAND_ALIAS(station, set, "<MAC address> mesh_power_mode " 549 "<active|light|deep>", NL80211_CMD_SET_STATION, 0, CIB_NETDEV, 550 handle_station_set_mesh_power_mode, 551 "Set link-specific mesh power mode for this station", 552 select_station_cmd, station_set_mesh_power_mode); 553 554 static int handle_station_dump(struct nl80211_state *state, 555 struct nl_cb *cb, 556 struct nl_msg *msg, 557 int argc, char **argv, 558 enum id_input id) 559 { 560 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL); 561 return 0; 562 } 563 COMMAND(station, dump, NULL, 564 NL80211_CMD_GET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump, 565 "List all stations known, e.g. the AP on managed interfaces"); 566