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