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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt