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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt