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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt