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