• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/nl80211.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt