1 /* 2 Copyright 2021 Jo-Philipp Wich <jo@mein.io> 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /** 18 * # Wireless Netlink 19 * 20 * The `nl80211` module provides functions for interacting with the nl80211 netlink interface 21 * for wireless networking configuration and management. 22 * 23 * Functions can be individually imported and directly accessed using the 24 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 25 * syntax: 26 * 27 * ```javascript 28 * import { error, request, listener, waitfor, const } from 'nl80211'; 29 * 30 * // Send a nl80211 request 31 * let response = request(const.NL80211_CMD_GET_WIPHY, 0, { wiphy: 0 }); 32 * 33 * // Create a listener for wireless events 34 * let wifiListener = listener((msg) => { 35 * print('Received wireless event:', msg, '\n'); 36 * }, [const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_DEL_INTERFACE]); 37 * 38 * // Wait for a specific nl80211 event 39 * let event = waitfor([const.NL80211_CMD_NEW_SCAN_RESULTS], 5000); 40 * if (event) 41 * print('Received scan results:', event.msg, '\n'); 42 * ``` 43 * 44 * Alternatively, the module namespace can be imported 45 * using a wildcard import statement: 46 * 47 * ```javascript 48 * import * as nl80211 from 'nl80211'; 49 * 50 * // Send a nl80211 request 51 * let response = nl80211.request(nl80211.const.NL80211_CMD_GET_WIPHY, 0, { wiphy: 0 }); 52 * 53 * // Create a listener for wireless events 54 * let listener = nl80211.listener((msg) => { 55 * print('Received wireless event:', msg, '\n'); 56 * }, [nl80211.const.NL80211_CMD_NEW_INTERFACE, nl80211.const.NL80211_CMD_DEL_INTERFACE]); 57 * ``` 58 * 59 * Additionally, the nl80211 module namespace may also be imported by invoking 60 * the `ucode` interpreter with the `-lnl80211` switch. 61 * 62 * @module nl80211 63 */ 64 65 #include <stdio.h> 66 #include <stdint.h> 67 #include <stdbool.h> 68 #include <stdarg.h> 69 #include <unistd.h> 70 #include <errno.h> 71 #include <string.h> 72 #include <limits.h> 73 #include <math.h> 74 #include <assert.h> 75 #include <fcntl.h> 76 #include <poll.h> 77 78 #include <net/if.h> 79 #include <netinet/ether.h> 80 #include <arpa/inet.h> 81 #include <netlink/msg.h> 82 #include <netlink/attr.h> 83 #include <netlink/socket.h> 84 #include <netlink/genl/genl.h> 85 #include <netlink/genl/family.h> 86 #include <netlink/genl/ctrl.h> 87 88 #include <linux/nl80211.h> 89 #include <linux/ieee80211.h> 90 #include <linux/mac80211_hwsim.h> 91 #include <libubox/uloop.h> 92 93 #include "ucode/module.h" 94 #include "ucode/platform.h" 95 96 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 97 98 #define err_return(code, ...) do { set_error(code, __VA_ARGS__); return NULL; } while(0) 99 100 /* Modified downstream nl80211.h headers may disable certain unsupported 101 * attributes by setting the corresponding defines to 0x10000 without having 102 * to patch the attribute dictionaries within this file. */ 103 104 #define NL80211_ATTR_NOT_IMPLEMENTED 0x10000 105 106 #define NL80211_CMDS_BITMAP_SIZE DIV_ROUND_UP(NL80211_CMD_MAX + 1, 32) 107 108 static struct { 109 int code; 110 char *msg; 111 } last_error; 112 113 __attribute__((format(printf, 2, 3))) static void 114 set_error(int errcode, const char *fmt, ...) { 115 va_list ap; 116 117 if (errcode == -(NLE_MAX + 1)) 118 return; 119 120 free(last_error.msg); 121 122 last_error.code = errcode; 123 last_error.msg = NULL; 124 125 if (fmt) { 126 va_start(ap, fmt); 127 xvasprintf(&last_error.msg, fmt, ap); 128 va_end(ap); 129 } 130 } 131 132 static uc_resource_type_t *listener_type; 133 static uc_value_t *listener_registry; 134 static uc_vm_t *listener_vm; 135 136 typedef struct { 137 uint32_t cmds[NL80211_CMDS_BITMAP_SIZE]; 138 size_t index; 139 } uc_nl_listener_t; 140 141 static bool 142 uc_nl_parse_u32(uc_value_t *val, uint32_t *n) 143 { 144 uint64_t u; 145 146 u = ucv_to_unsigned(val); 147 148 if (errno != 0 || u > UINT32_MAX) 149 return false; 150 151 *n = (uint32_t)u; 152 153 return true; 154 } 155 156 static bool 157 uc_nl_parse_s32(uc_value_t *val, uint32_t *n) 158 { 159 int64_t i; 160 161 i = ucv_to_integer(val); 162 163 if (errno != 0 || i < INT32_MIN || i > INT32_MAX) 164 return false; 165 166 *n = (uint32_t)i; 167 168 return true; 169 } 170 171 static bool 172 uc_nl_parse_u64(uc_value_t *val, uint64_t *n) 173 { 174 *n = ucv_to_unsigned(val); 175 176 return (errno == 0); 177 } 178 179 static bool 180 uc_nl_parse_ipaddr(uc_vm_t *vm, uc_value_t *val, struct in_addr *in) 181 { 182 char *s = ucv_to_string(vm, val); 183 bool valid = true; 184 185 if (!s) 186 return false; 187 188 valid = (inet_pton(AF_INET, s, in) == 1); 189 190 free(s); 191 192 return valid; 193 } 194 195 typedef enum { 196 DT_FLAG, 197 DT_BOOL, 198 DT_U8, 199 DT_S8, 200 DT_U16, 201 DT_U32, 202 DT_S32, 203 DT_U64, 204 DT_STRING, 205 DT_NETDEV, 206 DT_LLADDR, 207 DT_INADDR, 208 DT_NESTED, 209 DT_HT_MCS, 210 DT_HT_CAP, 211 DT_VHT_MCS, 212 DT_HE_MCS, 213 DT_IE, 214 } uc_nl_attr_datatype_t; 215 216 enum { 217 DF_NO_SET = (1 << 0), 218 DF_MULTIPLE = (1 << 1), 219 DF_AUTOIDX = (1 << 2), 220 DF_TYPEIDX = (1 << 3), 221 DF_OFFSET1 = (1 << 4), 222 DF_ARRAY = (1 << 5), 223 DF_BINARY = (1 << 6), 224 DF_RELATED = (1 << 7), 225 DF_REPEATED = (1 << 8), 226 }; 227 228 typedef struct uc_nl_attr_spec { 229 size_t attr; 230 const char *key; 231 uc_nl_attr_datatype_t type; 232 uint32_t flags; 233 const void *auxdata; 234 } uc_nl_attr_spec_t; 235 236 typedef struct uc_nl_nested_spec { 237 size_t headsize; 238 size_t nattrs; 239 const uc_nl_attr_spec_t attrs[]; 240 } uc_nl_nested_spec_t; 241 242 #define SIZE(type) (void *)(uintptr_t)sizeof(struct type) 243 #define MEMBER(type, field) (void *)(uintptr_t)offsetof(struct type, field) 244 #define ATTRID(id) (void *)(uintptr_t)(id) 245 246 static const uc_nl_nested_spec_t nl80211_cqm_nla = { 247 .headsize = 0, 248 .nattrs = 5, 249 .attrs = { 250 { NL80211_ATTR_CQM_PKT_LOSS_EVENT, "cqm_pkt_loss_event", DT_U32, 0, NULL }, 251 { NL80211_ATTR_CQM_RSSI_HYST, "cqm_rssi_hyst", DT_U32, 0, NULL }, 252 { NL80211_ATTR_CQM_RSSI_LEVEL, "cqm_rssi_level", DT_S32, 0, NULL }, 253 { NL80211_ATTR_CQM_RSSI_THOLD, "cqm_rssi_thold", DT_U32, 0, NULL }, 254 { NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, "cqm_rssi_threshold_event", DT_U32, 0, NULL }, 255 } 256 }; 257 258 static const uc_nl_nested_spec_t nl80211_ftm_responder_stats_nla = { 259 .headsize = 0, 260 .nattrs = 9, 261 .attrs = { 262 { NL80211_FTM_STATS_SUCCESS_NUM, "success_num", DT_U32, 0, NULL }, 263 { NL80211_FTM_STATS_PARTIAL_NUM, "partial_num", DT_U32, 0, NULL }, 264 { NL80211_FTM_STATS_FAILED_NUM, "failed_num", DT_U32, 0, NULL }, 265 { NL80211_FTM_STATS_ASAP_NUM, "asap_num", DT_U32, 0, NULL }, 266 { NL80211_FTM_STATS_NON_ASAP_NUM, "non_asap_num", DT_U32, 0, NULL }, 267 { NL80211_FTM_STATS_TOTAL_DURATION_MSEC, "total_duration_msec", DT_U64, 0, NULL }, 268 { NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM, "unknown_triggers_num", DT_U32, 0, NULL }, 269 { NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM, "reschedule_requests_num", DT_U32, 0, NULL }, 270 { NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM, "out_of_window_triggers_num", DT_U32, 0, NULL }, 271 } 272 }; 273 274 static const uc_nl_nested_spec_t nl80211_ifcomb_limit_types_nla = { 275 .headsize = 0, 276 .nattrs = 12, 277 .attrs = { 278 { 1, "ibss", DT_FLAG, 0, NULL }, 279 { 2, "managed", DT_FLAG, 0, NULL }, 280 { 3, "ap", DT_FLAG, 0, NULL }, 281 { 4, "ap_vlan", DT_FLAG, 0, NULL }, 282 { 5, "wds", DT_FLAG, 0, NULL }, 283 { 6, "monitor", DT_FLAG, 0, NULL }, 284 { 7, "mesh_point", DT_FLAG, 0, NULL }, 285 { 8, "p2p_client", DT_FLAG, 0, NULL }, 286 { 9, "p2p_go", DT_FLAG, 0, NULL }, 287 { 10, "p2p_device", DT_FLAG, 0, NULL }, 288 { 11, "outside_bss_context", DT_FLAG, 0, NULL }, 289 { 12, "nan", DT_FLAG, 0, NULL }, 290 } 291 }; 292 293 static const uc_nl_nested_spec_t nl80211_ifcomb_limits_nla = { 294 .headsize = 0, 295 .nattrs = 2, 296 .attrs = { 297 { NL80211_IFACE_LIMIT_TYPES, "types", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, 298 { NL80211_IFACE_LIMIT_MAX, "max", DT_U32, 0, NULL }, 299 } 300 }; 301 302 static const uc_nl_nested_spec_t nl80211_ifcomb_nla = { 303 .headsize = 0, 304 .nattrs = 5, 305 .attrs = { 306 { NL80211_IFACE_COMB_LIMITS, "limits", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_ifcomb_limits_nla }, 307 { NL80211_IFACE_COMB_MAXNUM, "maxnum", DT_U32, 0, NULL }, 308 { NL80211_IFACE_COMB_STA_AP_BI_MATCH, "sta_ap_bi_match", DT_FLAG, 0, NULL }, 309 { NL80211_IFACE_COMB_NUM_CHANNELS, "num_channels", DT_U32, 0, NULL }, 310 { NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, "radar_detect_widths", DT_U32, 0, NULL }, 311 } 312 }; 313 314 static const uc_nl_nested_spec_t nl80211_ftm_responder_nla = { 315 .headsize = 0, 316 .nattrs = 3, 317 .attrs = { 318 { NL80211_FTM_RESP_ATTR_ENABLED, "enabled", DT_FLAG, 0, NULL }, 319 { NL80211_FTM_RESP_ATTR_LCI, "lci", DT_STRING, DF_BINARY, NULL }, 320 { NL80211_FTM_RESP_ATTR_CIVICLOC, "civicloc", DT_STRING, DF_BINARY, NULL }, 321 } 322 }; 323 324 static const uc_nl_nested_spec_t nl80211_keys_nla = { 325 .headsize = 0, 326 .nattrs = 4, 327 .attrs = { 328 { NL80211_KEY_DEFAULT, "default", DT_FLAG, 0, NULL }, 329 { NL80211_KEY_IDX, "idx", DT_U8, 0, NULL }, 330 { NL80211_KEY_CIPHER, "cipher", DT_U32, 0, NULL }, 331 { NL80211_KEY_DATA, "data", DT_STRING, DF_BINARY, NULL }, 332 } 333 }; 334 335 static const uc_nl_nested_spec_t nl80211_mesh_params_nla = { 336 .headsize = 0, 337 .nattrs = 29, 338 .attrs = { 339 { NL80211_MESHCONF_RETRY_TIMEOUT, "retry_timeout", DT_U16, 0, NULL }, 340 { NL80211_MESHCONF_CONFIRM_TIMEOUT, "confirm_timeout", DT_U16, 0, NULL }, 341 { NL80211_MESHCONF_HOLDING_TIMEOUT, "holding_timeout", DT_U16, 0, NULL }, 342 { NL80211_MESHCONF_MAX_PEER_LINKS, "max_peer_links", DT_U16, 0, NULL }, 343 { NL80211_MESHCONF_MAX_RETRIES, "max_retries", DT_U8, 0, NULL }, 344 { NL80211_MESHCONF_TTL, "ttl", DT_U8, 0, NULL }, 345 { NL80211_MESHCONF_ELEMENT_TTL, "element_ttl", DT_U8, 0, NULL }, 346 { NL80211_MESHCONF_AUTO_OPEN_PLINKS, "auto_open_plinks", DT_BOOL, 0, NULL }, 347 { NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, "hwmp_max_preq_retries", DT_U8, 0, NULL }, 348 { NL80211_MESHCONF_PATH_REFRESH_TIME, "path_refresh_time", DT_U32, 0, NULL }, 349 { NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, "min_discovery_timeout", DT_U16, 0, NULL }, 350 { NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, "hwmp_active_path_timeout", DT_U32, 0, NULL }, 351 { NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, "hwmp_preq_min_interval", DT_U16, 0, NULL }, 352 { NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, "hwmp_net_diam_trvs_time", DT_U16, 0, NULL }, 353 { NL80211_MESHCONF_HWMP_ROOTMODE, "hwmp_rootmode", DT_U8, 0, NULL }, 354 { NL80211_MESHCONF_HWMP_RANN_INTERVAL, "hwmp_rann_interval", DT_U16, 0, NULL }, 355 { NL80211_MESHCONF_GATE_ANNOUNCEMENTS, "gate_announcements", DT_U8, 0, NULL }, 356 { NL80211_MESHCONF_FORWARDING, "forwarding", DT_BOOL, 0, NULL }, 357 { NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, "sync_offset_max_neighbor", DT_U32, 0, NULL }, 358 { NL80211_MESHCONF_RSSI_THRESHOLD, "rssi_threshold", DT_S32, 0, NULL }, 359 { NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, "hwmp_path_to_root_timeout", DT_U32, 0, NULL }, 360 { NL80211_MESHCONF_HWMP_ROOT_INTERVAL, "hwmp_root_interval", DT_U16, 0, NULL }, 361 { NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, "hwmp_confirmation_interval", DT_U16, 0, NULL }, 362 { NL80211_MESHCONF_POWER_MODE, "power_mode", DT_U32, 0, NULL }, 363 { NL80211_MESHCONF_AWAKE_WINDOW, "awake_window", DT_U16, 0, NULL }, 364 { NL80211_MESHCONF_PLINK_TIMEOUT, "plink_timeout", DT_U32, 0, NULL }, 365 { NL80211_MESHCONF_CONNECTED_TO_GATE, "connected_to_gate", DT_BOOL, 0, NULL }, 366 { NL80211_MESHCONF_NOLEARN, "nolearn", DT_BOOL, 0, NULL }, 367 { NL80211_MESHCONF_CONNECTED_TO_AS, "connected_to_as", DT_BOOL, 0, NULL }, 368 } 369 }; 370 371 static const uc_nl_nested_spec_t nl80211_mesh_setup_nla = { 372 .headsize = 0, 373 .nattrs = 1, 374 .attrs = { 375 { NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, "enable_vendor_sync", DT_BOOL, 0, NULL }, 376 } 377 }; 378 379 static const uc_nl_nested_spec_t nl80211_mntr_flags_nla = { 380 .headsize = 0, 381 .nattrs = 7, 382 .attrs = { 383 { NL80211_MNTR_FLAG_FCSFAIL, "fcsfail", DT_FLAG, 0, NULL }, 384 { NL80211_MNTR_FLAG_PLCPFAIL, "plcpfail", DT_FLAG, 0, NULL }, 385 { NL80211_MNTR_FLAG_CONTROL, "control", DT_FLAG, 0, NULL }, 386 { NL80211_MNTR_FLAG_OTHER_BSS, "other_bss", DT_FLAG, 0, NULL }, 387 { NL80211_MNTR_FLAG_COOK_FRAMES, "cook_frames", DT_FLAG, 0, NULL }, 388 { NL80211_MNTR_FLAG_ACTIVE, "active", DT_FLAG, 0, NULL }, 389 { NL80211_MNTR_FLAG_SKIP_TX, "skip_tx", DT_FLAG, 0, NULL }, 390 } 391 }; 392 393 static const uc_nl_nested_spec_t nl80211_nan_func_srf_nla = { 394 .headsize = 0, 395 .nattrs = 4, 396 .attrs = { 397 { NL80211_NAN_SRF_INCLUDE, "include", DT_FLAG, 0, NULL }, 398 { NL80211_NAN_SRF_BF_IDX, "bf_idx", DT_U8, 0, NULL }, 399 { NL80211_NAN_SRF_BF, "bf", DT_STRING, DF_BINARY, NULL }, 400 { NL80211_NAN_SRF_MAC_ADDRS, "mac_addrs", DT_LLADDR, DF_MULTIPLE|DF_AUTOIDX, NULL }, 401 } 402 }; 403 404 static const uc_nl_nested_spec_t nl80211_nan_func_nla = { 405 .headsize = 0, 406 .nattrs = 16, 407 .attrs = { 408 { NL80211_NAN_FUNC_TYPE, "type", DT_U8, 0, NULL }, 409 { NL80211_NAN_FUNC_SERVICE_ID, "service_id", DT_STRING, DF_BINARY, NULL }, 410 { NL80211_NAN_FUNC_PUBLISH_TYPE, "publish_type", DT_U8, 0, NULL }, 411 { NL80211_NAN_FUNC_PUBLISH_BCAST, "publish_bcast", DT_FLAG, 0, NULL }, 412 { NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE, "subscribe_active", DT_FLAG, 0, NULL }, 413 { NL80211_NAN_FUNC_FOLLOW_UP_ID, "follow_up_id", DT_U8, 0, NULL }, 414 { NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID, "follow_up_req_id", DT_U8, 0, NULL }, 415 { NL80211_NAN_FUNC_FOLLOW_UP_DEST, "follow_up_dest", DT_LLADDR, 0, NULL }, 416 { NL80211_NAN_FUNC_CLOSE_RANGE, "close_range", DT_FLAG, 0, NULL }, 417 { NL80211_NAN_FUNC_TTL, "ttl", DT_U32, 0, NULL }, 418 { NL80211_NAN_FUNC_SERVICE_INFO, "service_info", DT_STRING, 0, NULL }, 419 { NL80211_NAN_FUNC_SRF, "srf", DT_NESTED, 0, &nl80211_nan_func_srf_nla }, 420 { NL80211_NAN_FUNC_RX_MATCH_FILTER, "rx_match_filter", DT_STRING, DF_MULTIPLE|DF_AUTOIDX, NULL }, 421 { NL80211_NAN_FUNC_TX_MATCH_FILTER, "tx_match_filter", DT_STRING, DF_MULTIPLE|DF_AUTOIDX, NULL }, 422 { NL80211_NAN_FUNC_INSTANCE_ID, "instance_id", DT_U8, 0, NULL }, 423 { NL80211_NAN_FUNC_TERM_REASON, "term_reason", DT_U8, 0, NULL }, 424 } 425 }; 426 427 static const uc_nl_nested_spec_t nl80211_peer_measurements_type_ftm_nla = { 428 .headsize = 0, 429 .nattrs = 13, 430 .attrs = { 431 { NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, "num_bursts_exp", DT_U8, 0, NULL }, 432 { NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, "burst_period", DT_U16, 0, NULL }, 433 { NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES, "num_ftmr_retries", DT_U8, 0, NULL }, 434 { NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, "burst_duration", DT_U8, 0, NULL }, 435 { NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, "ftms_per_burst", DT_U8, 0, NULL }, 436 { NL80211_PMSR_FTM_REQ_ATTR_ASAP, "asap", DT_FLAG, 0, NULL }, 437 { NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC, "request_civicloc", DT_FLAG, 0, NULL }, 438 { NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI, "request_lci", DT_FLAG, 0, NULL }, 439 { NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED, "trigger_based", DT_FLAG, 0, NULL }, 440 { NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, "preamble", DT_U32, 0, NULL }, 441 { NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED, "non_trigger_based", DT_FLAG, 0, NULL }, 442 { NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK, "lmr_feedback", DT_FLAG, 0, NULL }, 443 { NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR, "bss_color", DT_U8, 0, NULL }, 444 } 445 }; 446 447 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_req_data_nla = { 448 .headsize = 0, 449 .nattrs = 2, 450 .attrs = { 451 { NL80211_PMSR_TYPE_FTM, "ftm", DT_NESTED, 0, &nl80211_peer_measurements_type_ftm_nla }, 452 { NL80211_PMSR_REQ_ATTR_GET_AP_TSF, "get_ap_tsf", DT_FLAG, 0, NULL }, 453 } 454 }; 455 456 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_req_nla = { 457 .headsize = 0, 458 .nattrs = 1, 459 .attrs = { 460 { NL80211_PMSR_REQ_ATTR_DATA, "data", DT_NESTED, 0, &nl80211_peer_measurements_peers_req_data_nla }, 461 } 462 }; 463 464 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_chan_nla = { 465 .headsize = 0, 466 .nattrs = 4, 467 .attrs = { 468 { NL80211_ATTR_WIPHY_FREQ, "freq", DT_U32, 0, NULL }, 469 { NL80211_ATTR_CENTER_FREQ1, "center_freq1", DT_U32, 0, NULL }, 470 { NL80211_ATTR_CENTER_FREQ2, "center_freq2", DT_U32, 0, NULL }, 471 { NL80211_ATTR_CHANNEL_WIDTH, "channel_width", DT_U32, 0, NULL }, 472 } 473 }; 474 475 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_resp_data_nla = { 476 .headsize = 0, 477 .nattrs = 1, 478 .attrs = { 479 { NL80211_PMSR_TYPE_FTM, "ftm", DT_NESTED, 0, &nl80211_peer_measurements_type_ftm_nla }, 480 } 481 }; 482 483 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_resp_nla = { 484 .headsize = 0, 485 .nattrs = 5, 486 .attrs = { 487 { NL80211_PMSR_RESP_ATTR_STATUS, "status", DT_U32, 0, NULL }, 488 { NL80211_PMSR_RESP_ATTR_HOST_TIME, "host_time", DT_U64, 0, NULL }, 489 { NL80211_PMSR_RESP_ATTR_AP_TSF, "ap_tsf", DT_U64, 0, NULL }, 490 { NL80211_PMSR_RESP_ATTR_FINAL, "final", DT_FLAG, 0, NULL }, 491 { NL80211_PMSR_RESP_ATTR_DATA, "data", DT_NESTED, 0, &nl80211_peer_measurements_peers_resp_data_nla }, 492 } 493 }; 494 495 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_nla = { 496 .headsize = 0, 497 .nattrs = 4, 498 .attrs = { 499 { NL80211_PMSR_PEER_ATTR_ADDR, "addr", DT_LLADDR, 0, NULL }, 500 { NL80211_PMSR_PEER_ATTR_REQ, "req", DT_NESTED, 0, &nl80211_peer_measurements_peers_req_nla }, 501 { NL80211_PMSR_PEER_ATTR_CHAN, "chan", DT_NESTED, 0, &nl80211_peer_measurements_peers_chan_nla }, 502 { NL80211_PMSR_PEER_ATTR_RESP, "resp", DT_NESTED, 0, &nl80211_peer_measurements_peers_resp_nla } 503 } 504 }; 505 506 static const uc_nl_nested_spec_t nl80211_peer_measurements_nla = { 507 .headsize = 0, 508 .nattrs = 1, 509 .attrs = { 510 { NL80211_PMSR_ATTR_PEERS, "peers", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_peer_measurements_peers_nla }, 511 } 512 }; 513 514 static const uc_nl_nested_spec_t nl80211_reg_rules_nla = { 515 .headsize = 0, 516 .nattrs = 7, 517 .attrs = { 518 { NL80211_ATTR_REG_RULE_FLAGS, "reg_rule_flags", DT_U32, 0, NULL }, 519 { NL80211_ATTR_FREQ_RANGE_START, "freq_range_start", DT_U32, 0, NULL }, 520 { NL80211_ATTR_FREQ_RANGE_END, "freq_range_end", DT_U32, 0, NULL }, 521 { NL80211_ATTR_FREQ_RANGE_MAX_BW, "freq_range_max_bw", DT_U32, 0, NULL }, 522 { NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, "power_rule_max_ant_gain", DT_U32, 0, NULL }, 523 { NL80211_ATTR_POWER_RULE_MAX_EIRP, "power_rule_max_eirp", DT_U32, 0, NULL }, 524 { NL80211_ATTR_DFS_CAC_TIME, "dfs_cac_time", DT_U32, 0, NULL }, 525 } 526 }; 527 528 static const uc_nl_nested_spec_t nl80211_frame_types_nla = { 529 .headsize = 0, 530 .nattrs = 12, 531 .attrs = { 532 { 1, "ibss", DT_U16, DF_MULTIPLE, NULL }, 533 { 2, "managed", DT_U16, DF_MULTIPLE, NULL }, 534 { 3, "ap", DT_U16, DF_MULTIPLE, NULL }, 535 { 4, "ap_vlan", DT_U16, DF_MULTIPLE, NULL }, 536 { 5, "wds", DT_U16, DF_MULTIPLE, NULL }, 537 { 6, "monitor", DT_U16, DF_MULTIPLE, NULL }, 538 { 7, "mesh_point", DT_U16, DF_MULTIPLE, NULL }, 539 { 8, "p2p_client", DT_U16, DF_MULTIPLE, NULL }, 540 { 9, "p2p_go", DT_U16, DF_MULTIPLE, NULL }, 541 { 10, "p2p_device", DT_U16, DF_MULTIPLE, NULL }, 542 { 11, "outside_bss_context", DT_U16, DF_MULTIPLE, NULL }, 543 { 12, "nan", DT_U16, DF_MULTIPLE, NULL }, 544 } 545 }; 546 547 static const uc_nl_nested_spec_t nl80211_sched_scan_match_nla = { 548 .headsize = 0, 549 .nattrs = 1, 550 .attrs = { 551 { NL80211_SCHED_SCAN_MATCH_ATTR_SSID, "ssid", DT_STRING, DF_BINARY, NULL }, 552 } 553 }; 554 555 static const uc_nl_nested_spec_t nl80211_sched_scan_plan_nla = { 556 .headsize = 0, 557 .nattrs = 2, 558 .attrs = { 559 { NL80211_SCHED_SCAN_PLAN_INTERVAL, "interval", DT_U32, 0, NULL }, 560 { NL80211_SCHED_SCAN_PLAN_ITERATIONS, "iterations", DT_U32, 0, NULL }, 561 } 562 }; 563 564 enum { 565 HWSIM_TM_ATTR_CMD = 1, 566 HWSIM_TM_ATTR_PS = 2, 567 }; 568 569 static const uc_nl_nested_spec_t nl80211_testdata_nla = { 570 .headsize = 0, 571 .nattrs = 2, 572 .attrs = { 573 { HWSIM_TM_ATTR_CMD, "cmd", DT_U32, 0, NULL }, 574 { HWSIM_TM_ATTR_PS, "ps", DT_U32, 0, NULL }, 575 } 576 }; 577 578 static const uc_nl_nested_spec_t nl80211_tid_config_nla = { 579 .headsize = 0, 580 .nattrs = 1, 581 .attrs = { 582 { NL80211_TID_CONFIG_ATTR_TIDS, "tids", DT_U16, 0, NULL }, 583 } 584 }; 585 586 static const uc_nl_nested_spec_t nl80211_wiphy_bands_freqs_wmm_nla = { 587 .headsize = 0, 588 .nattrs = 4, 589 .attrs = { 590 { NL80211_WMMR_CW_MIN, "cw_min", DT_U16, 0, NULL }, 591 { NL80211_WMMR_CW_MAX, "cw_max", DT_U16, 0, NULL }, 592 { NL80211_WMMR_AIFSN, "aifsn", DT_U8, 0, NULL }, 593 { NL80211_WMMR_TXOP, "txop", DT_U16, 0, NULL }, 594 } 595 }; 596 597 static const uc_nl_nested_spec_t nl80211_wiphy_bands_freqs_nla = { 598 .headsize = 0, 599 .nattrs = 25, 600 .attrs = { 601 { NL80211_FREQUENCY_ATTR_FREQ, "freq", DT_U32, 0, NULL }, 602 { NL80211_FREQUENCY_ATTR_DISABLED, "disabled", DT_FLAG, 0, NULL }, 603 { NL80211_FREQUENCY_ATTR_NO_IR, "no_ir", DT_FLAG, 0, NULL }, 604 { __NL80211_FREQUENCY_ATTR_NO_IBSS, "no_ibss", DT_FLAG, 0, NULL }, 605 { NL80211_FREQUENCY_ATTR_RADAR, "radar", DT_FLAG, 0, NULL }, 606 { NL80211_FREQUENCY_ATTR_MAX_TX_POWER, "max_tx_power", DT_U32, 0, NULL }, 607 { NL80211_FREQUENCY_ATTR_DFS_STATE, "dfs_state", DT_U32, 0, NULL }, 608 { NL80211_FREQUENCY_ATTR_DFS_TIME, "dfs_time", DT_U32, 0, NULL }, 609 { NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, "no_ht40_minus", DT_FLAG, 0, NULL }, 610 { NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, "no_ht40_plus", DT_FLAG, 0, NULL }, 611 { NL80211_FREQUENCY_ATTR_NO_80MHZ, "no_80mhz", DT_FLAG, 0, NULL }, 612 { NL80211_FREQUENCY_ATTR_NO_160MHZ, "no_160mhz", DT_FLAG, 0, NULL }, 613 { NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, "dfs_cac_time", DT_FLAG, 0, NULL }, 614 { NL80211_FREQUENCY_ATTR_INDOOR_ONLY, "indoor_only", DT_FLAG, 0, NULL }, 615 { NL80211_FREQUENCY_ATTR_IR_CONCURRENT, "ir_concurrent", DT_FLAG, 0, NULL }, 616 { NL80211_FREQUENCY_ATTR_NO_20MHZ, "no_20mhz", DT_FLAG, 0, NULL }, 617 { NL80211_FREQUENCY_ATTR_NO_10MHZ, "no_10mhz", DT_FLAG, 0, NULL }, 618 { NL80211_FREQUENCY_ATTR_WMM, "wmm", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_bands_freqs_wmm_nla }, 619 { NL80211_FREQUENCY_ATTR_NO_HE, "no_he", DT_FLAG, 0, NULL }, 620 { NL80211_FREQUENCY_ATTR_OFFSET, "offset", DT_U32, 0, NULL }, 621 { NL80211_FREQUENCY_ATTR_1MHZ, "1mhz", DT_FLAG, 0, NULL }, 622 { NL80211_FREQUENCY_ATTR_2MHZ, "2mhz", DT_FLAG, 0, NULL }, 623 { NL80211_FREQUENCY_ATTR_4MHZ, "4mhz", DT_FLAG, 0, NULL }, 624 { NL80211_FREQUENCY_ATTR_8MHZ, "8mhz", DT_FLAG, 0, NULL }, 625 { NL80211_FREQUENCY_ATTR_16MHZ, "16mhz", DT_FLAG, 0, NULL }, 626 } 627 }; 628 629 static const uc_nl_nested_spec_t nl80211_wiphy_bands_rates_nla = { 630 .headsize = 0, 631 .nattrs = 2, 632 .attrs = { 633 { NL80211_BITRATE_ATTR_RATE, "rate", DT_U32, 0, NULL }, 634 { NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, "2ghz_shortpreamble", DT_FLAG, 0, NULL }, 635 } 636 }; 637 638 static const uc_nl_nested_spec_t nl80211_wiphy_bands_iftype_data_nla = { 639 .headsize = 0, 640 .nattrs = 9, 641 .attrs = { 642 { NL80211_BAND_IFTYPE_ATTR_IFTYPES, "iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, 643 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, "he_cap_mac", DT_U8, DF_ARRAY, NULL }, 644 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, "he_cap_phy", DT_U8, DF_ARRAY, NULL }, 645 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, "he_cap_mcs_set", DT_HE_MCS, DF_RELATED, ATTRID(NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY) }, 646 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, "he_cap_ppe", DT_U8, DF_ARRAY, NULL }, 647 { NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, "he_6ghz_capa", DT_U16, 0, NULL }, 648 { NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, "vendor_elems", DT_STRING, DF_BINARY, NULL }, 649 { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, "eht_cap_mac", DT_U8, DF_ARRAY, NULL }, 650 { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, "eht_cap_phy", DT_U8, DF_ARRAY, NULL }, 651 } 652 }; 653 654 static const uc_nl_nested_spec_t nl80211_wiphy_bands_nla = { 655 .headsize = 0, 656 .nattrs = 11, 657 .attrs = { 658 { NL80211_BAND_ATTR_FREQS, "freqs", DT_NESTED, DF_MULTIPLE|DF_TYPEIDX, &nl80211_wiphy_bands_freqs_nla }, 659 { NL80211_BAND_ATTR_RATES, "rates", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_bands_rates_nla }, 660 { NL80211_BAND_ATTR_HT_MCS_SET, "ht_mcs_set", DT_HT_MCS, 0, NULL }, 661 { NL80211_BAND_ATTR_HT_CAPA, "ht_capa", DT_U16, 0, NULL }, 662 { NL80211_BAND_ATTR_HT_AMPDU_FACTOR, "ht_ampdu_factor", DT_U8, 0, NULL }, 663 { NL80211_BAND_ATTR_HT_AMPDU_DENSITY, "ht_ampdu_density", DT_U8, 0, NULL }, 664 { NL80211_BAND_ATTR_VHT_MCS_SET, "vht_mcs_set", DT_VHT_MCS, 0, NULL }, 665 { NL80211_BAND_ATTR_VHT_CAPA, "vht_capa", DT_U32, 0, NULL }, 666 { NL80211_BAND_ATTR_IFTYPE_DATA, "iftype_data", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_bands_iftype_data_nla }, 667 { NL80211_BAND_ATTR_EDMG_CHANNELS, "edmg_channels", DT_U8, 0, NULL }, 668 { NL80211_BAND_ATTR_EDMG_BW_CONFIG, "edmg_bw_config", DT_U8, 0, NULL }, 669 } 670 }; 671 672 static const uc_nl_nested_spec_t nl80211_wowlan_triggers_tcp_nla = { 673 .headsize = 0, 674 .nattrs = 11, 675 .attrs = { 676 { NL80211_WOWLAN_TCP_SRC_IPV4, "src_ipv4", DT_INADDR, 0, NULL }, 677 { NL80211_WOWLAN_TCP_SRC_PORT, "src_port", DT_U16, 0, NULL }, 678 { NL80211_WOWLAN_TCP_DST_IPV4, "dst_ipv4", DT_INADDR, 0, NULL }, 679 { NL80211_WOWLAN_TCP_DST_PORT, "dst_port", DT_U16, 0, NULL }, 680 { NL80211_WOWLAN_TCP_DST_MAC, "dst_mac", DT_LLADDR, 0, NULL }, 681 { NL80211_WOWLAN_TCP_DATA_PAYLOAD, "data_payload", DT_STRING, DF_BINARY, NULL }, 682 { NL80211_WOWLAN_TCP_DATA_INTERVAL, "data_interval", DT_U32, 0, NULL }, 683 { NL80211_WOWLAN_TCP_WAKE_MASK, "wake_mask", DT_STRING, DF_BINARY, NULL }, 684 { NL80211_WOWLAN_TCP_WAKE_PAYLOAD, "wake_payload", DT_STRING, DF_BINARY, NULL }, 685 { NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, "data_payload_seq", DT_U32, DF_ARRAY, NULL }, 686 { NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, "data_payload_token", DT_STRING, DF_BINARY, NULL }, /* XXX: struct nl80211_wowlan_tcp_data_token */ 687 } 688 }; 689 690 static const uc_nl_nested_spec_t nl80211_pkt_pattern_nla = { 691 .headsize = 0, 692 .nattrs = 3, 693 .attrs = { 694 { NL80211_PKTPAT_MASK, "mask", DT_STRING, DF_BINARY, NULL }, 695 { NL80211_PKTPAT_PATTERN, "pattern", DT_STRING, DF_BINARY, NULL }, 696 { NL80211_PKTPAT_OFFSET, "offset", DT_U32, 0, NULL }, 697 } 698 }; 699 700 static const uc_nl_nested_spec_t nl80211_wowlan_triggers_nla = { 701 .headsize = 0, 702 .nattrs = 9, 703 .attrs = { 704 { NL80211_WOWLAN_TRIG_ANY, "any", DT_FLAG, 0, NULL }, 705 { NL80211_WOWLAN_TRIG_DISCONNECT, "disconnect", DT_FLAG, 0, NULL }, 706 { NL80211_WOWLAN_TRIG_MAGIC_PKT, "magic_pkt", DT_FLAG, 0, NULL }, 707 { NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, "gtk_rekey_failure", DT_FLAG, 0, NULL }, 708 { NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, "eap_ident_request", DT_FLAG, 0, NULL }, 709 { NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, "4way_handshake", DT_FLAG, 0, NULL }, 710 { NL80211_WOWLAN_TRIG_RFKILL_RELEASE, "rfkill_release", DT_FLAG, 0, NULL }, 711 { NL80211_WOWLAN_TRIG_TCP_CONNECTION, "tcp_connection", DT_NESTED, 0, &nl80211_wowlan_triggers_tcp_nla }, 712 { NL80211_WOWLAN_TRIG_PKT_PATTERN, "pkt_pattern", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX|DF_OFFSET1, &nl80211_pkt_pattern_nla }, 713 } 714 }; 715 716 static const uc_nl_nested_spec_t nl80211_coalesce_rule_nla = { 717 .headsize = 0, 718 .nattrs = 3, 719 .attrs = { 720 { NL80211_ATTR_COALESCE_RULE_CONDITION, "coalesce_rule_condition", DT_U32, 0, NULL }, 721 { NL80211_ATTR_COALESCE_RULE_DELAY, "coalesce_rule_delay", DT_U32, 0, NULL }, 722 { NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, "coalesce_rule_pkt_pattern", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX|DF_OFFSET1, &nl80211_pkt_pattern_nla }, 723 } 724 }; 725 726 static const uc_nl_nested_spec_t nl80211_bss_nla = { 727 .headsize = 0, 728 .nattrs = 12, 729 .attrs = { 730 { NL80211_BSS_BSSID, "bssid", DT_LLADDR, 0, NULL }, 731 { NL80211_BSS_STATUS, "status", DT_U32, 0, NULL }, 732 { NL80211_BSS_LAST_SEEN_BOOTTIME, "last_seen_boottime", DT_U64, 0, NULL }, 733 { NL80211_BSS_TSF, "tsf", DT_U64, 0, NULL }, 734 { NL80211_BSS_FREQUENCY, "frequency", DT_U32, 0, NULL }, 735 { NL80211_BSS_BEACON_INTERVAL, "beacon_interval", DT_U16, 0, NULL }, 736 { NL80211_BSS_CAPABILITY, "capability", DT_U16, 0, NULL }, 737 { NL80211_BSS_SIGNAL_MBM, "signal_mbm", DT_S32, 0, NULL }, 738 { NL80211_BSS_SIGNAL_UNSPEC, "signal_unspec", DT_U8, 0, NULL }, 739 { NL80211_BSS_SEEN_MS_AGO, "seen_ms_ago", DT_S32, 0, NULL }, 740 { NL80211_BSS_INFORMATION_ELEMENTS, "information_elements", DT_IE, 0, NULL }, 741 { NL80211_BSS_BEACON_IES, "beacon_ies", DT_IE, 0, NULL }, 742 } 743 }; 744 745 static const uc_nl_nested_spec_t nl80211_sta_info_bitrate_nla = { 746 .headsize = 0, 747 .nattrs = 22, 748 .attrs = { 749 { NL80211_RATE_INFO_BITRATE, "bitrate", DT_U16, 0, NULL }, 750 { NL80211_RATE_INFO_BITRATE32, "bitrate32", DT_U32, 0, NULL }, 751 { NL80211_RATE_INFO_MCS, "mcs", DT_U8, 0, NULL }, 752 { NL80211_RATE_INFO_40_MHZ_WIDTH, "40_mhz_width", DT_FLAG, 0, NULL }, 753 { NL80211_RATE_INFO_SHORT_GI, "short_gi", DT_FLAG, 0, NULL }, 754 { NL80211_RATE_INFO_VHT_MCS, "vht_mcs", DT_U8, 0, NULL }, 755 { NL80211_RATE_INFO_VHT_NSS, "vht_nss", DT_U8, 0, NULL }, 756 { NL80211_RATE_INFO_HE_MCS, "he_mcs", DT_U8, 0, NULL }, 757 { NL80211_RATE_INFO_HE_NSS, "he_nss", DT_U8, 0, NULL }, 758 { NL80211_RATE_INFO_HE_GI, "he_gi", DT_U8, 0, NULL }, 759 { NL80211_RATE_INFO_HE_DCM, "he_dcm", DT_U8, 0, NULL }, 760 { NL80211_RATE_INFO_HE_RU_ALLOC, "he_ru_alloc", DT_U8, 0, NULL }, 761 { NL80211_RATE_INFO_EHT_MCS, "eht_mcs", DT_U8, 0, NULL }, 762 { NL80211_RATE_INFO_EHT_NSS, "eht_nss", DT_U8, 0, NULL }, 763 { NL80211_RATE_INFO_EHT_GI, "eht_gi", DT_U8, 0, NULL }, 764 { NL80211_RATE_INFO_40_MHZ_WIDTH, "width_40", DT_FLAG, 0, NULL }, 765 { NL80211_RATE_INFO_80_MHZ_WIDTH, "width_80", DT_FLAG, 0, NULL }, 766 { NL80211_RATE_INFO_80P80_MHZ_WIDTH, "width_80p80", DT_FLAG, 0, NULL }, 767 { NL80211_RATE_INFO_160_MHZ_WIDTH, "width_160", DT_FLAG, 0, NULL }, 768 { NL80211_RATE_INFO_320_MHZ_WIDTH, "width_320", DT_FLAG, 0, NULL }, 769 { NL80211_RATE_INFO_10_MHZ_WIDTH, "width_10", DT_FLAG, 0, NULL }, 770 { NL80211_RATE_INFO_5_MHZ_WIDTH, "width_5", DT_FLAG, 0, NULL }, 771 } 772 }; 773 774 static const uc_nl_nested_spec_t nl80211_tid_txq_stats_nla = { 775 .headsize = 0, 776 .nattrs = 9, 777 .attrs = { 778 { NL80211_TXQ_STATS_BACKLOG_BYTES, "backlog_bytes", DT_U32, 0, NULL }, 779 { NL80211_TXQ_STATS_BACKLOG_PACKETS, "backlog_packets", DT_U32, 0, NULL }, 780 { NL80211_TXQ_STATS_FLOWS, "flows", DT_U32, 0, NULL }, 781 { NL80211_TXQ_STATS_DROPS, "drops", DT_U32, 0, NULL }, 782 { NL80211_TXQ_STATS_ECN_MARKS, "ecn_marks", DT_U32, 0, NULL }, 783 { NL80211_TXQ_STATS_OVERLIMIT, "overlimit", DT_U32, 0, NULL }, 784 { NL80211_TXQ_STATS_COLLISIONS, "collisions", DT_U32, 0, NULL }, 785 { NL80211_TXQ_STATS_TX_BYTES, "tx_bytes", DT_U32, 0, NULL }, 786 { NL80211_TXQ_STATS_TX_PACKETS, "tx_packets", DT_U32, 0, NULL }, 787 } 788 }; 789 790 static const uc_nl_nested_spec_t nl80211_tid_stats_nla = { 791 .headsize = 0, 792 .nattrs = 5, 793 .attrs = { 794 { NL80211_TID_STATS_RX_MSDU, "rx_msdu", DT_U64, 0, NULL }, 795 { NL80211_TID_STATS_TX_MSDU, "tx_msdu", DT_U64, 0, NULL }, 796 { NL80211_TID_STATS_TX_MSDU_RETRIES, "tx_msdu_retries", DT_U64, 0, NULL }, 797 { NL80211_TID_STATS_TX_MSDU_FAILED, "tx_msdu_failed", DT_U64, 0, NULL }, 798 { NL80211_TID_STATS_TXQ_STATS, "txq_stats", DT_NESTED, 0, &nl80211_tid_txq_stats_nla }, 799 } 800 }; 801 802 static const uc_nl_nested_spec_t nl80211_bss_param_nla = { 803 .headsize = 0, 804 .nattrs = 5, 805 .attrs = { 806 { NL80211_STA_BSS_PARAM_CTS_PROT, "cts_prot", DT_FLAG, 0, NULL }, 807 { NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, "short_preamble", DT_FLAG, 0, NULL }, 808 { NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, "short_slot_time", DT_FLAG, 0, NULL }, 809 { NL80211_STA_BSS_PARAM_DTIM_PERIOD, "dtim_period", DT_U8, 0, NULL }, 810 { NL80211_STA_BSS_PARAM_BEACON_INTERVAL, "beacon_interval", DT_U16, 0, NULL }, 811 } 812 }; 813 814 static const uc_nl_nested_spec_t nl80211_sta_info_nla = { 815 .headsize = 0, 816 .nattrs = 40, 817 .attrs = { 818 { NL80211_STA_INFO_INACTIVE_TIME, "inactive_time", DT_U32, 0, NULL }, 819 { NL80211_STA_INFO_RX_BYTES, "rx_bytes", DT_U32, 0, NULL }, 820 { NL80211_STA_INFO_TX_BYTES, "tx_bytes", DT_U32, 0, NULL }, 821 { NL80211_STA_INFO_RX_BYTES64, "rx_bytes64", DT_U64, 0, NULL }, 822 { NL80211_STA_INFO_TX_BYTES64, "tx_bytes64", DT_U64, 0, NULL }, 823 { NL80211_STA_INFO_RX_PACKETS, "rx_packets", DT_U32, 0, NULL }, 824 { NL80211_STA_INFO_TX_PACKETS, "tx_packets", DT_U32, 0, NULL }, 825 { NL80211_STA_INFO_BEACON_RX, "beacon_rx", DT_U64, 0, NULL }, 826 { NL80211_STA_INFO_SIGNAL, "signal", DT_S8, 0, NULL }, 827 { NL80211_STA_INFO_T_OFFSET, "t_offset", DT_U64, 0, NULL }, 828 { NL80211_STA_INFO_TX_BITRATE, "tx_bitrate", DT_NESTED, 0, &nl80211_sta_info_bitrate_nla }, 829 { NL80211_STA_INFO_RX_BITRATE, "rx_bitrate", DT_NESTED, 0, &nl80211_sta_info_bitrate_nla }, 830 { NL80211_STA_INFO_LLID, "llid", DT_U16, 0, NULL }, 831 { NL80211_STA_INFO_PLID, "plid", DT_U16, 0, NULL }, 832 { NL80211_STA_INFO_PLINK_STATE, "plink_state", DT_U8, 0, NULL }, 833 { NL80211_STA_INFO_TX_RETRIES, "tx_retries", DT_U32, 0, NULL }, 834 { NL80211_STA_INFO_TX_FAILED, "tx_failed", DT_U32, 0, NULL }, 835 { NL80211_STA_INFO_BEACON_LOSS, "beacon_loss", DT_U32, 0, NULL }, 836 { NL80211_STA_INFO_RX_DROP_MISC, "rx_drop_misc", DT_U64, 0, NULL }, 837 { NL80211_STA_INFO_STA_FLAGS, "sta_flags", DT_U32, DF_ARRAY, NULL }, 838 { NL80211_STA_INFO_LOCAL_PM, "local_pm", DT_U32, 0, NULL }, 839 { NL80211_STA_INFO_PEER_PM, "peer_pm", DT_U32, 0, NULL }, 840 { NL80211_STA_INFO_NONPEER_PM, "nonpeer_pm", DT_U32, 0, NULL }, 841 { NL80211_STA_INFO_CHAIN_SIGNAL, "chain_signal", DT_S8, DF_MULTIPLE|DF_AUTOIDX, NULL }, 842 { NL80211_STA_INFO_CHAIN_SIGNAL_AVG, "chain_signal_avg", DT_S8, DF_MULTIPLE|DF_AUTOIDX, NULL }, 843 { NL80211_STA_INFO_TID_STATS, "tid_stats", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_tid_stats_nla }, 844 { NL80211_STA_INFO_BSS_PARAM, "bss_param", DT_NESTED, 0, &nl80211_bss_param_nla }, 845 { NL80211_STA_INFO_RX_DURATION, "rx_duration", DT_U64, 0, NULL }, 846 { NL80211_STA_INFO_TX_DURATION, "tx_duration", DT_U64, 0, NULL }, 847 { NL80211_STA_INFO_ACK_SIGNAL, "ack_signal", DT_S8, 0, NULL }, 848 { NL80211_STA_INFO_ACK_SIGNAL_AVG, "ack_signal_avg", DT_S8, 0, NULL }, 849 { NL80211_STA_INFO_AIRTIME_LINK_METRIC, "airtime_link_metric", DT_U32, 0, NULL }, 850 { NL80211_STA_INFO_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL }, 851 { NL80211_STA_INFO_CONNECTED_TO_AS, "connected_to_as", DT_BOOL, 0, NULL }, 852 { NL80211_STA_INFO_CONNECTED_TO_GATE, "connected_to_gate", DT_BOOL, 0, NULL }, 853 { NL80211_STA_INFO_CONNECTED_TIME, "connected_time", DT_U32, 0, NULL }, 854 { NL80211_STA_INFO_ASSOC_AT_BOOTTIME, "assoc_at_boottime", DT_U64, 0, NULL }, 855 { NL80211_STA_INFO_BEACON_SIGNAL_AVG, "beacon_signal_avg", DT_S8, 0, NULL }, 856 { NL80211_STA_INFO_EXPECTED_THROUGHPUT, "expected_throughput", DT_U32, 0, NULL }, 857 { NL80211_STA_INFO_SIGNAL_AVG, "signal_avg", DT_S8, 0, NULL }, 858 } 859 }; 860 861 static const uc_nl_nested_spec_t nl80211_survey_info_nla = { 862 .headsize = 0, 863 .nattrs = 8, 864 .attrs = { 865 { NL80211_SURVEY_INFO_FREQUENCY, "frequency", DT_U32, 0, NULL }, 866 { NL80211_SURVEY_INFO_TIME, "time", DT_U64, 0, NULL }, 867 { NL80211_SURVEY_INFO_TIME_TX, "time_tx", DT_U64, 0, NULL }, 868 { NL80211_SURVEY_INFO_TIME_RX, "time_rx", DT_U64, 0, NULL }, 869 { NL80211_SURVEY_INFO_TIME_BUSY, "busy", DT_U64, 0, NULL }, 870 { NL80211_SURVEY_INFO_TIME_EXT_BUSY, "ext_busy", DT_U64, 0, NULL }, 871 { NL80211_SURVEY_INFO_TIME_SCAN, "scan", DT_U64, 0, NULL }, 872 { NL80211_SURVEY_INFO_NOISE, "noise", DT_S8, 0, NULL }, 873 } 874 }; 875 876 static const uc_nl_nested_spec_t nl80211_mpath_info_nla = { 877 .headsize = 0, 878 .nattrs = 8, 879 .attrs = { 880 { NL80211_MPATH_INFO_SN, "sn", DT_U32, 0, NULL }, 881 { NL80211_MPATH_INFO_METRIC, "metric", DT_U32, 0, NULL }, 882 { NL80211_MPATH_INFO_EXPTIME, "expire", DT_U32, 0, NULL }, 883 { NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, "discovery_timeout", DT_U32, 0, NULL }, 884 { NL80211_MPATH_INFO_DISCOVERY_RETRIES, "discovery_retries", DT_U8, 0, NULL }, 885 { NL80211_MPATH_INFO_FLAGS, "flags", DT_U8, 0, NULL }, 886 { NL80211_MPATH_INFO_HOP_COUNT, "hop_count", DT_U8, 0, NULL }, 887 { NL80211_MPATH_INFO_PATH_CHANGE, "path_change", DT_U32, 0, NULL }, 888 } 889 }; 890 891 static const uc_nl_nested_spec_t nl80211_radio_freq_range_nla = { 892 .headsize = 0, 893 .nattrs = 2, 894 .attrs = { 895 { NL80211_WIPHY_RADIO_FREQ_ATTR_START, "start", DT_U32, 0, NULL }, 896 { NL80211_WIPHY_RADIO_FREQ_ATTR_END, "end", DT_U32, 0, NULL }, 897 } 898 }; 899 900 static const uc_nl_nested_spec_t nl80211_wiphy_radio_nla = { 901 .headsize = 0, 902 .nattrs = 4, 903 .attrs = { 904 { NL80211_WIPHY_RADIO_ATTR_INDEX, "index", DT_U32, 0, NULL }, 905 { NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE, "freq_ranges", DT_NESTED, DF_REPEATED, &nl80211_radio_freq_range_nla }, 906 { NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, "interface_combinations", DT_NESTED, DF_REPEATED, &nl80211_ifcomb_nla }, 907 { NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, "antenna_mask", DT_U32, 0, NULL }, 908 } 909 }; 910 911 static const uc_nl_nested_spec_t nl80211_msg = { 912 .headsize = 0, 913 .nattrs = 130, 914 .attrs = { 915 { NL80211_ATTR_4ADDR, "4addr", DT_U8, 0, NULL }, 916 { NL80211_ATTR_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL }, 917 { NL80211_ATTR_AKM_SUITES, "akm_suites", DT_U32, 0, NULL }, 918 { NL80211_ATTR_AUTH_TYPE, "auth_type", DT_U32, 0, NULL }, 919 { NL80211_ATTR_BANDS, "bands", DT_U32, 0, NULL }, 920 { NL80211_ATTR_BEACON_HEAD, "beacon_head", DT_STRING, DF_BINARY, NULL }, 921 { NL80211_ATTR_BEACON_INTERVAL, "beacon_interval", DT_U32, 0, NULL }, 922 { NL80211_ATTR_BEACON_TAIL, "beacon_tail", DT_STRING, DF_BINARY, NULL }, 923 { NL80211_ATTR_BSS, "bss", DT_NESTED, 0, &nl80211_bss_nla }, 924 { NL80211_ATTR_BSS_BASIC_RATES, "bss_basic_rates", DT_U32, DF_ARRAY, NULL }, 925 { NL80211_ATTR_CENTER_FREQ1, "center_freq1", DT_U32, 0, NULL }, 926 { NL80211_ATTR_CENTER_FREQ2, "center_freq2", DT_U32, 0, NULL }, 927 { NL80211_ATTR_CHANNEL_WIDTH, "channel_width", DT_U32, 0, NULL }, 928 { NL80211_ATTR_CH_SWITCH_BLOCK_TX, "ch_switch_block_tx", DT_FLAG, 0, NULL }, 929 { NL80211_ATTR_CH_SWITCH_COUNT, "ch_switch_count", DT_U32, 0, NULL }, 930 { NL80211_ATTR_CIPHER_SUITES, "cipher_suites", DT_U32, DF_ARRAY, NULL }, 931 { NL80211_ATTR_CIPHER_SUITES_PAIRWISE, "cipher_suites_pairwise", DT_U32, 0, NULL }, 932 { NL80211_ATTR_CIPHER_SUITE_GROUP, "cipher_suite_group", DT_U32, 0, NULL }, 933 { NL80211_ATTR_COALESCE_RULE, "coalesce_rule", DT_NESTED, 0, &nl80211_coalesce_rule_nla }, 934 { NL80211_ATTR_COOKIE, "cookie", DT_U64, 0, NULL }, 935 { NL80211_ATTR_CQM, "cqm", DT_NESTED, 0, &nl80211_cqm_nla }, 936 { NL80211_ATTR_DFS_CAC_TIME, "dfs_cac_time", DT_U32, 0, NULL }, 937 { NL80211_ATTR_DFS_REGION, "dfs_region", DT_U8, 0, NULL }, 938 { NL80211_ATTR_DTIM_PERIOD, "dtim_period", DT_U32, 0, NULL }, 939 { NL80211_ATTR_DURATION, "duration", DT_U32, 0, NULL }, 940 { NL80211_ATTR_EXT_FEATURES, "extended_features", DT_U8, DF_ARRAY, NULL }, 941 { NL80211_ATTR_FEATURE_FLAGS, "feature_flags", DT_U32, 0, NULL }, 942 { NL80211_ATTR_FRAME, "frame", DT_STRING, DF_BINARY, NULL }, 943 { NL80211_ATTR_FRAME_MATCH, "frame_match", DT_STRING, DF_BINARY, NULL }, 944 { NL80211_ATTR_FRAME_TYPE, "frame_type", DT_U16, 0, NULL }, 945 { NL80211_ATTR_FREQ_FIXED, "freq_fixed", DT_FLAG, 0, NULL }, 946 { NL80211_ATTR_FTM_RESPONDER, "ftm_responder", DT_NESTED, 0, &nl80211_ftm_responder_nla }, 947 { NL80211_ATTR_FTM_RESPONDER_STATS, "ftm_responder_stats", DT_NESTED, 0, &nl80211_ftm_responder_stats_nla }, 948 { NL80211_ATTR_HIDDEN_SSID, "hidden_ssid", DT_U32, 0, NULL }, 949 { NL80211_ATTR_HT_CAPABILITY_MASK, "ht_capability_mask", DT_HT_CAP, 0, NULL }, 950 { NL80211_ATTR_IE, "ie", DT_IE, 0, NULL }, 951 { NL80211_ATTR_IFINDEX, "dev", DT_NETDEV, 0, NULL }, 952 { NL80211_ATTR_IFNAME, "ifname", DT_STRING, 0, NULL }, 953 { NL80211_ATTR_IFTYPE, "iftype", DT_U32, 0, NULL }, 954 { NL80211_ATTR_INACTIVITY_TIMEOUT, "inactivity_timeout", DT_U16, 0, NULL }, 955 { NL80211_ATTR_INTERFACE_COMBINATIONS, "interface_combinations", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_ifcomb_nla }, 956 { NL80211_ATTR_KEYS, "keys", DT_NESTED, DF_AUTOIDX, &nl80211_keys_nla }, 957 { NL80211_ATTR_KEY_SEQ, "key_seq", DT_STRING, DF_BINARY, NULL }, 958 { NL80211_ATTR_KEY_TYPE, "key_type", DT_U32, 0, NULL }, 959 { NL80211_ATTR_LOCAL_MESH_POWER_MODE, "local_mesh_power_mode", DT_U32, 0, NULL }, 960 { NL80211_ATTR_MAC, "mac", DT_LLADDR, 0, NULL }, 961 { NL80211_ATTR_MAC_MASK, "mac_mask", DT_LLADDR, 0, NULL }, 962 { NL80211_ATTR_MCAST_RATE, "mcast_rate", DT_U32, 0, NULL }, 963 { NL80211_ATTR_MEASUREMENT_DURATION, "measurement_duration", DT_U16, 0, NULL }, 964 { NL80211_ATTR_MESH_ID, "mesh_id", DT_STRING, 0, NULL }, 965 { NL80211_ATTR_MESH_PARAMS, "mesh_params", DT_NESTED, 0, &nl80211_mesh_params_nla }, 966 { NL80211_ATTR_MESH_SETUP, "mesh_setup", DT_NESTED, 0, &nl80211_mesh_setup_nla }, 967 { NL80211_ATTR_MGMT_SUBTYPE, "mgmt_subtype", DT_U8, 0, NULL }, 968 { NL80211_ATTR_MNTR_FLAGS, "mntr_flags", DT_NESTED, 0, &nl80211_mntr_flags_nla }, 969 { NL80211_ATTR_MPATH_NEXT_HOP, "mpath_next_hop", DT_LLADDR, 0, NULL }, 970 { NL80211_ATTR_MPATH_INFO, "mpath_info", DT_NESTED, 0, &nl80211_mpath_info_nla }, 971 { NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, "mu_mimo_follow_mac_addr", DT_LLADDR, 0, NULL }, 972 { NL80211_ATTR_NAN_FUNC, "nan_func", DT_NESTED, 0, &nl80211_nan_func_nla }, 973 { NL80211_ATTR_NAN_MASTER_PREF, "nan_master_pref", DT_U8, 0, NULL }, 974 { NL80211_ATTR_NETNS_FD, "netns_fd", DT_U32, 0, NULL }, 975 { NL80211_ATTR_NOACK_MAP, "noack_map", DT_U16, 0, NULL }, 976 { NL80211_ATTR_NSS, "nss", DT_U8, 0, NULL }, 977 { NL80211_ATTR_PEER_MEASUREMENTS, "peer_measurements", DT_NESTED, 0, &nl80211_peer_measurements_nla }, 978 { NL80211_ATTR_PID, "pid", DT_U32, 0, NULL }, 979 { NL80211_ATTR_PMK, "pmk", DT_STRING, DF_BINARY, NULL }, 980 { NL80211_ATTR_PRIVACY, "privacy", DT_FLAG, 0, NULL }, 981 { NL80211_ATTR_PROTOCOL_FEATURES, "protocol_features", DT_U32, 0, NULL }, 982 { NL80211_ATTR_PS_STATE, "ps_state", DT_U32, 0, NULL }, 983 { NL80211_ATTR_RADAR_EVENT, "radar_event", DT_U32, 0, NULL }, 984 { NL80211_ATTR_REASON_CODE, "reason_code", DT_U16, 0, NULL }, 985 { NL80211_ATTR_REG_ALPHA2, "reg_alpha2", DT_STRING, 0, NULL }, 986 { NL80211_ATTR_REG_INITIATOR, "reg_initiator", DT_U32, 0, NULL }, 987 { NL80211_ATTR_REG_RULES, "reg_rules", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_reg_rules_nla }, 988 { NL80211_ATTR_REG_TYPE, "reg_type", DT_U8, 0, NULL }, 989 { NL80211_ATTR_RX_FRAME_TYPES, "rx_frame_types", DT_NESTED, 0, &nl80211_frame_types_nla }, 990 { NL80211_ATTR_RX_SIGNAL_DBM, "rx_signal_dbm", DT_U32, 0, NULL }, 991 { NL80211_ATTR_SCAN_FLAGS, "scan_flags", DT_U32, 0, NULL }, 992 { NL80211_ATTR_SCAN_FREQUENCIES, "scan_frequencies", DT_U32, DF_MULTIPLE|DF_AUTOIDX, NULL }, 993 { NL80211_ATTR_SCAN_SSIDS, "scan_ssids", DT_STRING, DF_MULTIPLE|DF_AUTOIDX, NULL }, 994 { NL80211_ATTR_SCHED_SCAN_DELAY, "sched_scan_delay", DT_U32, 0, NULL }, 995 { NL80211_ATTR_SCHED_SCAN_INTERVAL, "sched_scan_interval", DT_U32, 0, NULL }, 996 { NL80211_ATTR_SCHED_SCAN_MATCH, "sched_scan_match", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_sched_scan_match_nla }, 997 { NL80211_ATTR_SCHED_SCAN_PLANS, "sched_scan_plans", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX|DF_OFFSET1, &nl80211_sched_scan_plan_nla }, 998 { NL80211_ATTR_SMPS_MODE, "smps_mode", DT_U8, 0, NULL }, 999 { NL80211_ATTR_SPLIT_WIPHY_DUMP, "split_wiphy_dump", DT_FLAG, 0, NULL }, 1000 { NL80211_ATTR_SSID, "ssid", DT_STRING, DF_BINARY, NULL }, 1001 { NL80211_ATTR_STATUS_CODE, "status_code", DT_U16, 0, NULL }, 1002 { NL80211_ATTR_STA_INFO, "sta_info", DT_NESTED, 0, &nl80211_sta_info_nla }, 1003 { NL80211_ATTR_STA_PLINK_ACTION, "sta_plink_action", DT_U8, 0, NULL }, 1004 { NL80211_ATTR_STA_TX_POWER, "sta_tx_power", DT_U16, 0, NULL }, 1005 { NL80211_ATTR_STA_TX_POWER_SETTING, "sta_tx_power_setting", DT_U8, 0, NULL }, 1006 { NL80211_ATTR_STA_VLAN, "sta_vlan", DT_U32, 0, NULL }, 1007 { NL80211_ATTR_SUPPORTED_COMMANDS, "supported_commands", DT_U32, DF_NO_SET|DF_MULTIPLE|DF_AUTOIDX, NULL }, 1008 { NL80211_ATTR_TESTDATA, "testdata", DT_NESTED, 0, &nl80211_testdata_nla }, 1009 { NL80211_ATTR_TID_CONFIG, "tid_config", DT_NESTED, DF_MULTIPLE, &nl80211_tid_config_nla }, 1010 { NL80211_ATTR_TIMEOUT, "timeout", DT_U32, 0, NULL }, 1011 { NL80211_ATTR_TXQ_LIMIT, "txq_limit", DT_U32, 0, NULL }, 1012 { NL80211_ATTR_TXQ_MEMORY_LIMIT, "txq_memory_limit", DT_U32, 0, NULL }, 1013 { NL80211_ATTR_TXQ_QUANTUM, "txq_quantum", DT_U32, 0, NULL }, 1014 { NL80211_ATTR_TX_FRAME_TYPES, "tx_frame_types", DT_NESTED, 0, &nl80211_frame_types_nla }, 1015 { NL80211_ATTR_USE_MFP, "use_mfp", DT_U32, 0, NULL }, 1016 { NL80211_ATTR_VENDOR_DATA, "vendor_data", DT_STRING, DF_BINARY, NULL }, 1017 { NL80211_ATTR_VENDOR_ID, "vendor_id", DT_U32, 0, NULL }, 1018 { NL80211_ATTR_VENDOR_SUBCMD, "vendor_subcmd", DT_U32, 0, NULL }, 1019 { NL80211_ATTR_WDEV, "wdev", DT_U64, 0, NULL }, 1020 { NL80211_ATTR_WIPHY, "wiphy", DT_U32, 0, NULL }, 1021 { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, "wiphy_antenna_avail_rx", DT_U32, 0, NULL }, 1022 { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, "wiphy_antenna_avail_tx", DT_U32, 0, NULL }, 1023 { NL80211_ATTR_WIPHY_ANTENNA_RX, "wiphy_antenna_rx", DT_U32, 0, NULL }, 1024 { NL80211_ATTR_WIPHY_ANTENNA_TX, "wiphy_antenna_tx", DT_U32, 0, NULL }, 1025 { NL80211_ATTR_WIPHY_BANDS, "wiphy_bands", DT_NESTED, DF_NO_SET|DF_MULTIPLE|DF_TYPEIDX, &nl80211_wiphy_bands_nla }, 1026 { NL80211_ATTR_WIPHY_CHANNEL_TYPE, "wiphy_channel_type", DT_U32, 0, NULL }, 1027 { NL80211_ATTR_WIPHY_COVERAGE_CLASS, "wiphy_coverage_class", DT_U8, 0, NULL }, 1028 { NL80211_ATTR_WIPHY_DYN_ACK, "wiphy_dyn_ack", DT_FLAG, 0, NULL }, 1029 { NL80211_ATTR_WIPHY_FRAG_THRESHOLD, "wiphy_frag_threshold", DT_S32, 0, NULL }, 1030 { NL80211_ATTR_WIPHY_FREQ, "wiphy_freq", DT_U32, 0, NULL }, 1031 { NL80211_ATTR_WIPHY_NAME, "wiphy_name", DT_STRING, 0, NULL }, 1032 { NL80211_ATTR_WIPHY_RETRY_LONG, "wiphy_retry_long", DT_U8, 0, NULL }, 1033 { NL80211_ATTR_WIPHY_RETRY_SHORT, "wiphy_retry_short", DT_U8, 0, NULL }, 1034 { NL80211_ATTR_WIPHY_RTS_THRESHOLD, "wiphy_rts_threshold", DT_S32, 0, NULL }, 1035 { NL80211_ATTR_WIPHY_TX_POWER_LEVEL, "wiphy_tx_power_level", DT_U32, 0, NULL }, 1036 { NL80211_ATTR_WIPHY_TX_POWER_SETTING, "wiphy_tx_power_setting", DT_U32, 0, NULL }, 1037 { NL80211_ATTR_WOWLAN_TRIGGERS, "wowlan_triggers", DT_NESTED, 0, &nl80211_wowlan_triggers_nla }, 1038 { NL80211_ATTR_WPA_VERSIONS, "wpa_versions", DT_U32, 0, NULL }, 1039 { NL80211_ATTR_SUPPORTED_IFTYPES, "supported_iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, 1040 { NL80211_ATTR_SOFTWARE_IFTYPES, "software_iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, 1041 { NL80211_ATTR_MAX_AP_ASSOC_STA, "max_ap_assoc", DT_U16, 0, NULL }, 1042 { NL80211_ATTR_SURVEY_INFO, "survey_info", DT_NESTED, 0, &nl80211_survey_info_nla }, 1043 { NL80211_ATTR_WIPHY_RADIOS, "radios", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_radio_nla }, 1044 { NL80211_ATTR_VIF_RADIO_MASK, "vif_radio_mask", DT_U32, 0, NULL }, 1045 } 1046 }; 1047 1048 static const uc_nl_nested_spec_t hwsim_tx_info_struct = { 1049 .headsize = sizeof(struct hwsim_tx_rate), 1050 .nattrs = 2, 1051 .attrs = { 1052 { NLA_UNSPEC, "idx", DT_S8, 0, MEMBER(hwsim_tx_rate, idx) }, 1053 { NLA_UNSPEC, "count", DT_U8, 0, MEMBER(hwsim_tx_rate, count) }, 1054 } 1055 }; 1056 1057 static const uc_nl_nested_spec_t hwsim_tx_info_flags_struct = { 1058 .headsize = sizeof(struct hwsim_tx_rate_flag), 1059 .nattrs = 2, 1060 .attrs = { 1061 { NLA_UNSPEC, "idx", DT_S8, 0, MEMBER(hwsim_tx_rate_flag, idx) }, 1062 { NLA_UNSPEC, "flags", DT_U16, 0, MEMBER(hwsim_tx_rate_flag, flags) }, 1063 } 1064 }; 1065 1066 static const uc_nl_nested_spec_t hwsim_pmsr_support_nla = { 1067 .headsize = 0, 1068 .nattrs = 5, 1069 .attrs = { 1070 { NL80211_PMSR_ATTR_MAX_PEERS, "max_peers", DT_U32, 0, NULL }, 1071 { NL80211_PMSR_ATTR_REPORT_AP_TSF, "report_ap_tsf", DT_FLAG, 0, NULL }, 1072 { NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, "randomize_mac_addr", DT_FLAG, 0, NULL }, 1073 { NL80211_PMSR_ATTR_TYPE_CAPA, "type_capa", DT_U32, 0, NULL }, 1074 { NL80211_PMSR_ATTR_PEERS, "peers", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_peer_measurements_peers_nla }, 1075 } 1076 }; 1077 1078 static const uc_nl_nested_spec_t hwsim_pmsr_request_nla = { 1079 .headsize = 0, 1080 .nattrs = 1, 1081 .attrs = { 1082 { NL80211_ATTR_PEER_MEASUREMENTS, "peer_measurements", DT_NESTED, 0, &nl80211_peer_measurements_nla }, 1083 } 1084 }; 1085 1086 static const uc_nl_nested_spec_t hwsim_msg = { 1087 .headsize = 0, 1088 .nattrs = 27, 1089 .attrs = { 1090 { HWSIM_ATTR_ADDR_RECEIVER, "addr_receiver", DT_LLADDR, 0, NULL }, 1091 { HWSIM_ATTR_ADDR_TRANSMITTER, "addr_transmitter", DT_LLADDR, 0, NULL }, 1092 { HWSIM_ATTR_FRAME, "frame", DT_STRING, DF_BINARY, NULL }, 1093 { HWSIM_ATTR_FLAGS, "flags", DT_U32, 0, NULL }, 1094 { HWSIM_ATTR_RX_RATE, "rx_rate", DT_U32, 0, NULL }, 1095 { HWSIM_ATTR_SIGNAL, "signal", DT_U32, 0, NULL }, 1096 { HWSIM_ATTR_TX_INFO, "tx_info", DT_NESTED, DF_ARRAY, &hwsim_tx_info_struct }, 1097 { HWSIM_ATTR_COOKIE, "cookie", DT_U64, 0, NULL }, 1098 { HWSIM_ATTR_CHANNELS, "channels", DT_U32, 0, NULL }, 1099 { HWSIM_ATTR_RADIO_ID, "radio_id", DT_U32, 0, NULL }, 1100 { HWSIM_ATTR_REG_HINT_ALPHA2, "reg_hint_alpha2", DT_STRING, DF_BINARY, NULL }, 1101 { HWSIM_ATTR_REG_CUSTOM_REG, "reg_custom_reg", DT_U32, 0, NULL }, 1102 { HWSIM_ATTR_REG_STRICT_REG, "reg_strict_reg", DT_FLAG, 0, NULL }, 1103 { HWSIM_ATTR_SUPPORT_P2P_DEVICE, "support_p2p_device", DT_FLAG, 0, NULL }, 1104 { HWSIM_ATTR_USE_CHANCTX, "use_chanctx", DT_FLAG, 0, NULL }, 1105 { HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, "destroy_radio_on_close", DT_FLAG, 0, NULL }, 1106 { HWSIM_ATTR_RADIO_NAME, "radio_name", DT_STRING, DF_BINARY, NULL }, 1107 { HWSIM_ATTR_NO_VIF, "no_vif", DT_FLAG, 0, NULL }, 1108 { HWSIM_ATTR_FREQ, "freq", DT_U32, 0, NULL }, 1109 { HWSIM_ATTR_TX_INFO_FLAGS, "tx_info_flags", DT_NESTED, DF_ARRAY, &hwsim_tx_info_flags_struct }, 1110 { HWSIM_ATTR_PERM_ADDR, "perm_addr", DT_LLADDR, 0, NULL }, 1111 { HWSIM_ATTR_IFTYPE_SUPPORT, "iftype_support", DT_U32, 0, NULL }, 1112 { HWSIM_ATTR_CIPHER_SUPPORT, "cipher_support", DT_U32, DF_ARRAY, NULL }, 1113 { HWSIM_ATTR_MLO_SUPPORT, "mlo_support", DT_FLAG, 0, NULL }, 1114 { HWSIM_ATTR_PMSR_SUPPORT, "pmsr_support", DT_NESTED, 0, &hwsim_pmsr_support_nla }, 1115 { HWSIM_ATTR_PMSR_REQUEST, "pmsr_request", DT_NESTED, 0, &hwsim_pmsr_request_nla }, 1116 { HWSIM_ATTR_PMSR_RESULT, "pmsr_result", DT_NESTED, 0, &hwsim_pmsr_support_nla }, 1117 } 1118 }; 1119 1120 1121 static bool 1122 nla_check_len(struct nlattr *nla, size_t sz) 1123 { 1124 return (nla && nla_len(nla) >= (ssize_t)sz); 1125 } 1126 1127 static bool 1128 nla_parse_error(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *v, const char *msg) 1129 { 1130 char *s; 1131 1132 s = ucv_to_string(vm, v); 1133 1134 set_error(NLE_INVAL, "%s `%s` has invalid value `%s`: %s", 1135 spec->attr ? "attribute" : "field", 1136 spec->key, 1137 s, 1138 msg); 1139 1140 free(s); 1141 1142 return false; 1143 } 1144 1145 static void 1146 uc_nl_put_struct_member(char *base, const void *offset, size_t datalen, void *data) 1147 { 1148 memcpy(base + (uintptr_t)offset, data, datalen); 1149 } 1150 1151 static void 1152 uc_nl_put_struct_member_u8(char *base, const void *offset, uint8_t u8) 1153 { 1154 base[(uintptr_t)offset] = u8; 1155 } 1156 1157 static void 1158 uc_nl_put_struct_member_u32(char *base, const void *offset, uint32_t u32) 1159 { 1160 uc_nl_put_struct_member(base, offset, sizeof(u32), &u32); 1161 } 1162 1163 static void * 1164 uc_nl_get_struct_member(char *base, const void *offset, size_t datalen, void *data) 1165 { 1166 memcpy(data, base + (uintptr_t)offset, datalen); 1167 1168 return data; 1169 } 1170 1171 static uint8_t 1172 uc_nl_get_struct_member_u8(char *base, const void *offset) 1173 { 1174 return (uint8_t)base[(uintptr_t)offset]; 1175 } 1176 1177 static uint32_t 1178 uc_nl_get_struct_member_u32(char *base, const void *offset) 1179 { 1180 uint32_t u32; 1181 1182 uc_nl_get_struct_member(base, offset, sizeof(u32), &u32); 1183 1184 return u32; 1185 } 1186 1187 static bool 1188 uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx); 1189 1190 static uc_value_t * 1191 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr *attr, struct nlattr *attr2, uc_vm_t *vm); 1192 1193 static bool 1194 uc_nl_convert_attrs(struct nl_msg *msg, void *buf, size_t buflen, size_t headsize, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj) 1195 { 1196 struct nlattr **tb, *nla, *nla2, *nla_nest; 1197 size_t i, type, maxattr = 0; 1198 uc_value_t *v, *arr; 1199 int rem; 1200 1201 for (i = 0; i < nattrs; i++) 1202 if (attrs[i].attr > maxattr) 1203 maxattr = attrs[i].attr; 1204 1205 tb = calloc(maxattr + 1, sizeof(struct nlattr *)); 1206 1207 if (!tb) 1208 return false; 1209 1210 nla_for_each_attr(nla, buf + headsize, buflen - headsize, rem) { 1211 type = nla_type(nla); 1212 1213 if (type <= maxattr && !tb[type]) 1214 tb[type] = nla; 1215 } 1216 1217 for (i = 0; i < nattrs; i++) { 1218 if (attrs[i].attr != 0 && !tb[attrs[i].attr]) 1219 continue; 1220 1221 if (attrs[i].flags & DF_REPEATED) { 1222 arr = ucv_array_new(vm); 1223 1224 nla = tb[attrs[i].attr]; 1225 rem = buflen - ((void *)nla - buf); 1226 for (; nla_ok(nla, rem); nla = nla_next(nla, &rem)) { 1227 if (nla_type(nla) != (int)attrs[i].attr) 1228 break; 1229 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, nla, NULL, vm); 1230 if (!v) 1231 continue; 1232 1233 ucv_array_push(arr, v); 1234 } 1235 if (!ucv_array_length(arr)) { 1236 ucv_put(arr); 1237 continue; 1238 } 1239 1240 v = arr; 1241 } 1242 else if (attrs[i].flags & DF_MULTIPLE) { 1243 arr = ucv_array_new(vm); 1244 nla_nest = tb[attrs[i].attr]; 1245 1246 nla_for_each_attr(nla, nla_data(nla_nest), nla_len(nla_nest), rem) { 1247 if (!(attrs[i].flags & (DF_AUTOIDX|DF_TYPEIDX)) && 1248 attrs[i].auxdata && nla_type(nla) != (intptr_t)attrs[i].auxdata) 1249 continue; 1250 1251 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, nla, NULL, vm); 1252 1253 if (!v) 1254 continue; 1255 1256 if (attrs[i].flags & DF_TYPEIDX) 1257 ucv_array_set(arr, nla_type(nla) - !!(attrs[i].flags & DF_OFFSET1), v); 1258 else 1259 ucv_array_push(arr, v); 1260 } 1261 1262 if (!ucv_array_length(arr)) { 1263 ucv_put(arr); 1264 1265 continue; 1266 } 1267 1268 v = arr; 1269 } 1270 else { 1271 if (attrs[i].flags & DF_RELATED) 1272 nla2 = tb[(uintptr_t)attrs[i].auxdata]; 1273 else 1274 nla2 = NULL; 1275 1276 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb[attrs[i].attr], nla2, vm); 1277 1278 if (!v) 1279 continue; 1280 } 1281 1282 ucv_object_add(obj, attrs[i].key, v); 1283 } 1284 1285 free(tb); 1286 1287 return true; 1288 } 1289 1290 static bool 1291 uc_nl_parse_attrs(struct nl_msg *msg, char *base, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj) 1292 { 1293 struct nlattr *nla_nest = NULL; 1294 uc_value_t *v, *item; 1295 size_t i, j, idx; 1296 bool exists; 1297 1298 for (i = 0; i < nattrs; i++) { 1299 if (attrs[i].attr == NL80211_ATTR_NOT_IMPLEMENTED) 1300 continue; 1301 1302 v = ucv_object_get(obj, attrs[i].key, &exists); 1303 1304 if (!exists) 1305 continue; 1306 1307 if (attrs[i].flags & DF_MULTIPLE) { 1308 nla_nest = nla_nest_start(msg, attrs[i].attr); 1309 1310 if (ucv_type(v) == UC_ARRAY) { 1311 for (j = 0; j < ucv_array_length(v); j++) { 1312 item = ucv_array_get(v, j); 1313 1314 if (!item && (attrs[i].flags & DF_TYPEIDX)) 1315 continue; 1316 1317 if (!attrs[i].auxdata || (attrs[i].flags & (DF_AUTOIDX|DF_TYPEIDX))) 1318 idx = j + !!(attrs[i].flags & DF_OFFSET1); 1319 else 1320 idx = (uintptr_t)attrs[i].auxdata; 1321 1322 if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, item, idx)) 1323 return false; 1324 } 1325 } 1326 else { 1327 if (!attrs[i].auxdata || (attrs[i].flags & (DF_AUTOIDX|DF_TYPEIDX))) 1328 idx = !!(attrs[i].flags & DF_OFFSET1); 1329 else 1330 idx = (uintptr_t)attrs[i].auxdata; 1331 1332 if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, idx)) 1333 return false; 1334 } 1335 1336 nla_nest_end(msg, nla_nest); 1337 } 1338 else if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, 0)) { 1339 return false; 1340 } 1341 } 1342 1343 return true; 1344 } 1345 1346 static bool 1347 uc_nl_parse_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val) 1348 { 1349 const uc_nl_nested_spec_t *nest = spec->auxdata; 1350 struct nlattr *nested_nla; 1351 1352 if (!nest) 1353 return false; 1354 1355 nested_nla = nla_reserve(msg, spec->attr, nest->headsize); 1356 1357 if (!uc_nl_parse_attrs(msg, nla_data(nested_nla), nest->attrs, nest->nattrs, vm, val)) 1358 return false; 1359 1360 nla_nest_end(msg, nested_nla); 1361 1362 return true; 1363 } 1364 1365 static uc_value_t * 1366 uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) 1367 { 1368 const uc_nl_nested_spec_t *nest = spec->auxdata; 1369 uc_value_t *nested_obj; 1370 bool rv; 1371 1372 if (!nest) 1373 return NULL; 1374 1375 if (!nla_check_len(attr, nest->headsize)) 1376 return NULL; 1377 1378 nested_obj = ucv_object_new(vm); 1379 1380 rv = uc_nl_convert_attrs(msg, 1381 nla_data(attr), nla_len(attr), nest->headsize, 1382 nest->attrs, nest->nattrs, 1383 vm, nested_obj); 1384 1385 if (!rv) { 1386 ucv_put(nested_obj); 1387 1388 return NULL; 1389 } 1390 1391 return nested_obj; 1392 } 1393 1394 static uc_value_t * 1395 uc_nl_convert_rta_ht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) 1396 { 1397 uc_value_t *mcs_obj, *mcs_idx; 1398 uint16_t max_rate = 0; 1399 uint8_t *mcs; 1400 size_t i; 1401 1402 if (!nla_check_len(attr, 16)) 1403 return NULL; 1404 1405 mcs = nla_data(attr); 1406 mcs_obj = ucv_object_new(vm); 1407 1408 max_rate = (mcs[10] | ((mcs[11] & 0x3) << 8)); 1409 1410 if (max_rate) 1411 ucv_object_add(mcs_obj, "rx_highest_data_rate", ucv_uint64_new(max_rate)); 1412 1413 mcs_idx = ucv_array_new(vm); 1414 1415 for (i = 0; i <= 76; i++) 1416 if (mcs[i / 8] & (1 << (i % 8))) 1417 ucv_array_push(mcs_idx, ucv_uint64_new(i)); 1418 1419 ucv_object_add(mcs_obj, "rx_mcs_indexes", mcs_idx); 1420 1421 ucv_object_add(mcs_obj, "tx_mcs_set_defined", ucv_boolean_new(mcs[12] & (1 << 0))); 1422 ucv_object_add(mcs_obj, "tx_rx_mcs_set_equal", ucv_boolean_new(!(mcs[12] & (1 << 1)))); 1423 ucv_object_add(mcs_obj, "tx_max_spatial_streams", ucv_uint64_new(((mcs[12] >> 2) & 3) + 1)); 1424 ucv_object_add(mcs_obj, "tx_unequal_modulation", ucv_boolean_new(mcs[12] & (1 << 4))); 1425 1426 return mcs_obj; 1427 } 1428 1429 static uc_value_t * 1430 uc_nl_convert_rta_ht_cap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) 1431 { 1432 uc_value_t *cap_obj, *mcs_obj, *rx_mask; 1433 struct ieee80211_ht_cap *cap; 1434 size_t i; 1435 1436 if (!nla_check_len(attr, sizeof(*cap))) 1437 return NULL; 1438 1439 cap = nla_data(attr); 1440 cap_obj = ucv_object_new(vm); 1441 1442 ucv_object_add(cap_obj, "cap_info", ucv_uint64_new(le16toh(cap->cap_info))); 1443 ucv_object_add(cap_obj, "ampdu_params_info", ucv_uint64_new(cap->ampdu_params_info)); 1444 ucv_object_add(cap_obj, "extended_ht_cap_info", ucv_uint64_new(le16toh(cap->extended_ht_cap_info))); 1445 ucv_object_add(cap_obj, "tx_BF_cap_info", ucv_uint64_new(le32toh(cap->tx_BF_cap_info))); 1446 ucv_object_add(cap_obj, "antenna_selection_info", ucv_uint64_new(cap->antenna_selection_info)); 1447 1448 mcs_obj = ucv_object_new(vm); 1449 rx_mask = ucv_array_new_length(vm, sizeof(cap->mcs.rx_mask)); 1450 1451 for (i = 0; i < sizeof(cap->mcs.rx_mask); i++) 1452 ucv_array_push(rx_mask, ucv_uint64_new(cap->mcs.rx_mask[i])); 1453 1454 ucv_object_add(mcs_obj, "rx_mask", rx_mask); 1455 ucv_object_add(mcs_obj, "rx_highest", ucv_uint64_new(le16toh(cap->mcs.rx_highest))); 1456 ucv_object_add(mcs_obj, "tx_params", ucv_uint64_new(cap->mcs.tx_params)); 1457 1458 ucv_object_add(cap_obj, "mcs", mcs_obj); 1459 1460 return cap_obj; 1461 } 1462 1463 static uc_value_t * 1464 uc_nl_convert_rta_vht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) 1465 { 1466 uc_value_t *mcs_obj, *mcs_set, *mcs_entry, *mcs_idx; 1467 size_t i, j, max_idx; 1468 uint16_t u16; 1469 uint8_t *mcs; 1470 1471 if (!nla_check_len(attr, 8)) 1472 return NULL; 1473 1474 mcs = nla_data(attr); 1475 mcs_obj = ucv_object_new(vm); 1476 1477 u16 = mcs[0] | (mcs[1] << 8); 1478 mcs_set = ucv_array_new(vm); 1479 1480 for (i = 1; i <= 8; i++) { 1481 switch ((u16 >> ((i - 1) * 2)) & 3) { 1482 case 0: max_idx = 7; break; 1483 case 1: max_idx = 8; break; 1484 case 2: max_idx = 9; break; 1485 default: continue; 1486 } 1487 1488 mcs_idx = ucv_array_new_length(vm, max_idx + 1); 1489 1490 for (j = 0; j <= max_idx; j++) 1491 ucv_array_push(mcs_idx, ucv_uint64_new(j)); 1492 1493 mcs_entry = ucv_object_new(vm); 1494 1495 ucv_object_add(mcs_entry, "streams", ucv_uint64_new(i)); 1496 ucv_object_add(mcs_entry, "mcs_indexes", mcs_idx); 1497 1498 ucv_array_push(mcs_set, mcs_entry); 1499 } 1500 1501 ucv_object_add(mcs_obj, "rx_mcs_set", mcs_set); 1502 ucv_object_add(mcs_obj, "rx_highest_data_rate", ucv_uint64_new((mcs[2] | (mcs[3] << 8)) & 0x1fff)); 1503 1504 u16 = mcs[4] | (mcs[5] << 8); 1505 mcs_set = ucv_array_new(vm); 1506 1507 for (i = 1; i <= 8; i++) { 1508 switch ((u16 >> ((i - 1) * 2)) & 3) { 1509 case 0: max_idx = 7; break; 1510 case 1: max_idx = 8; break; 1511 case 2: max_idx = 9; break; 1512 default: continue; 1513 } 1514 1515 mcs_idx = ucv_array_new_length(vm, max_idx + 1); 1516 1517 for (j = 0; j <= max_idx; j++) 1518 ucv_array_push(mcs_idx, ucv_uint64_new(j)); 1519 1520 mcs_entry = ucv_object_new(vm); 1521 1522 ucv_object_add(mcs_entry, "streams", ucv_uint64_new(i)); 1523 ucv_object_add(mcs_entry, "mcs_indexes", mcs_idx); 1524 1525 ucv_array_push(mcs_set, mcs_entry); 1526 } 1527 1528 ucv_object_add(mcs_obj, "tx_mcs_set", mcs_set); 1529 ucv_object_add(mcs_obj, "tx_highest_data_rate", ucv_uint64_new((mcs[6] | (mcs[7] << 8)) & 0x1fff)); 1530 1531 return mcs_obj; 1532 } 1533 1534 static uc_value_t * 1535 uc_nl_convert_rta_he_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, struct nlattr *phy_attr, uc_vm_t *vm) 1536 { 1537 uint8_t bw_support_mask[] = { (1 << 1) | (1 << 2), (1 << 3), (1 << 4) }; 1538 uc_value_t *mcs_set, *mcs_bw, *mcs_dir, *mcs_entry, *mcs_idx; 1539 uint16_t bw[] = { 80, 160, 8080 }, mcs[6]; 1540 uint16_t u16, phy_cap_0 = 0; 1541 size_t i, j, k, l, max_idx; 1542 1543 if (!nla_check_len(attr, sizeof(mcs))) 1544 return NULL; 1545 1546 if (nla_check_len(phy_attr, sizeof(phy_cap_0))) 1547 phy_cap_0 = nla_get_u16(phy_attr); 1548 1549 memcpy(mcs, nla_data(attr), sizeof(mcs)); 1550 1551 mcs_set = ucv_array_new_length(vm, 3); 1552 1553 for (i = 0; i < ARRAY_SIZE(bw); i++) { 1554 if (!(phy_cap_0 & (bw_support_mask[i] << 8))) 1555 continue; 1556 1557 mcs_bw = ucv_object_new(vm); 1558 1559 for (j = 0; j < 2; j++) { 1560 mcs_dir = ucv_array_new_length(vm, 8); 1561 1562 for (k = 0; k < 8; k++) { 1563 u16 = mcs[(i * 2) + j]; 1564 u16 >>= k * 2; 1565 u16 &= 0x3; 1566 1567 switch (u16) { 1568 case 0: max_idx = 7; break; 1569 case 1: max_idx = 9; break; 1570 case 2: max_idx = 11; break; 1571 case 3: continue; 1572 } 1573 1574 mcs_idx = ucv_array_new_length(vm, max_idx + 1); 1575 1576 for (l = 0; l <= max_idx; l++) 1577 ucv_array_push(mcs_idx, ucv_uint64_new(l)); 1578 1579 mcs_entry = ucv_object_new(vm); 1580 1581 ucv_object_add(mcs_entry, "streams", ucv_uint64_new(k + 1)); 1582 ucv_object_add(mcs_entry, "mcs_indexes", mcs_idx); 1583 1584 ucv_array_push(mcs_dir, mcs_entry); 1585 } 1586 1587 if (ucv_array_length(mcs_dir)) 1588 ucv_object_add(mcs_bw, j ? "tx_mcs_set" : "rx_mcs_set", mcs_dir); 1589 else 1590 ucv_put(mcs_dir); 1591 } 1592 1593 if (ucv_object_length(mcs_bw)) { 1594 ucv_object_add(mcs_bw, "bandwidth", ucv_uint64_new(bw[i])); 1595 ucv_array_push(mcs_set, mcs_bw); 1596 } 1597 else { 1598 ucv_put(mcs_bw); 1599 } 1600 } 1601 1602 return mcs_set; 1603 } 1604 1605 static uc_value_t * 1606 uc_nl_convert_rta_ie(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) 1607 { 1608 uc_value_t *ie_arr, *ie_obj; 1609 uint8_t *ie; 1610 size_t len; 1611 1612 len = nla_len(attr); 1613 ie = nla_data(attr); 1614 1615 if (len < 2) 1616 return NULL; 1617 1618 ie_arr = ucv_array_new(vm); 1619 1620 while (len >= 2 && len - 2 >= ie[1]) { 1621 ie_obj = ucv_object_new(vm); 1622 1623 ucv_object_add(ie_obj, "type", ucv_uint64_new(ie[0])); 1624 ucv_object_add(ie_obj, "data", ucv_string_new_length((char *)&ie[2], ie[1])); 1625 1626 ucv_array_push(ie_arr, ie_obj); 1627 1628 len -= ie[1] + 2; 1629 ie += ie[1] + 2; 1630 } 1631 1632 return ie_arr; 1633 } 1634 1635 1636 static bool 1637 uc_nl_parse_numval(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, void *dst) 1638 { 1639 uint64_t u64; 1640 uint32_t u32; 1641 uint16_t u16; 1642 uint8_t u8; 1643 1644 switch (spec->type) { 1645 case DT_U8: 1646 if (!uc_nl_parse_u32(val, &u32) || u32 > 255) 1647 return nla_parse_error(spec, vm, val, "not an integer or out of range 0-255"); 1648 1649 u8 = (uint8_t)u32; 1650 1651 memcpy(dst, &u8, sizeof(u8)); 1652 break; 1653 1654 case DT_U16: 1655 if (!uc_nl_parse_u32(val, &u32) || u32 > 65535) 1656 return nla_parse_error(spec, vm, val, "not an integer or out of range 0-65535"); 1657 1658 u16 = (uint16_t)u32; 1659 1660 memcpy(dst, &u16, sizeof(u16)); 1661 break; 1662 1663 case DT_S32: 1664 case DT_U32: 1665 if (spec->type == DT_S32 && !uc_nl_parse_s32(val, &u32)) 1666 return nla_parse_error(spec, vm, val, "not an integer or out of range -2147483648-2147483647"); 1667 else if (spec->type == DT_U32 && !uc_nl_parse_u32(val, &u32)) 1668 return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295"); 1669 1670 memcpy(dst, &u32, sizeof(u32)); 1671 break; 1672 1673 case DT_U64: 1674 if (!uc_nl_parse_u64(val, &u64)) 1675 return nla_parse_error(spec, vm, val, "not an integer or negative"); 1676 1677 memcpy(dst, &u64, sizeof(u64)); 1678 break; 1679 1680 default: 1681 return false; 1682 } 1683 1684 return true; 1685 } 1686 1687 static const uint8_t dt_sizes[] = { 1688 [DT_U8] = sizeof(uint8_t), 1689 [DT_S8] = sizeof(int8_t), 1690 [DT_U16] = sizeof(uint16_t), 1691 [DT_U32] = sizeof(uint32_t), 1692 [DT_S32] = sizeof(int32_t), 1693 [DT_U64] = sizeof(uint64_t), 1694 }; 1695 1696 static bool 1697 uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx) 1698 { 1699 char buf[sizeof(uint64_t)]; 1700 struct in_addr in = { 0 }; 1701 struct ether_addr *ea; 1702 struct nlattr *nla; 1703 uc_value_t *item; 1704 size_t attr, i; 1705 uint32_t u32; 1706 char *s; 1707 1708 if (spec->flags & DF_MULTIPLE) 1709 attr = idx; 1710 else 1711 attr = spec->attr; 1712 1713 switch (spec->type) { 1714 case DT_U8: 1715 case DT_U16: 1716 case DT_U32: 1717 case DT_S32: 1718 case DT_U64: 1719 if (spec->flags & DF_ARRAY) { 1720 assert(spec->attr != 0); 1721 1722 if (ucv_type(val) != UC_ARRAY) 1723 return nla_parse_error(spec, vm, val, "not an array"); 1724 1725 nla = nla_reserve(msg, spec->attr, ucv_array_length(val) * dt_sizes[spec->type]); 1726 s = nla_data(nla); 1727 1728 for (i = 0; i < ucv_array_length(val); i++) { 1729 item = ucv_array_get(val, i); 1730 1731 if (!uc_nl_parse_numval(spec, msg, base, vm, item, buf)) 1732 return false; 1733 1734 memcpy(s, buf, dt_sizes[spec->type]); 1735 1736 s += dt_sizes[spec->type]; 1737 } 1738 } 1739 else { 1740 if (!uc_nl_parse_numval(spec, msg, base, vm, val, buf)) 1741 return false; 1742 1743 if (spec->attr == 0) 1744 uc_nl_put_struct_member(base, spec->auxdata, dt_sizes[spec->type], buf); 1745 else 1746 nla_put(msg, attr, dt_sizes[spec->type], buf); 1747 } 1748 1749 break; 1750 1751 case DT_BOOL: 1752 u32 = (uint32_t)ucv_is_truish(val); 1753 1754 if (spec->attr == 0) 1755 uc_nl_put_struct_member_u8(base, spec->auxdata, u32); 1756 else 1757 nla_put_u8(msg, attr, u32); 1758 1759 break; 1760 1761 case DT_FLAG: 1762 u32 = (uint32_t)ucv_is_truish(val); 1763 1764 if (spec->attr == 0) 1765 uc_nl_put_struct_member_u8(base, spec->auxdata, u32); 1766 else if (u32 == 1) 1767 nla_put_flag(msg, attr); 1768 1769 break; 1770 1771 case DT_STRING: 1772 assert(spec->attr != 0); 1773 1774 if (ucv_type(val) == UC_STRING) { 1775 nla_put(msg, attr, 1776 ucv_string_length(val) + !(spec->flags & DF_BINARY), 1777 ucv_string_get(val)); 1778 } 1779 else { 1780 s = ucv_to_string(vm, val); 1781 1782 if (!s) 1783 return nla_parse_error(spec, vm, val, "out of memory"); 1784 1785 nla_put(msg, attr, strlen(s) + !(spec->flags & DF_BINARY), s); 1786 free(s); 1787 } 1788 1789 break; 1790 1791 case DT_NETDEV: 1792 if (ucv_type(val) == UC_INTEGER) { 1793 if (ucv_int64_get(val) < 0 || 1794 ucv_int64_get(val) > UINT32_MAX) 1795 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295"); 1796 1797 u32 = (uint32_t)ucv_int64_get(val); 1798 } 1799 else { 1800 s = ucv_to_string(vm, val); 1801 1802 if (!s) 1803 return nla_parse_error(spec, vm, val, "out of memory"); 1804 1805 u32 = if_nametoindex(s); 1806 1807 free(s); 1808 } 1809 1810 if (spec->attr == 0) 1811 uc_nl_put_struct_member_u32(base, spec->auxdata, u32); 1812 else 1813 nla_put_u32(msg, attr, u32); 1814 1815 break; 1816 1817 case DT_LLADDR: 1818 assert(spec->attr != 0); 1819 1820 s = ucv_to_string(vm, val); 1821 1822 if (!s) 1823 return nla_parse_error(spec, vm, val, "out of memory"); 1824 1825 ea = ether_aton(s); 1826 1827 free(s); 1828 1829 if (!ea) 1830 return nla_parse_error(spec, vm, val, "invalid MAC address"); 1831 1832 nla_put(msg, attr, sizeof(*ea), ea); 1833 1834 break; 1835 1836 case DT_INADDR: 1837 assert(spec->attr != 0); 1838 1839 if (!uc_nl_parse_ipaddr(vm, val, &in)) 1840 return nla_parse_error(spec, vm, val, "invalid IP address"); 1841 1842 nla_put(msg, attr, sizeof(in), &in); 1843 1844 break; 1845 1846 case DT_NESTED: 1847 if (spec->flags & DF_ARRAY) { 1848 const uc_nl_nested_spec_t *nested = spec->auxdata; 1849 1850 assert(nested != NULL); 1851 assert(nested->headsize > 0); 1852 1853 if (ucv_type(val) != UC_ARRAY) 1854 return nla_parse_error(spec, vm, val, "not an array"); 1855 1856 nla = nla_reserve(msg, spec->attr, ucv_array_length(val) * nested->headsize); 1857 s = nla_data(nla); 1858 1859 for (i = 0; i < ucv_array_length(val); i++) { 1860 item = ucv_array_get(val, i); 1861 1862 if (!uc_nl_parse_attrs(msg, s, nested->attrs, nested->nattrs, vm, item)) 1863 return false; 1864 1865 s += nested->headsize; 1866 } 1867 1868 return true; 1869 } 1870 1871 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val)) 1872 return false; 1873 1874 break; 1875 1876 default: 1877 assert(0); 1878 } 1879 1880 return true; 1881 } 1882 1883 static uc_value_t * 1884 uc_nl_convert_numval(const uc_nl_attr_spec_t *spec, char *base) 1885 { 1886 union { uint8_t *u8; uint16_t *u16; uint32_t *u32; uint64_t *u64; char *base; } t = { .base = base }; 1887 1888 switch (spec->type) { 1889 case DT_U8: 1890 return ucv_uint64_new(t.u8[0]); 1891 1892 case DT_S8: 1893 return ucv_int64_new((int8_t)t.u8[0]); 1894 1895 case DT_U16: 1896 return ucv_uint64_new(t.u16[0]); 1897 1898 case DT_U32: 1899 return ucv_uint64_new(t.u32[0]); 1900 1901 case DT_S32: 1902 return ucv_int64_new((int32_t)t.u32[0]); 1903 1904 case DT_U64: 1905 return ucv_uint64_new(t.u64[0]); 1906 1907 default: 1908 return NULL; 1909 } 1910 } 1911 1912 static uc_value_t * 1913 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr *attr, struct nlattr *attr2, uc_vm_t *vm) 1914 { 1915 union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 }; 1916 char buf[sizeof("FF:FF:FF:FF:FF:FF")]; 1917 struct ether_addr *ea; 1918 uc_value_t *v; 1919 int i; 1920 1921 switch (spec->type) { 1922 case DT_U8: 1923 case DT_S8: 1924 case DT_U16: 1925 case DT_U32: 1926 case DT_S32: 1927 case DT_U64: 1928 if (spec->flags & DF_ARRAY) { 1929 assert(spec->attr != 0); 1930 assert((nla_len(attr) % dt_sizes[spec->type]) == 0); 1931 1932 v = ucv_array_new_length(vm, nla_len(attr) / dt_sizes[spec->type]); 1933 1934 for (i = 0; i < nla_len(attr); i += dt_sizes[spec->type]) 1935 ucv_array_push(v, uc_nl_convert_numval(spec, nla_data(attr) + i)); 1936 1937 return v; 1938 } 1939 else if (spec->attr == 0) { 1940 return uc_nl_convert_numval(spec, base + (uintptr_t)spec->auxdata); 1941 } 1942 else if (nla_check_len(attr, dt_sizes[spec->type])) { 1943 return uc_nl_convert_numval(spec, nla_data(attr)); 1944 } 1945 1946 return NULL; 1947 1948 case DT_BOOL: 1949 if (spec->attr == 0) 1950 t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata); 1951 else if (nla_check_len(attr, sizeof(t.u8))) 1952 t.u8 = nla_get_u8(attr); 1953 1954 return ucv_boolean_new(t.u8 != 0); 1955 1956 case DT_FLAG: 1957 if (spec->attr == 0) 1958 t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata); 1959 else if (attr != NULL) 1960 t.u8 = 1; 1961 1962 return ucv_boolean_new(t.u8 != 0); 1963 1964 case DT_STRING: 1965 assert(spec->attr != 0); 1966 1967 if (!nla_check_len(attr, 1)) 1968 return NULL; 1969 1970 t.sz = nla_len(attr); 1971 1972 if (!(spec->flags & DF_BINARY)) 1973 t.sz -= 1; 1974 1975 return ucv_string_new_length(nla_data(attr), t.sz); 1976 1977 case DT_NETDEV: 1978 if (spec->attr == 0) 1979 t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata); 1980 else if (nla_check_len(attr, sizeof(t.u32))) 1981 t.u32 = nla_get_u32(attr); 1982 1983 if (if_indextoname(t.u32, buf)) 1984 return ucv_string_new(buf); 1985 1986 return NULL; 1987 1988 case DT_LLADDR: 1989 assert(spec->attr != 0); 1990 1991 if (!nla_check_len(attr, sizeof(*ea))) 1992 return NULL; 1993 1994 ea = nla_data(attr); 1995 1996 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 1997 ea->ether_addr_octet[0], ea->ether_addr_octet[1], 1998 ea->ether_addr_octet[2], ea->ether_addr_octet[3], 1999 ea->ether_addr_octet[4], ea->ether_addr_octet[5]); 2000 2001 return ucv_string_new(buf); 2002 2003 case DT_INADDR: 2004 assert(spec->attr != 0); 2005 2006 if (!nla_check_len(attr, sizeof(struct in_addr)) || 2007 !inet_ntop(AF_INET, nla_data(attr), buf, sizeof(buf))) 2008 return NULL; 2009 2010 return ucv_string_new(buf); 2011 2012 case DT_NESTED: 2013 if (spec->flags & DF_ARRAY) { 2014 const uc_nl_nested_spec_t *nested = spec->auxdata; 2015 2016 assert(nested != NULL); 2017 assert(nested->headsize > 0); 2018 assert((nla_len(attr) % nested->headsize) == 0); 2019 2020 v = ucv_array_new_length(vm, nla_len(attr) / nested->headsize); 2021 2022 for (i = 0; i < nla_len(attr); i += nested->headsize) { 2023 uc_value_t *item = ucv_object_new(vm); 2024 2025 ucv_array_push(v, item); 2026 2027 bool rv = uc_nl_convert_attrs(msg, 2028 nla_data(attr) + i, nla_len(attr) - i, nested->headsize, 2029 nested->attrs, nested->nattrs, vm, item); 2030 2031 if (!rv) { 2032 ucv_put(v); 2033 2034 return NULL; 2035 } 2036 } 2037 2038 return v; 2039 } 2040 2041 return uc_nl_convert_rta_nested(spec, msg, attr, vm); 2042 2043 case DT_HT_MCS: 2044 return uc_nl_convert_rta_ht_mcs(spec, msg, attr, vm); 2045 2046 case DT_HT_CAP: 2047 return uc_nl_convert_rta_ht_cap(spec, msg, attr, vm); 2048 2049 case DT_VHT_MCS: 2050 return uc_nl_convert_rta_vht_mcs(spec, msg, attr, vm); 2051 2052 case DT_HE_MCS: 2053 return uc_nl_convert_rta_he_mcs(spec, msg, attr, attr2, vm); 2054 2055 case DT_IE: 2056 return uc_nl_convert_rta_ie(spec, msg, attr, vm); 2057 2058 default: 2059 assert(0); 2060 } 2061 2062 return NULL; 2063 } 2064 2065 2066 static struct { 2067 struct nl_sock *sock; 2068 struct nl_sock *evsock; 2069 struct nl_cache *cache; 2070 struct uloop_fd evsock_fd; 2071 struct nl_cb *evsock_cb; 2072 } nl80211_conn; 2073 2074 typedef enum { 2075 STATE_UNREPLIED, 2076 STATE_CONTINUE, 2077 STATE_REPLIED, 2078 STATE_ERROR 2079 } reply_state_t; 2080 2081 typedef struct { 2082 reply_state_t state; 2083 uc_vm_t *vm; 2084 uc_value_t *res; 2085 bool merge_phy_info; 2086 bool single_phy_info; 2087 const uc_nl_nested_spec_t *spec; 2088 } request_state_t; 2089 2090 2091 /** 2092 * Get the last error information 2093 * 2094 * This function returns information about the last error that occurred 2095 * in nl80211 operations. It provides both error code and error message. 2096 * 2097 * @returns {Object|null} Object with 'code' and 'msg' properties containing 2098 * the error information, or null if no error occurred 2099 * @example 2100 * // Send a request that might fail 2101 * let result = request(const.SOME_COMMAND, 0, invalid_params); 2102 * if (!result) { 2103 * let error = error(); 2104 * print('Error occurred:', error.code, error.msg); 2105 * } 2106 */ 2107 static uc_value_t * 2108 uc_nl_error(uc_vm_t *vm, size_t nargs) 2109 { 2110 uc_stringbuf_t *buf; 2111 const char *s; 2112 2113 if (last_error.code == 0) 2114 return NULL; 2115 2116 buf = ucv_stringbuf_new(); 2117 2118 if (last_error.code == NLE_FAILURE && last_error.msg) { 2119 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg)); 2120 } 2121 else { 2122 s = nl_geterror(last_error.code); 2123 2124 ucv_stringbuf_addstr(buf, s, strlen(s)); 2125 2126 if (last_error.msg) 2127 ucv_stringbuf_printf(buf, ": %s", last_error.msg); 2128 } 2129 2130 set_error(0, NULL); 2131 2132 return ucv_stringbuf_finish(buf); 2133 } 2134 2135 static int 2136 cb_done(struct nl_msg *msg, void *arg) 2137 { 2138 request_state_t *s = arg; 2139 2140 s->state = STATE_REPLIED; 2141 2142 return NL_STOP; 2143 } 2144 2145 static void 2146 deep_merge_array(uc_value_t *dest, uc_value_t *src); 2147 2148 static void 2149 deep_merge_object(uc_value_t *dest, uc_value_t *src); 2150 2151 static void 2152 deep_merge_array(uc_value_t *dest, uc_value_t *src) 2153 { 2154 uc_value_t *e, *v; 2155 size_t i; 2156 2157 if (ucv_type(dest) == UC_ARRAY && ucv_type(src) == UC_ARRAY) { 2158 for (i = 0; i < ucv_array_length(src); i++) { 2159 e = ucv_array_get(dest, i); 2160 v = ucv_array_get(src, i); 2161 2162 if (!e) 2163 ucv_array_set(dest, i, ucv_get(v)); 2164 else if (ucv_type(v) == UC_ARRAY) 2165 deep_merge_array(e, v); 2166 else if (ucv_type(v) == UC_OBJECT) 2167 deep_merge_object(e, v); 2168 } 2169 } 2170 } 2171 2172 static void 2173 deep_merge_object(uc_value_t *dest, uc_value_t *src) 2174 { 2175 uc_value_t *e; 2176 bool exists; 2177 2178 if (ucv_type(dest) == UC_OBJECT && ucv_type(src) == UC_OBJECT) { 2179 ucv_object_foreach(src, k, v) { 2180 e = ucv_object_get(dest, k, &exists); 2181 2182 if (!exists) 2183 ucv_object_add(dest, k, ucv_get(v)); 2184 else if (ucv_type(v) == UC_ARRAY) 2185 deep_merge_array(e, v); 2186 else if (ucv_type(v) == UC_OBJECT) 2187 deep_merge_object(e, v); 2188 } 2189 } 2190 } 2191 2192 static int 2193 cb_reply(struct nl_msg *msg, void *arg) 2194 { 2195 struct nlmsghdr *hdr = nlmsg_hdr(msg); 2196 struct genlmsghdr *gnlh = nlmsg_data(hdr); 2197 request_state_t *s = arg; 2198 uc_value_t *o, *idx; 2199 int64_t i; 2200 bool rv; 2201 2202 o = ucv_object_new(s->vm); 2203 2204 rv = uc_nl_convert_attrs(msg, 2205 genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), 2206 0, s->spec->attrs, s->spec->nattrs, s->vm, o); 2207 2208 if (rv) { 2209 if (hdr->nlmsg_flags & NLM_F_MULTI) { 2210 if (s->merge_phy_info && s->single_phy_info) { 2211 if (!s->res) { 2212 s->res = o; 2213 } 2214 else { 2215 deep_merge_object(s->res, o); 2216 ucv_put(o); 2217 } 2218 } 2219 else if (s->merge_phy_info) { 2220 idx = ucv_object_get(o, "wiphy", NULL); 2221 i = idx ? ucv_int64_get(idx) : -1; 2222 2223 if (i >= 0) { 2224 if (!s->res) 2225 s->res = ucv_array_new(s->vm); 2226 2227 idx = ucv_array_get(s->res, i); 2228 2229 if (idx) { 2230 deep_merge_object(idx, o); 2231 ucv_put(o); 2232 } 2233 else { 2234 ucv_array_set(s->res, i, o); 2235 } 2236 } 2237 } 2238 else { 2239 if (!s->res) 2240 s->res = ucv_array_new(s->vm); 2241 2242 ucv_array_push(s->res, o); 2243 } 2244 } 2245 else { 2246 s->res = o; 2247 } 2248 } 2249 else { 2250 ucv_put(o); 2251 } 2252 2253 s->state = STATE_CONTINUE; 2254 2255 return NL_SKIP; 2256 } 2257 2258 static bool 2259 uc_nl_connect_sock(struct nl_sock **sk, bool nonblocking) 2260 { 2261 int err, fd; 2262 2263 if (*sk) 2264 return true; 2265 2266 *sk = nl_socket_alloc(); 2267 2268 if (!*sk) { 2269 set_error(NLE_NOMEM, NULL); 2270 goto err; 2271 } 2272 2273 err = genl_connect(*sk); 2274 2275 if (err != 0) { 2276 set_error(err, NULL); 2277 goto err; 2278 } 2279 2280 fd = nl_socket_get_fd(*sk); 2281 2282 if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) { 2283 set_error(NLE_FAILURE, "unable to set FD_CLOEXEC flag on socket: %s", strerror(errno)); 2284 goto err; 2285 } 2286 2287 if (nonblocking) { 2288 err = nl_socket_set_nonblocking(*sk); 2289 2290 if (err != 0) { 2291 set_error(err, NULL); 2292 goto err; 2293 } 2294 } 2295 2296 return true; 2297 2298 err: 2299 if (*sk) { 2300 nl_socket_free(*sk); 2301 *sk = NULL; 2302 } 2303 2304 return false; 2305 } 2306 2307 static int 2308 uc_nl_find_family_id(const char *name) 2309 { 2310 struct genl_family *fam; 2311 2312 if (!nl80211_conn.cache && genl_ctrl_alloc_cache(nl80211_conn.sock, &nl80211_conn.cache)) 2313 return -NLE_NOMEM; 2314 2315 fam = genl_ctrl_search_by_name(nl80211_conn.cache, name); 2316 2317 if (!fam) 2318 return -NLE_OBJ_NOTFOUND; 2319 2320 return genl_family_get_id(fam); 2321 } 2322 2323 static int 2324 cb_errno(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) 2325 { 2326 int *ret = arg; 2327 2328 if (err->error > 0) { 2329 set_error(NLE_RANGE, 2330 "Illegal error code %d in netlink reply", err->error); 2331 2332 *ret = -(NLE_MAX + 1); 2333 } 2334 else { 2335 *ret = -nl_syserr2nlerr(err->error); 2336 } 2337 2338 return NL_STOP; 2339 } 2340 2341 static int 2342 cb_ack(struct nl_msg *msg, void *arg) 2343 { 2344 int *ret = arg; 2345 2346 *ret = 0; 2347 2348 return NL_STOP; 2349 } 2350 2351 static int 2352 cb_subscribe(struct nl_msg *msg, void *arg) 2353 { 2354 struct nlattr *nla, *tb[CTRL_ATTR_MAX + 1], *grp[CTRL_ATTR_MCAST_GRP_MAX + 1]; 2355 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 2356 struct { int id; const char *group; } *ret = arg; 2357 int rem; 2358 2359 nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); 2360 2361 if (!tb[CTRL_ATTR_MCAST_GROUPS]) 2362 return NL_SKIP; 2363 2364 nla_for_each_nested(nla, tb[CTRL_ATTR_MCAST_GROUPS], rem) { 2365 nla_parse(grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(nla), nla_len(nla), NULL); 2366 2367 if (!grp[CTRL_ATTR_MCAST_GRP_NAME] || !grp[CTRL_ATTR_MCAST_GRP_ID]) 2368 continue; 2369 2370 if (strncmp(nla_data(grp[CTRL_ATTR_MCAST_GRP_NAME]), 2371 ret->group, nla_len(grp[CTRL_ATTR_MCAST_GRP_NAME]))) 2372 continue; 2373 2374 ret->id = nla_get_u32(grp[CTRL_ATTR_MCAST_GRP_ID]); 2375 2376 break; 2377 } 2378 2379 return NL_SKIP; 2380 } 2381 2382 static bool 2383 uc_nl_subscribe(struct nl_sock *sk, const char *family, const char *group) 2384 { 2385 struct { int id; const char *group; } grp = { -NLE_OBJ_NOTFOUND, group }; 2386 struct nl_msg *msg; 2387 struct nl_cb *cb; 2388 int id, ret; 2389 2390 if (!uc_nl_connect_sock(&nl80211_conn.sock, false)) 2391 return NULL; 2392 2393 msg = nlmsg_alloc(); 2394 2395 if (!msg) 2396 err_return(NLE_NOMEM, NULL); 2397 2398 id = uc_nl_find_family_id("nlctrl"); 2399 2400 if (id < 0) 2401 err_return(-id, NULL); 2402 2403 genlmsg_put(msg, 0, 0, id, 0, 0, CTRL_CMD_GETFAMILY, 0); 2404 nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family); 2405 2406 cb = nl_cb_alloc(NL_CB_DEFAULT); 2407 2408 if (!cb) { 2409 nlmsg_free(msg); 2410 err_return(NLE_NOMEM, NULL); 2411 } 2412 2413 nl_send_auto_complete(nl80211_conn.sock, msg); 2414 2415 ret = 1; 2416 2417 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_ack, &ret); 2418 nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &ret); 2419 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_subscribe, &grp); 2420 2421 while (ret > 0) 2422 nl_recvmsgs(nl80211_conn.sock, cb); 2423 2424 nlmsg_free(msg); 2425 nl_cb_put(cb); 2426 2427 if (ret < 0) 2428 err_return(ret, NULL); 2429 2430 if (grp.id < 0) 2431 err_return(grp.id, NULL); 2432 2433 ret = nl_socket_add_membership(sk, grp.id); 2434 2435 if (ret != 0) 2436 err_return(ret, NULL); 2437 2438 return true; 2439 } 2440 2441 2442 struct waitfor_ctx { 2443 uint8_t cmd; 2444 uc_vm_t *vm; 2445 uc_value_t *res; 2446 uint32_t cmds[NL80211_CMDS_BITMAP_SIZE]; 2447 }; 2448 2449 static uc_value_t * 2450 uc_nl_prepare_event(uc_vm_t *vm, struct nl_msg *msg) 2451 { 2452 struct nlmsghdr *hdr = nlmsg_hdr(msg); 2453 struct genlmsghdr *gnlh = nlmsg_data(hdr); 2454 uc_value_t *o = ucv_object_new(vm); 2455 const uc_nl_attr_spec_t *attrs; 2456 size_t nattrs; 2457 2458 if (hdr->nlmsg_type == uc_nl_find_family_id("MAC80211_HWSIM")) { 2459 attrs = hwsim_msg.attrs; 2460 nattrs = hwsim_msg.nattrs; 2461 } 2462 else { 2463 attrs = nl80211_msg.attrs; 2464 nattrs = nl80211_msg.nattrs; 2465 } 2466 2467 if (!uc_nl_convert_attrs(msg, genlmsg_attrdata(gnlh, 0), 2468 genlmsg_attrlen(gnlh, 0), 0, attrs, nattrs, vm, o)) { 2469 ucv_put(o); 2470 return NULL; 2471 } 2472 2473 return o; 2474 } 2475 2476 static int 2477 cb_listener_event(struct nl_msg *msg, void *arg) 2478 { 2479 struct nlmsghdr *hdr = nlmsg_hdr(msg); 2480 struct genlmsghdr *gnlh = nlmsg_data(hdr); 2481 uc_vm_t *vm = listener_vm; 2482 2483 if (!nl80211_conn.evsock_fd.registered || !vm) 2484 return NL_SKIP; 2485 2486 for (size_t i = 0; i < ucv_array_length(listener_registry); i += 2) { 2487 uc_value_t *this = ucv_array_get(listener_registry, i); 2488 uc_value_t *func = ucv_array_get(listener_registry, i + 1); 2489 uc_nl_listener_t *l; 2490 uc_value_t *o, *data; 2491 2492 l = ucv_resource_data(this, "nl80211.listener"); 2493 if (!l) 2494 continue; 2495 2496 if (gnlh->cmd > NL80211_CMD_MAX || 2497 !(l->cmds[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32)))) 2498 continue; 2499 2500 if (!ucv_is_callable(func)) 2501 continue; 2502 2503 data = uc_nl_prepare_event(vm, msg); 2504 if (!data) 2505 return NL_SKIP; 2506 2507 o = ucv_object_new(vm); 2508 ucv_object_add(o, "cmd", ucv_int64_new(gnlh->cmd)); 2509 ucv_object_add(o, "msg", data); 2510 2511 uc_vm_stack_push(vm, ucv_get(this)); 2512 uc_vm_stack_push(vm, ucv_get(func)); 2513 uc_vm_stack_push(vm, o); 2514 2515 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) { 2516 uloop_end(); 2517 return NL_STOP; 2518 } 2519 2520 ucv_put(uc_vm_stack_pop(vm)); 2521 } 2522 2523 return NL_SKIP; 2524 } 2525 2526 static int 2527 cb_event(struct nl_msg *msg, void *arg) 2528 { 2529 struct nlmsghdr *hdr = nlmsg_hdr(msg); 2530 struct genlmsghdr *gnlh = nlmsg_data(hdr); 2531 struct waitfor_ctx *s = arg; 2532 uc_value_t *o; 2533 2534 cb_listener_event(msg, arg); 2535 2536 if (gnlh->cmd > NL80211_CMD_MAX || 2537 !(s->cmds[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32)))) 2538 return NL_SKIP; 2539 2540 o = uc_nl_prepare_event(s->vm, msg); 2541 if (o) 2542 s->res = o; 2543 2544 s->cmd = gnlh->cmd; 2545 2546 return NL_SKIP; 2547 } 2548 2549 static int 2550 cb_seq(struct nl_msg *msg, void *arg) 2551 { 2552 return NL_OK; 2553 } 2554 2555 static bool 2556 uc_nl_fill_cmds(uint32_t *cmd_bits, uc_value_t *cmds) 2557 { 2558 if (ucv_type(cmds) == UC_ARRAY) { 2559 for (size_t i = 0; i < ucv_array_length(cmds); i++) { 2560 int64_t n = ucv_int64_get(ucv_array_get(cmds, i)); 2561 2562 if (n >= HWSIM_CMD_OFFSET) 2563 n -= HWSIM_CMD_OFFSET; 2564 2565 if (errno || n < 0 || n > NL80211_CMD_MAX) 2566 return false; 2567 2568 cmd_bits[n / 32] |= (1 << (n % 32)); 2569 } 2570 } 2571 else if (ucv_type(cmds) == UC_INTEGER) { 2572 int64_t n = ucv_int64_get(cmds); 2573 2574 if (n >= HWSIM_CMD_OFFSET) 2575 n -= HWSIM_CMD_OFFSET; 2576 2577 if (errno || n < 0 || n > NL80211_CMD_MAX) 2578 return false; 2579 2580 cmd_bits[n / 32] |= (1 << (n % 32)); 2581 } 2582 else if (!cmds) 2583 memset(cmd_bits, 0xff, NL80211_CMDS_BITMAP_SIZE * sizeof(*cmd_bits)); 2584 else 2585 return false; 2586 2587 return true; 2588 } 2589 2590 static bool 2591 uc_nl_evsock_init(void) 2592 { 2593 if (nl80211_conn.evsock) 2594 return true; 2595 2596 if (!uc_nl_connect_sock(&nl80211_conn.evsock, true)) 2597 return false; 2598 2599 if (!uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "config") || 2600 !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "scan") || 2601 !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "regulatory") || 2602 !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "mlme") || 2603 !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "vendor") || 2604 !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "nan")) { 2605 nl_socket_free(nl80211_conn.evsock); 2606 nl80211_conn.evsock = NULL; 2607 return false; 2608 } 2609 2610 return true; 2611 } 2612 2613 /** 2614 * Wait for a specific nl80211 event 2615 * 2616 * This function waits for a specified nl80211 command to be received within 2617 * a given timeout period. It's useful for asynchronous event handling. 2618 * 2619 * @param {Array|number} cmds - Array of command IDs or single command ID to wait for 2620 * @param {number} timeout - Timeout in milliseconds (optional, default: -1 for infinite) 2621 * @returns {Object|null} Object with 'cmd' and 'msg' properties if event received, 2622 * null if timeout or error occurs 2623 * @example 2624 * // Wait for scan results with 5 second timeout 2625 * let event = waitfor([const.NL80211_CMD_NEW_SCAN_RESULTS], 5000); 2626 * if (event) 2627 * print('Received scan results:', event.msg); 2628 */ 2629 static uc_value_t * 2630 uc_nl_waitfor(uc_vm_t *vm, size_t nargs) 2631 { 2632 struct pollfd pfd = { .events = POLLIN }; 2633 uc_value_t *cmds = uc_fn_arg(0); 2634 uc_value_t *timeout = uc_fn_arg(1); 2635 uc_value_t *rv = NULL; 2636 struct waitfor_ctx ctx = { .vm = vm }; 2637 struct nl_cb *cb; 2638 int ms = -1, err; 2639 2640 if (timeout) { 2641 int64_t n = ucv_int64_get(timeout); 2642 2643 if (ucv_type(timeout) != UC_INTEGER || n < INT32_MIN || n > INT32_MAX) 2644 err_return(NLE_INVAL, "Invalid timeout specified"); 2645 2646 ms = (int)n; 2647 } 2648 2649 if (!uc_nl_fill_cmds(ctx.cmds, cmds)) 2650 err_return(NLE_INVAL, "Invalid command ID specified"); 2651 2652 if (!uc_nl_evsock_init()) 2653 return NULL; 2654 2655 cb = nl_cb_alloc(NL_CB_DEFAULT); 2656 2657 if (!cb) 2658 err_return(NLE_NOMEM, NULL); 2659 2660 err = 0; 2661 2662 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL); 2663 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_event, &ctx); 2664 nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &err); 2665 2666 pfd.fd = nl_socket_get_fd(nl80211_conn.evsock); 2667 2668 if (poll(&pfd, 1, ms) == 1) { 2669 while (err == 0 && ctx.cmd == 0) 2670 nl_recvmsgs(nl80211_conn.evsock, cb); 2671 } 2672 2673 nl_cb_put(cb); 2674 2675 if (ctx.cmd) { 2676 rv = ucv_object_new(vm); 2677 2678 ucv_object_add(rv, "cmd", ucv_int64_new(ctx.cmd)); 2679 ucv_object_add(rv, "msg", ctx.res); 2680 2681 return rv; 2682 } 2683 else if (err) { 2684 err_return(err, NULL); 2685 } 2686 else { 2687 err_return(NLE_FAILURE, "No event received"); 2688 } 2689 } 2690 2691 static uc_value_t * 2692 uc_nl_request_common(struct nl_sock *sock, uc_vm_t *vm, size_t nargs) 2693 { 2694 request_state_t st = { .vm = vm }; 2695 uc_value_t *cmd = uc_fn_arg(0); 2696 uc_value_t *flags = uc_fn_arg(1); 2697 uc_value_t *payload = uc_fn_arg(2); 2698 uint16_t flagval = 0; 2699 struct nl_msg *msg; 2700 struct nl_cb *cb; 2701 int ret, id, cid; 2702 2703 if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 || 2704 (flags != NULL && ucv_type(flags) != UC_INTEGER) || 2705 (payload != NULL && ucv_type(payload) != UC_OBJECT)) 2706 err_return(NLE_INVAL, NULL); 2707 2708 if (flags) { 2709 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff) 2710 err_return(NLE_INVAL, NULL); 2711 else 2712 flagval = (uint16_t)ucv_int64_get(flags); 2713 } 2714 2715 msg = nlmsg_alloc(); 2716 2717 if (!msg) 2718 err_return(NLE_NOMEM, NULL); 2719 2720 cid = ucv_int64_get(cmd); 2721 2722 if (cid >= HWSIM_CMD_OFFSET) { 2723 id = uc_nl_find_family_id("MAC80211_HWSIM"); 2724 cid -= HWSIM_CMD_OFFSET; 2725 st.spec = &hwsim_msg; 2726 } 2727 else if (cid == NL80211_CMD_GET_WIPHY) { 2728 id = uc_nl_find_family_id("nl80211"); 2729 st.spec = &nl80211_msg; 2730 st.merge_phy_info = true; 2731 2732 if (ucv_object_get(payload, "wiphy", NULL) != NULL) 2733 st.single_phy_info = true; 2734 2735 if (ucv_is_truish(ucv_object_get(payload, "split_wiphy_dump", NULL))) 2736 flagval |= NLM_F_DUMP; 2737 } 2738 else { 2739 id = uc_nl_find_family_id("nl80211"); 2740 st.spec = &nl80211_msg; 2741 } 2742 2743 if (id < 0) 2744 err_return(-id, NULL); 2745 2746 genlmsg_put(msg, 0, 0, id, 0, flagval, cid, 0); 2747 2748 if (!uc_nl_parse_attrs(msg, nlmsg_data(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) { 2749 nlmsg_free(msg); 2750 2751 return NULL; 2752 } 2753 2754 cb = nl_cb_alloc(NL_CB_DEFAULT); 2755 2756 if (!cb) { 2757 nlmsg_free(msg); 2758 err_return(NLE_NOMEM, NULL); 2759 } 2760 2761 ret = 1; 2762 2763 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st); 2764 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st); 2765 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_done, &st); 2766 nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &ret); 2767 2768 nl_send_auto_complete(sock, msg); 2769 2770 while (ret > 0 && st.state < STATE_REPLIED) 2771 nl_recvmsgs(sock, cb); 2772 2773 nlmsg_free(msg); 2774 nl_cb_put(cb); 2775 2776 if (ret < 0) 2777 err_return(ret, NULL); 2778 2779 switch (st.state) { 2780 case STATE_REPLIED: 2781 return st.res; 2782 2783 case STATE_UNREPLIED: 2784 return ucv_boolean_new(true); 2785 2786 default: 2787 set_error(NLE_FAILURE, "Interrupted reply"); 2788 2789 return ucv_boolean_new(false); 2790 } 2791 } 2792 2793 /** 2794 * Send a nl80211 request 2795 * 2796 * This function sends a nl80211 netlink request to the kernel and processes 2797 * the response. It's the main interface for interacting with wireless devices. 2798 * 2799 * @param {number} cmd - The nl80211 command ID to execute 2800 * @param {number} flags - Netlink flags (optional, default: 0) 2801 * @param {Object} payload - Request payload object with attributes (optional) 2802 * @returns {Object|boolean} Response object from the kernel, or true for 2803 * successful acknowledgment without data 2804 * @example 2805 * // Get wireless device information 2806 * let response = request(const.NL80211_CMD_GET_WIPHY, 0, { wiphy: 0 }); 2807 * print('Wireless device info:', response); 2808 * 2809 * // Set wireless interface mode 2810 * let result = request(const.NL80211_CMD_SET_INTERFACE, 0, { 2811 * ifindex: 1, 2812 * iftype: const.NL80211_IFTYPE_AP 2813 * }); 2814 */ 2815 static uc_value_t * 2816 uc_nl_request(uc_vm_t *vm, size_t nargs) 2817 { 2818 if (!uc_nl_connect_sock(&nl80211_conn.sock, false)) 2819 return NULL; 2820 2821 return uc_nl_request_common(nl80211_conn.sock, vm, nargs); 2822 } 2823 2824 2825 /** 2826 * Represents a netlink listener resource. 2827 * 2828 * @class module:nl80211.listener 2829 * @hideconstructor 2830 * 2831 * @see {@link module:nl80211#listener|listener()} 2832 * 2833 * @example 2834 * const nlListener = listener((msg) => { 2835 * print('Received netlink message:', msg, '\n'); 2836 * }, [const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_DEL_INTERFACE]); 2837 * 2838 * nlListener.set_commands([const.NL80211_CMD_GET_WIPHY, const.NL80211_CMD_SET_INTERFACE]); 2839 * 2840 * nlListener.close(); 2841 */ 2842 2843 static void 2844 uc_nl_listener_cb(struct uloop_fd *fd, unsigned int events) 2845 { 2846 nl_recvmsgs(nl80211_conn.evsock, nl80211_conn.evsock_cb); 2847 } 2848 2849 /** 2850 * Create a listener for nl80211 events 2851 * 2852 * This function creates a listener that will receive nl80211 events matching 2853 * the specified command IDs. The listener runs asynchronously and calls the 2854 * provided callback function when events are received. 2855 * 2856 * @param {Function} callback - Function to call when an event is received 2857 * @param {Array|number} cmds - Array of command IDs or single command ID to listen for 2858 * @returns {Object} Listener resource object that can be used to manage the listener 2859 * @example 2860 * // Listen for interface changes 2861 * let listener = listener((msg) => { 2862 * print('Interface event:', msg.cmd, msg.msg); 2863 * }, [const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_DEL_INTERFACE]); 2864 * 2865 * // Listen for scan results 2866 * let scanListener = listener((msg) => { 2867 * print('Scan completed:', msg.msg); 2868 * }, const.NL80211_CMD_NEW_SCAN_RESULTS); 2869 */ 2870 static uc_value_t * 2871 uc_nl_listener(uc_vm_t *vm, size_t nargs) 2872 { 2873 struct uloop_fd *fd = &nl80211_conn.evsock_fd; 2874 uc_nl_listener_t *l; 2875 uc_value_t *cb_func = uc_fn_arg(0); 2876 uc_value_t *cmds = uc_fn_arg(1); 2877 uc_value_t *rv; 2878 size_t i; 2879 2880 if (!ucv_is_callable(cb_func)) { 2881 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid callback"); 2882 return NULL; 2883 } 2884 2885 if (!uc_nl_evsock_init()) 2886 return NULL; 2887 2888 if (!fd->registered) { 2889 fd->fd = nl_socket_get_fd(nl80211_conn.evsock); 2890 fd->cb = uc_nl_listener_cb; 2891 uloop_fd_add(fd, ULOOP_READ); 2892 } 2893 2894 if (!nl80211_conn.evsock_cb) { 2895 struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); 2896 2897 if (!cb) 2898 err_return(NLE_NOMEM, NULL); 2899 2900 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL); 2901 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_listener_event, NULL); 2902 nl80211_conn.evsock_cb = cb; 2903 } 2904 2905 for (i = 0; i < ucv_array_length(listener_registry); i += 2) { 2906 if (!ucv_array_get(listener_registry, i)) 2907 break; 2908 } 2909 2910 ucv_array_set(listener_registry, i + 1, ucv_get(cb_func)); 2911 l = xalloc(sizeof(*l)); 2912 l->index = i; 2913 if (!uc_nl_fill_cmds(l->cmds, cmds)) { 2914 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID"); 2915 free(l); 2916 return NULL; 2917 } 2918 2919 rv = uc_resource_new(listener_type, l); 2920 ucv_array_set(listener_registry, i, ucv_get(rv)); 2921 listener_vm = vm; 2922 2923 return rv; 2924 } 2925 2926 static void 2927 uc_nl_listener_free(void *arg) 2928 { 2929 uc_nl_listener_t *l = arg; 2930 2931 ucv_array_set(listener_registry, l->index, NULL); 2932 ucv_array_set(listener_registry, l->index + 1, NULL); 2933 free(l); 2934 } 2935 2936 /** 2937 * Set the commands this listener should listen for 2938 * 2939 * This method updates the command IDs that the listener will respond to. 2940 * It allows dynamic modification of which events the listener handles. 2941 * 2942 * @param {number[]|number} cmds - Array of command IDs or single command ID to listen for 2943 * @returns {void} 2944 * @example 2945 * // Create a listener initially for interface events 2946 * let listener = listener(callback, [const.NL80211_CMD_NEW_INTERFACE]); 2947 * 2948 * // Later, make it also listen for scan events 2949 * listener.set_commands([const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_NEW_SCAN_RESULTS]); 2950 */ 2951 static uc_value_t * 2952 uc_nl_listener_set_commands(uc_vm_t *vm, size_t nargs) 2953 { 2954 uc_nl_listener_t *l = uc_fn_thisval("nl80211.listener"); 2955 uc_value_t *cmds = uc_fn_arg(0); 2956 2957 if (!l) 2958 return NULL; 2959 2960 memset(l->cmds, 0, sizeof(l->cmds)); 2961 if (!uc_nl_fill_cmds(l->cmds, cmds)) 2962 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID"); 2963 2964 return NULL; 2965 } 2966 2967 /** 2968 * Send a nl80211 request from the listener 2969 * 2970 * This method allows the listener to send its own nl80211 requests 2971 * using the same connection used for event listening. 2972 * 2973 * @param {number} cmd - The nl80211 command ID to execute 2974 * @param {number} flags - Netlink flags (optional, default: 0) 2975 * @param {Object} payload - Request payload object with attributes (optional) 2976 * @returns {Object|boolean} Response object from the kernel, or true for 2977 * successful acknowledgment without data 2978 * @example 2979 * // Listener sends its own request 2980 * let response = listener.request(const.NL80211_CMD_GET_STATION, 0, { 2981 * ifindex: 1, 2982 * mac: "00:11:22:33:44:55" 2983 * }); 2984 */ 2985 static uc_value_t * 2986 uc_nl_listener_request(uc_vm_t *vm, size_t nargs) 2987 { 2988 return uc_nl_request_common(nl80211_conn.evsock, vm, nargs); 2989 } 2990 2991 /** 2992 * Close and remove the listener 2993 * 2994 * This method stops the listener from receiving further events and 2995 * cleans up all associated resources. The listener becomes invalid 2996 * after this method is called. 2997 * 2998 * @returns {void} 2999 * @example 3000 * // Create and use a listener 3001 * let listener = listener(callback, [const.NL80211_CMD_NEW_INTERFACE]); 3002 * 3003 * // Later, clean it up 3004 * listener.close(); 3005 */ 3006 static uc_value_t * 3007 uc_nl_listener_close(uc_vm_t *vm, size_t nargs) 3008 { 3009 uc_nl_listener_t **lptr = uc_fn_this("nl80211.listener"); 3010 uc_nl_listener_t *l; 3011 3012 if (!lptr) 3013 return NULL; 3014 3015 l = *lptr; 3016 if (!l) 3017 return NULL; 3018 3019 *lptr = NULL; 3020 uc_nl_listener_free(l); 3021 3022 return NULL; 3023 } 3024 3025 3026 static void 3027 register_constants(uc_vm_t *vm, uc_value_t *scope) 3028 { 3029 uc_value_t *c = ucv_object_new(vm); 3030 3031 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x)) 3032 3033 /** 3034 * @typedef 3035 * @name Netlink message flags 3036 * @property {number} NLM_F_ACK - Request for acknowledgment 3037 * @property {number} NLM_F_ACK_TLVS - Request for acknowledgment with TLVs 3038 * @property {number} NLM_F_APPEND - Append to existing list 3039 * @property {number} NLM_F_ATOMIC - Atomic operation 3040 * @property {number} NLM_F_CAPPED - Request capped 3041 * @property {number} NLM_F_CREATE - Create if not exists 3042 * @property {number} NLM_F_DUMP - Dump request 3043 * @property {number} NLM_F_DUMP_FILTERED - Dump filtered request 3044 * @property {number} NLM_F_DUMP_INTR - Dump interrupted 3045 * @property {number} NLM_F_ECHO - Echo request 3046 * @property {number} NLM_F_EXCL - Exclusive creation 3047 * @property {number} NLM_F_MATCH - Match request 3048 * @property {number} NLM_F_MULTI - Multi-part message 3049 * @property {number} NLM_F_NONREC - Non-recursive operation 3050 * @property {number} NLM_F_REPLACE - Replace existing 3051 * @property {number} NLM_F_REQUEST - Request message 3052 * @property {number} NLM_F_ROOT - Root operation 3053 */ 3054 ADD_CONST(NLM_F_ACK); 3055 ADD_CONST(NLM_F_ACK_TLVS); 3056 ADD_CONST(NLM_F_APPEND); 3057 ADD_CONST(NLM_F_ATOMIC); 3058 ADD_CONST(NLM_F_CAPPED); 3059 ADD_CONST(NLM_F_CREATE); 3060 ADD_CONST(NLM_F_DUMP); 3061 ADD_CONST(NLM_F_DUMP_FILTERED); 3062 ADD_CONST(NLM_F_DUMP_INTR); 3063 ADD_CONST(NLM_F_ECHO); 3064 ADD_CONST(NLM_F_EXCL); 3065 ADD_CONST(NLM_F_MATCH); 3066 ADD_CONST(NLM_F_MULTI); 3067 ADD_CONST(NLM_F_NONREC); 3068 ADD_CONST(NLM_F_REPLACE); 3069 ADD_CONST(NLM_F_REQUEST); 3070 ADD_CONST(NLM_F_ROOT); 3071 3072 /** 3073 * @typedef 3074 * @name nl80211 commands 3075 * @property {number} NL80211_CMD_GET_WIPHY - Get wireless PHY attributes 3076 * @property {number} NL80211_CMD_SET_WIPHY - Set wireless PHY attributes 3077 * @property {number} NL80211_CMD_NEW_WIPHY - Create new wireless PHY 3078 * @property {number} NL80211_CMD_DEL_WIPHY - Delete wireless PHY 3079 * @property {number} NL80211_CMD_GET_INTERFACE - Get interface information 3080 * @property {number} NL80211_CMD_SET_INTERFACE - Set interface attributes 3081 * @property {number} NL80211_CMD_NEW_INTERFACE - Create new interface 3082 * @property {number} NL80211_CMD_DEL_INTERFACE - Delete interface 3083 * @property {number} NL80211_CMD_GET_KEY - Get key 3084 * @property {number} NL80211_CMD_SET_KEY - Set key 3085 * @property {number} NL80211_CMD_NEW_KEY - Add new key 3086 * @property {number} NL80211_CMD_DEL_KEY - Delete key 3087 * @property {number} NL80211_CMD_GET_BEACON - Get beacon 3088 * @property {number} NL80211_CMD_SET_BEACON - Set beacon 3089 * @property {number} NL80211_CMD_NEW_BEACON - Set beacon (alias) 3090 * @property {number} NL80211_CMD_STOP_AP - Stop AP operation 3091 * @property {number} NL80211_CMD_DEL_BEACON - Delete beacon 3092 * @property {number} NL80211_CMD_GET_STATION - Get station information 3093 * @property {number} NL80211_CMD_SET_STATION - Set station attributes 3094 * @property {number} NL80211_CMD_NEW_STATION - Add new station 3095 * @property {number} NL80211_CMD_DEL_STATION - Delete station 3096 * @property {number} NL80211_CMD_GET_MPATH - Get mesh path 3097 * @property {number} NL80211_CMD_SET_MPATH - Set mesh path 3098 * @property {number} NL80211_CMD_NEW_MPATH - Add new mesh path 3099 * @property {number} NL80211_CMD_DEL_MPATH - Delete mesh path 3100 * @property {number} NL80211_CMD_SET_BSS - Set BSS attributes 3101 * @property {number} NL80211_CMD_SET_REG - Set regulatory domain 3102 * @property {number} NL80211_CMD_REQ_SET_REG - Request regulatory domain change 3103 * @property {number} NL80211_CMD_GET_MESH_CONFIG - Get mesh configuration 3104 * @property {number} NL80211_CMD_SET_MESH_CONFIG - Set mesh configuration 3105 * @property {number} NL80211_CMD_GET_REG - Get regulatory domain 3106 * @property {number} NL80211_CMD_GET_SCAN - Get scan results 3107 * @property {number} NL80211_CMD_TRIGGER_SCAN - Trigger scan 3108 * @property {number} NL80211_CMD_NEW_SCAN_RESULTS - New scan results available 3109 * @property {number} NL80211_CMD_SCAN_ABORTED - Scan aborted 3110 * @property {number} NL80211_CMD_REG_CHANGE - Regulatory domain change 3111 * @property {number} NL80211_CMD_AUTHENTICATE - Authenticate 3112 * @property {number} NL80211_CMD_ASSOCIATE - Associate 3113 * @property {number} NL80211_CMD_DEAUTHENTICATE - Deauthenticate 3114 * @property {number} NL80211_CMD_DISASSOCIATE - Disassociate 3115 * @property {number} NL80211_CMD_MICHAEL_MIC_FAILURE - Michael MIC failure 3116 * @property {number} NL80211_CMD_REG_BEACON_HINT - Beacon regulatory hint 3117 * @property {number} NL80211_CMD_JOIN_IBSS - Join IBSS 3118 * @property {number} NL80211_CMD_LEAVE_IBSS - Leave IBSS 3119 * @property {number} NL80211_CMD_TESTMODE - Test mode 3120 * @property {number} NL80211_CMD_CONNECT - Connect 3121 * @property {number} NL80211_CMD_ROAM - Roam 3122 * @property {number} NL80211_CMD_DISCONNECT - Disconnect 3123 * @property {number} NL80211_CMD_SET_WIPHY_NETNS - Set wireless PHY network namespace 3124 * @property {number} NL80211_CMD_GET_SURVEY - Get survey data 3125 * @property {number} NL80211_CMD_NEW_SURVEY_RESULTS - New survey results 3126 * @property {number} NL80211_CMD_SET_PMKSA - Set PMKSA 3127 * @property {number} NL80211_CMD_DEL_PMKSA - Delete PMKSA 3128 * @property {number} NL80211_CMD_FLUSH_PMKSA - Flush PMKSA 3129 * @property {number} NL80211_CMD_REMAIN_ON_CHANNEL - Remain on channel 3130 * @property {number} NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL - Cancel remain on channel 3131 * @property {number} NL80211_CMD_SET_TX_BITRATE_MASK - Set TX bitrate mask 3132 * @property {number} NL80211_CMD_REGISTER_FRAME - Register frame 3133 * @property {number} NL80211_CMD_REGISTER_ACTION - Register action frame 3134 * @property {number} NL80211_CMD_FRAME - Frame 3135 * @property {number} NL80211_CMD_ACTION - Action frame 3136 * @property {number} NL80211_CMD_FRAME_TX_STATUS - Frame TX status 3137 * @property {number} NL80211_CMD_ACTION_TX_STATUS - Action TX status 3138 * @property {number} NL80211_CMD_SET_POWER_SAVE - Set power save 3139 * @property {number} NL80211_CMD_GET_POWER_SAVE - Get power save 3140 * @property {number} NL80211_CMD_SET_CQM - Set CQM 3141 * @property {number} NL80211_CMD_NOTIFY_CQM - Notify CQM 3142 * @property {number} NL80211_CMD_SET_CHANNEL - Set channel 3143 * @property {number} NL80211_CMD_SET_WDS_PEER - Set WDS peer 3144 * @property {number} NL80211_CMD_FRAME_WAIT_CANCEL - Cancel frame wait 3145 * @property {number} NL80211_CMD_JOIN_MESH - Join mesh 3146 * @property {number} NL80211_CMD_LEAVE_MESH - Leave mesh 3147 * @property {number} NL80211_CMD_UNPROT_DEAUTHENTICATE - Unprotected deauthenticate 3148 * @property {number} NL80211_CMD_UNPROT_DISASSOCIATE - Unprotected disassociate 3149 * @property {number} NL80211_CMD_NEW_PEER_CANDIDATE - New peer candidate 3150 * @property {number} NL80211_CMD_GET_WOWLAN - Get WoWLAN 3151 * @property {number} NL80211_CMD_SET_WOWLAN - Set WoWLAN 3152 * @property {number} NL80211_CMD_START_SCHED_SCAN - Start scheduled scan 3153 * @property {number} NL80211_CMD_STOP_SCHED_SCAN - Stop scheduled scan 3154 * @property {number} NL80211_CMD_SCHED_SCAN_RESULTS - Scheduled scan results 3155 * @property {number} NL80211_CMD_SCHED_SCAN_STOPPED - Scheduled scan stopped 3156 * @property {number} NL80211_CMD_SET_REKEY_OFFLOAD - Set rekey offload 3157 * @property {number} NL80211_CMD_PMKSA_CANDIDATE - PMKSA candidate 3158 * @property {number} NL80211_CMD_TDLS_OPER - TDLS operation 3159 * @property {number} NL80211_CMD_TDLS_MGMT - TDLS management 3160 * @property {number} NL80211_CMD_UNEXPECTED_FRAME - Unexpected frame 3161 * @property {number} NL80211_CMD_PROBE_CLIENT - Probe client 3162 * @property {number} NL80211_CMD_REGISTER_BEACONS - Register beacons 3163 * @property {number} NL80211_CMD_UNEXPECTED_4ADDR_FRAME - Unexpected 4-address frame 3164 * @property {number} NL80211_CMD_SET_NOACK_MAP - Set no-ack map 3165 * @property {number} NL80211_CMD_CH_SWITCH_NOTIFY - Channel switch notify 3166 * @property {number} NL80211_CMD_START_P2P_DEVICE - Start P2P device 3167 * @property {number} NL80211_CMD_STOP_P2P_DEVICE - Stop P2P device 3168 * @property {number} NL80211_CMD_CONN_FAILED - Connection failed 3169 * @property {number} NL80211_CMD_SET_MCAST_RATE - Set multicast rate 3170 * @property {number} NL80211_CMD_SET_MAC_ACL - Set MAC ACL 3171 * @property {number} NL80211_CMD_RADAR_DETECT - Radar detect 3172 * @property {number} NL80211_CMD_GET_PROTOCOL_FEATURES - Get protocol features 3173 * @property {number} NL80211_CMD_UPDATE_FT_IES - Update FT IEs 3174 * @property {number} NL80211_CMD_FT_EVENT - FT event 3175 * @property {number} NL80211_CMD_CRIT_PROTOCOL_START - Start critical protocol 3176 * @property {number} NL80211_CMD_CRIT_PROTOCOL_STOP - Stop critical protocol 3177 * @property {number} NL80211_CMD_GET_COALESCE - Get coalesce 3178 * @property {number} NL80211_CMD_SET_COALESCE - Set coalesce 3179 * @property {number} NL80211_CMD_CHANNEL_SWITCH - Channel switch 3180 * @property {number} NL80211_CMD_VENDOR - Vendor command 3181 * @property {number} NL80211_CMD_SET_QOS_MAP - Set QoS map 3182 * @property {number} NL80211_CMD_ADD_TX_TS - Add TX TS 3183 * @property {number} NL80211_CMD_DEL_TX_TS - Delete TX TS 3184 * @property {number} NL80211_CMD_GET_MPP - Get MPP 3185 * @property {number} NL80211_CMD_JOIN_OCB - Join OCB 3186 * @property {number} NL80211_CMD_LEAVE_OCB - Leave OCB 3187 * @property {number} NL80211_CMD_CH_SWITCH_STARTED_NOTIFY - Channel switch started notify 3188 * @property {number} NL80211_CMD_TDLS_CHANNEL_SWITCH - TDLS channel switch 3189 * @property {number} NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH - Cancel TDLS channel switch 3190 */ 3191 ADD_CONST(NL80211_CMD_GET_WIPHY); 3192 ADD_CONST(NL80211_CMD_SET_WIPHY); 3193 ADD_CONST(NL80211_CMD_NEW_WIPHY); 3194 ADD_CONST(NL80211_CMD_DEL_WIPHY); 3195 ADD_CONST(NL80211_CMD_GET_INTERFACE); 3196 ADD_CONST(NL80211_CMD_SET_INTERFACE); 3197 ADD_CONST(NL80211_CMD_NEW_INTERFACE); 3198 ADD_CONST(NL80211_CMD_DEL_INTERFACE); 3199 ADD_CONST(NL80211_CMD_GET_KEY); 3200 ADD_CONST(NL80211_CMD_SET_KEY); 3201 ADD_CONST(NL80211_CMD_NEW_KEY); 3202 ADD_CONST(NL80211_CMD_DEL_KEY); 3203 ADD_CONST(NL80211_CMD_GET_BEACON); 3204 ADD_CONST(NL80211_CMD_SET_BEACON); 3205 ADD_CONST(NL80211_CMD_START_AP); 3206 ADD_CONST(NL80211_CMD_NEW_BEACON); 3207 ADD_CONST(NL80211_CMD_STOP_AP); 3208 ADD_CONST(NL80211_CMD_DEL_BEACON); 3209 ADD_CONST(NL80211_CMD_GET_STATION); 3210 ADD_CONST(NL80211_CMD_SET_STATION); 3211 ADD_CONST(NL80211_CMD_NEW_STATION); 3212 ADD_CONST(NL80211_CMD_DEL_STATION); 3213 ADD_CONST(NL80211_CMD_GET_MPATH); 3214 ADD_CONST(NL80211_CMD_SET_MPATH); 3215 ADD_CONST(NL80211_CMD_NEW_MPATH); 3216 ADD_CONST(NL80211_CMD_DEL_MPATH); 3217 ADD_CONST(NL80211_CMD_SET_BSS); 3218 ADD_CONST(NL80211_CMD_SET_REG); 3219 ADD_CONST(NL80211_CMD_REQ_SET_REG); 3220 ADD_CONST(NL80211_CMD_GET_MESH_CONFIG); 3221 ADD_CONST(NL80211_CMD_SET_MESH_CONFIG); 3222 ADD_CONST(NL80211_CMD_GET_REG); 3223 ADD_CONST(NL80211_CMD_GET_SCAN); 3224 ADD_CONST(NL80211_CMD_TRIGGER_SCAN); 3225 ADD_CONST(NL80211_CMD_NEW_SCAN_RESULTS); 3226 ADD_CONST(NL80211_CMD_SCAN_ABORTED); 3227 ADD_CONST(NL80211_CMD_REG_CHANGE); 3228 ADD_CONST(NL80211_CMD_AUTHENTICATE); 3229 ADD_CONST(NL80211_CMD_ASSOCIATE); 3230 ADD_CONST(NL80211_CMD_DEAUTHENTICATE); 3231 ADD_CONST(NL80211_CMD_DISASSOCIATE); 3232 ADD_CONST(NL80211_CMD_MICHAEL_MIC_FAILURE); 3233 ADD_CONST(NL80211_CMD_REG_BEACON_HINT); 3234 ADD_CONST(NL80211_CMD_JOIN_IBSS); 3235 ADD_CONST(NL80211_CMD_LEAVE_IBSS); 3236 ADD_CONST(NL80211_CMD_TESTMODE); 3237 ADD_CONST(NL80211_CMD_CONNECT); 3238 ADD_CONST(NL80211_CMD_ROAM); 3239 ADD_CONST(NL80211_CMD_DISCONNECT); 3240 ADD_CONST(NL80211_CMD_SET_WIPHY_NETNS); 3241 ADD_CONST(NL80211_CMD_GET_SURVEY); 3242 ADD_CONST(NL80211_CMD_NEW_SURVEY_RESULTS); 3243 ADD_CONST(NL80211_CMD_SET_PMKSA); 3244 ADD_CONST(NL80211_CMD_DEL_PMKSA); 3245 ADD_CONST(NL80211_CMD_FLUSH_PMKSA); 3246 ADD_CONST(NL80211_CMD_REMAIN_ON_CHANNEL); 3247 ADD_CONST(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); 3248 ADD_CONST(NL80211_CMD_SET_TX_BITRATE_MASK); 3249 ADD_CONST(NL80211_CMD_REGISTER_FRAME); 3250 ADD_CONST(NL80211_CMD_REGISTER_ACTION); 3251 ADD_CONST(NL80211_CMD_FRAME); 3252 ADD_CONST(NL80211_CMD_ACTION); 3253 ADD_CONST(NL80211_CMD_FRAME_TX_STATUS); 3254 ADD_CONST(NL80211_CMD_ACTION_TX_STATUS); 3255 ADD_CONST(NL80211_CMD_SET_POWER_SAVE); 3256 ADD_CONST(NL80211_CMD_GET_POWER_SAVE); 3257 ADD_CONST(NL80211_CMD_SET_CQM); 3258 ADD_CONST(NL80211_CMD_NOTIFY_CQM); 3259 ADD_CONST(NL80211_CMD_SET_CHANNEL); 3260 ADD_CONST(NL80211_CMD_SET_WDS_PEER); 3261 ADD_CONST(NL80211_CMD_FRAME_WAIT_CANCEL); 3262 ADD_CONST(NL80211_CMD_JOIN_MESH); 3263 ADD_CONST(NL80211_CMD_LEAVE_MESH); 3264 ADD_CONST(NL80211_CMD_UNPROT_DEAUTHENTICATE); 3265 ADD_CONST(NL80211_CMD_UNPROT_DISASSOCIATE); 3266 ADD_CONST(NL80211_CMD_NEW_PEER_CANDIDATE); 3267 ADD_CONST(NL80211_CMD_GET_WOWLAN); 3268 ADD_CONST(NL80211_CMD_SET_WOWLAN); 3269 ADD_CONST(NL80211_CMD_START_SCHED_SCAN); 3270 ADD_CONST(NL80211_CMD_STOP_SCHED_SCAN); 3271 ADD_CONST(NL80211_CMD_SCHED_SCAN_RESULTS); 3272 ADD_CONST(NL80211_CMD_SCHED_SCAN_STOPPED); 3273 ADD_CONST(NL80211_CMD_SET_REKEY_OFFLOAD); 3274 ADD_CONST(NL80211_CMD_PMKSA_CANDIDATE); 3275 ADD_CONST(NL80211_CMD_TDLS_OPER); 3276 ADD_CONST(NL80211_CMD_TDLS_MGMT); 3277 ADD_CONST(NL80211_CMD_UNEXPECTED_FRAME); 3278 ADD_CONST(NL80211_CMD_PROBE_CLIENT); 3279 ADD_CONST(NL80211_CMD_REGISTER_BEACONS); 3280 ADD_CONST(NL80211_CMD_UNEXPECTED_4ADDR_FRAME); 3281 ADD_CONST(NL80211_CMD_SET_NOACK_MAP); 3282 ADD_CONST(NL80211_CMD_CH_SWITCH_NOTIFY); 3283 ADD_CONST(NL80211_CMD_START_P2P_DEVICE); 3284 ADD_CONST(NL80211_CMD_STOP_P2P_DEVICE); 3285 ADD_CONST(NL80211_CMD_CONN_FAILED); 3286 ADD_CONST(NL80211_CMD_SET_MCAST_RATE); 3287 ADD_CONST(NL80211_CMD_SET_MAC_ACL); 3288 ADD_CONST(NL80211_CMD_RADAR_DETECT); 3289 ADD_CONST(NL80211_CMD_GET_PROTOCOL_FEATURES); 3290 ADD_CONST(NL80211_CMD_UPDATE_FT_IES); 3291 ADD_CONST(NL80211_CMD_FT_EVENT); 3292 ADD_CONST(NL80211_CMD_CRIT_PROTOCOL_START); 3293 ADD_CONST(NL80211_CMD_CRIT_PROTOCOL_STOP); 3294 ADD_CONST(NL80211_CMD_GET_COALESCE); 3295 ADD_CONST(NL80211_CMD_SET_COALESCE); 3296 ADD_CONST(NL80211_CMD_CHANNEL_SWITCH); 3297 ADD_CONST(NL80211_CMD_VENDOR); 3298 ADD_CONST(NL80211_CMD_SET_QOS_MAP); 3299 ADD_CONST(NL80211_CMD_ADD_TX_TS); 3300 ADD_CONST(NL80211_CMD_DEL_TX_TS); 3301 ADD_CONST(NL80211_CMD_GET_MPP); 3302 ADD_CONST(NL80211_CMD_JOIN_OCB); 3303 ADD_CONST(NL80211_CMD_LEAVE_OCB); 3304 ADD_CONST(NL80211_CMD_CH_SWITCH_STARTED_NOTIFY); 3305 ADD_CONST(NL80211_CMD_TDLS_CHANNEL_SWITCH); 3306 ADD_CONST(NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH); 3307 3308 /** 3309 * @typedef 3310 * @name HWSIM commands 3311 * @property {number} HWSIM_CMD_REGISTER - Register radio 3312 * @property {number} HWSIM_CMD_FRAME - Send frame 3313 * @property {number} HWSIM_CMD_TX_INFO_FRAME - Send TX info frame 3314 * @property {number} HWSIM_CMD_NEW_RADIO - Create new radio 3315 * @property {number} HWSIM_CMD_DEL_RADIO - Delete radio 3316 * @property {number} HWSIM_CMD_GET_RADIO - Get radio information 3317 * @property {number} HWSIM_CMD_ADD_MAC_ADDR - Add MAC address 3318 * @property {number} HWSIM_CMD_DEL_MAC_ADDR - Delete MAC address 3319 * @property {number} HWSIM_CMD_START_PMSR - Start peer measurement 3320 * @property {number} HWSIM_CMD_ABORT_PMSR - Abort peer measurement 3321 * @property {number} HWSIM_CMD_REPORT_PMSR - Report peer measurement 3322 */ 3323 ADD_CONST(HWSIM_CMD_REGISTER), 3324 ADD_CONST(HWSIM_CMD_FRAME), 3325 ADD_CONST(HWSIM_CMD_TX_INFO_FRAME), 3326 ADD_CONST(HWSIM_CMD_NEW_RADIO), 3327 ADD_CONST(HWSIM_CMD_DEL_RADIO), 3328 ADD_CONST(HWSIM_CMD_GET_RADIO), 3329 ADD_CONST(HWSIM_CMD_ADD_MAC_ADDR), 3330 ADD_CONST(HWSIM_CMD_DEL_MAC_ADDR), 3331 ADD_CONST(HWSIM_CMD_START_PMSR), 3332 ADD_CONST(HWSIM_CMD_ABORT_PMSR), 3333 ADD_CONST(HWSIM_CMD_REPORT_PMSR), 3334 3335 /** 3336 * @typedef 3337 * @name Interface types 3338 * @property {number} NL80211_IFTYPE_ADHOC - IBSS/ad-hoc interface 3339 * @property {number} NL80211_IFTYPE_STATION - Station interface 3340 * @property {number} NL80211_IFTYPE_AP - Access point interface 3341 * @property {number} NL80211_IFTYPE_AP_VLAN - AP VLAN interface 3342 * @property {number} NL80211_IFTYPE_WDS - WDS interface 3343 * @property {number} NL80211_IFTYPE_MONITOR - Monitor interface 3344 * @property {number} NL80211_IFTYPE_MESH_POINT - Mesh point interface 3345 * @property {number} NL80211_IFTYPE_P2P_CLIENT - P2P client interface 3346 * @property {number} NL80211_IFTYPE_P2P_GO - P2P group owner interface 3347 * @property {number} NL80211_IFTYPE_P2P_DEVICE - P2P device interface 3348 * @property {number} NL80211_IFTYPE_OCB - Outside context of BSS (OCB) interface 3349 */ 3350 ADD_CONST(NL80211_IFTYPE_ADHOC); 3351 ADD_CONST(NL80211_IFTYPE_STATION); 3352 ADD_CONST(NL80211_IFTYPE_AP); 3353 ADD_CONST(NL80211_IFTYPE_AP_VLAN); 3354 ADD_CONST(NL80211_IFTYPE_WDS); 3355 ADD_CONST(NL80211_IFTYPE_MONITOR); 3356 ADD_CONST(NL80211_IFTYPE_MESH_POINT); 3357 ADD_CONST(NL80211_IFTYPE_P2P_CLIENT); 3358 ADD_CONST(NL80211_IFTYPE_P2P_GO); 3359 ADD_CONST(NL80211_IFTYPE_P2P_DEVICE); 3360 ADD_CONST(NL80211_IFTYPE_OCB); 3361 3362 ucv_object_add(scope, "const", c); 3363 }; 3364 3365 static const uc_function_list_t global_fns[] = { 3366 { "error", uc_nl_error }, 3367 { "request", uc_nl_request }, 3368 { "waitfor", uc_nl_waitfor }, 3369 { "listener", uc_nl_listener }, 3370 }; 3371 3372 3373 static const uc_function_list_t listener_fns[] = { 3374 { "set_commands", uc_nl_listener_set_commands }, 3375 { "request", uc_nl_listener_request }, 3376 { "close", uc_nl_listener_close }, 3377 }; 3378 3379 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 3380 { 3381 uc_function_list_register(scope, global_fns); 3382 3383 listener_type = uc_type_declare(vm, "nl80211.listener", listener_fns, uc_nl_listener_free); 3384 listener_registry = ucv_array_new(vm); 3385 3386 uc_vm_registry_set(vm, "nl80211.registry", listener_registry); 3387 3388 register_constants(vm, scope); 3389 } 3390
This page was automatically generated by LXR 0.3.1. • OpenWrt