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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt