• 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                 if (ucv_type(val) == UC_STRING) {
1727                         nla_put(msg, attr,
1728                                 ucv_string_length(val) + !(spec->flags & DF_BINARY),
1729                                         ucv_string_get(val));
1730                 }
1731                 else {
1732                         s = ucv_to_string(vm, val);
1733 
1734                         if (!s)
1735                                 return nla_parse_error(spec, vm, val, "out of memory");
1736 
1737                         nla_put(msg, attr, strlen(s) + !(spec->flags & DF_BINARY), s);
1738                         free(s);
1739                 }
1740 
1741                 break;
1742 
1743         case DT_NETDEV:
1744                 if (ucv_type(val) == UC_INTEGER) {
1745                         if (ucv_int64_get(val) < 0 ||
1746                             ucv_int64_get(val) > UINT32_MAX)
1747                                 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295");
1748 
1749                         u32 = (uint32_t)ucv_int64_get(val);
1750                 }
1751                 else {
1752                         s = ucv_to_string(vm, val);
1753 
1754                         if (!s)
1755                                 return nla_parse_error(spec, vm, val, "out of memory");
1756 
1757                         u32 = if_nametoindex(s);
1758 
1759                         free(s);
1760                 }
1761 
1762                 if (spec->attr == 0)
1763                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
1764                 else
1765                         nla_put_u32(msg, attr, u32);
1766 
1767                 break;
1768 
1769         case DT_LLADDR:
1770                 assert(spec->attr != 0);
1771 
1772                 s = ucv_to_string(vm, val);
1773 
1774                 if (!s)
1775                         return nla_parse_error(spec, vm, val, "out of memory");
1776 
1777                 ea = ether_aton(s);
1778 
1779                 free(s);
1780 
1781                 if (!ea)
1782                         return nla_parse_error(spec, vm, val, "invalid MAC address");
1783 
1784                 nla_put(msg, attr, sizeof(*ea), ea);
1785 
1786                 break;
1787 
1788         case DT_INADDR:
1789                 assert(spec->attr != 0);
1790 
1791                 if (!uc_nl_parse_ipaddr(vm, val, &in))
1792                         return nla_parse_error(spec, vm, val, "invalid IP address");
1793 
1794                 nla_put(msg, attr, sizeof(in), &in);
1795 
1796                 break;
1797 
1798         case DT_NESTED:
1799                 if (spec->flags & DF_ARRAY) {
1800                         const uc_nl_nested_spec_t *nested = spec->auxdata;
1801 
1802                         assert(nested != NULL);
1803                         assert(nested->headsize > 0);
1804 
1805                         if (ucv_type(val) != UC_ARRAY)
1806                                 return nla_parse_error(spec, vm, val, "not an array");
1807 
1808                         nla = nla_reserve(msg, spec->attr, ucv_array_length(val) * nested->headsize);
1809                         s = nla_data(nla);
1810 
1811                         for (i = 0; i < ucv_array_length(val); i++) {
1812                                 item = ucv_array_get(val, i);
1813 
1814                                 if (!uc_nl_parse_attrs(msg, s, nested->attrs, nested->nattrs, vm, item))
1815                                         return false;
1816 
1817                                 s += nested->headsize;
1818                         }
1819 
1820                         return true;
1821                 }
1822 
1823                 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val))
1824                         return false;
1825 
1826                 break;
1827 
1828         default:
1829                 assert(0);
1830         }
1831 
1832         return true;
1833 }
1834 
1835 static uc_value_t *
1836 uc_nl_convert_numval(const uc_nl_attr_spec_t *spec, char *base)
1837 {
1838         union { uint8_t *u8; uint16_t *u16; uint32_t *u32; uint64_t *u64; char *base; } t = { .base = base };
1839 
1840         switch (spec->type) {
1841         case DT_U8:
1842                 return ucv_uint64_new(t.u8[0]);
1843 
1844         case DT_S8:
1845                 return ucv_int64_new((int8_t)t.u8[0]);
1846 
1847         case DT_U16:
1848                 return ucv_uint64_new(t.u16[0]);
1849 
1850         case DT_U32:
1851                 return ucv_uint64_new(t.u32[0]);
1852 
1853         case DT_S32:
1854                 return ucv_int64_new((int32_t)t.u32[0]);
1855 
1856         case DT_U64:
1857                 return ucv_uint64_new(t.u64[0]);
1858 
1859         default:
1860                 return NULL;
1861         }
1862 }
1863 
1864 static uc_value_t *
1865 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)
1866 {
1867         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 };
1868         char buf[sizeof("FF:FF:FF:FF:FF:FF")];
1869         struct ether_addr *ea;
1870         uc_value_t *v;
1871         int i;
1872 
1873         switch (spec->type) {
1874         case DT_U8:
1875         case DT_S8:
1876         case DT_U16:
1877         case DT_U32:
1878         case DT_S32:
1879         case DT_U64:
1880                 if (spec->flags & DF_ARRAY) {
1881                         assert(spec->attr != 0);
1882                         assert((nla_len(attr) % dt_sizes[spec->type]) == 0);
1883 
1884                         v = ucv_array_new_length(vm, nla_len(attr) / dt_sizes[spec->type]);
1885 
1886                         for (i = 0; i < nla_len(attr); i += dt_sizes[spec->type])
1887                                 ucv_array_push(v, uc_nl_convert_numval(spec, nla_data(attr) + i));
1888 
1889                         return v;
1890                 }
1891                 else if (spec->attr == 0) {
1892                         return uc_nl_convert_numval(spec, base + (uintptr_t)spec->auxdata);
1893                 }
1894                 else if (nla_check_len(attr, dt_sizes[spec->type])) {
1895                         return uc_nl_convert_numval(spec, nla_data(attr));
1896                 }
1897 
1898                 return NULL;
1899 
1900         case DT_BOOL:
1901                 if (spec->attr == 0)
1902                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
1903                 else if (nla_check_len(attr, sizeof(t.u8)))
1904                         t.u8 = nla_get_u8(attr);
1905 
1906                 return ucv_boolean_new(t.u8 != 0);
1907 
1908         case DT_FLAG:
1909                 if (spec->attr == 0)
1910                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
1911                 else if (attr != NULL)
1912                         t.u8 = 1;
1913 
1914                 return ucv_boolean_new(t.u8 != 0);
1915 
1916         case DT_STRING:
1917                 assert(spec->attr != 0);
1918 
1919                 if (!nla_check_len(attr, 1))
1920                         return NULL;
1921 
1922                 t.sz = nla_len(attr);
1923 
1924                 if (!(spec->flags & DF_BINARY))
1925                         t.sz -= 1;
1926 
1927                 return ucv_string_new_length(nla_data(attr), t.sz);
1928 
1929         case DT_NETDEV:
1930                 if (spec->attr == 0)
1931                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
1932                 else if (nla_check_len(attr, sizeof(t.u32)))
1933                         t.u32 = nla_get_u32(attr);
1934 
1935                 if (if_indextoname(t.u32, buf))
1936                         return ucv_string_new(buf);
1937 
1938                 return NULL;
1939 
1940         case DT_LLADDR:
1941                 assert(spec->attr != 0);
1942 
1943                 if (!nla_check_len(attr, sizeof(*ea)))
1944                         return NULL;
1945 
1946                 ea = nla_data(attr);
1947 
1948                 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
1949                         ea->ether_addr_octet[0], ea->ether_addr_octet[1],
1950                         ea->ether_addr_octet[2], ea->ether_addr_octet[3],
1951                         ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
1952 
1953                 return ucv_string_new(buf);
1954 
1955         case DT_INADDR:
1956                 assert(spec->attr != 0);
1957 
1958                 if (!nla_check_len(attr, sizeof(struct in_addr)) ||
1959                     !inet_ntop(AF_INET, nla_data(attr), buf, sizeof(buf)))
1960                         return NULL;
1961 
1962                 return ucv_string_new(buf);
1963 
1964         case DT_NESTED:
1965                 if (spec->flags & DF_ARRAY) {
1966                         const uc_nl_nested_spec_t *nested = spec->auxdata;
1967 
1968                         assert(nested != NULL);
1969                         assert(nested->headsize > 0);
1970                         assert((nla_len(attr) % nested->headsize) == 0);
1971 
1972                         v = ucv_array_new_length(vm, nla_len(attr) / nested->headsize);
1973 
1974                         for (i = 0; i < nla_len(attr); i += nested->headsize) {
1975                                 uc_value_t *item = ucv_object_new(vm);
1976 
1977                                 ucv_array_push(v, item);
1978 
1979                                 bool rv = uc_nl_convert_attrs(msg,
1980                                         nla_data(attr) + i, nla_len(attr) - i, nested->headsize,
1981                                         nested->attrs, nested->nattrs, vm, item);
1982 
1983                                 if (!rv) {
1984                                         ucv_put(v);
1985 
1986                                         return NULL;
1987                                 }
1988                         }
1989 
1990                         return v;
1991                 }
1992 
1993                 return uc_nl_convert_rta_nested(spec, msg, attr, vm);
1994 
1995         case DT_HT_MCS:
1996                 return uc_nl_convert_rta_ht_mcs(spec, msg, attr, vm);
1997 
1998         case DT_HT_CAP:
1999                 return uc_nl_convert_rta_ht_cap(spec, msg, attr, vm);
2000 
2001         case DT_VHT_MCS:
2002                 return uc_nl_convert_rta_vht_mcs(spec, msg, attr, vm);
2003 
2004         case DT_HE_MCS:
2005                 return uc_nl_convert_rta_he_mcs(spec, msg, attr, attr2, vm);
2006 
2007         case DT_IE:
2008                 return uc_nl_convert_rta_ie(spec, msg, attr, vm);
2009 
2010         default:
2011                 assert(0);
2012         }
2013 
2014         return NULL;
2015 }
2016 
2017 
2018 static struct {
2019         struct nl_sock *sock;
2020         struct nl_sock *evsock;
2021         struct nl_cache *cache;
2022         struct uloop_fd evsock_fd;
2023         struct nl_cb *evsock_cb;
2024 } nl80211_conn;
2025 
2026 typedef enum {
2027         STATE_UNREPLIED,
2028         STATE_CONTINUE,
2029         STATE_REPLIED,
2030         STATE_ERROR
2031 } reply_state_t;
2032 
2033 typedef struct {
2034         reply_state_t state;
2035         uc_vm_t *vm;
2036         uc_value_t *res;
2037         bool merge_phy_info;
2038         bool single_phy_info;
2039         const uc_nl_nested_spec_t *spec;
2040 } request_state_t;
2041 
2042 
2043 static uc_value_t *
2044 uc_nl_error(uc_vm_t *vm, size_t nargs)
2045 {
2046         uc_stringbuf_t *buf;
2047         const char *s;
2048 
2049         if (last_error.code == 0)
2050                 return NULL;
2051 
2052         buf = ucv_stringbuf_new();
2053 
2054         if (last_error.code == NLE_FAILURE && last_error.msg) {
2055                 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
2056         }
2057         else {
2058                 s = nl_geterror(last_error.code);
2059 
2060                 ucv_stringbuf_addstr(buf, s, strlen(s));
2061 
2062                 if (last_error.msg)
2063                         ucv_stringbuf_printf(buf, ": %s", last_error.msg);
2064         }
2065 
2066         set_error(0, NULL);
2067 
2068         return ucv_stringbuf_finish(buf);
2069 }
2070 
2071 static int
2072 cb_done(struct nl_msg *msg, void *arg)
2073 {
2074         request_state_t *s = arg;
2075 
2076         s->state = STATE_REPLIED;
2077 
2078         return NL_STOP;
2079 }
2080 
2081 static void
2082 deep_merge_array(uc_value_t *dest, uc_value_t *src);
2083 
2084 static void
2085 deep_merge_object(uc_value_t *dest, uc_value_t *src);
2086 
2087 static void
2088 deep_merge_array(uc_value_t *dest, uc_value_t *src)
2089 {
2090         uc_value_t *e, *v;
2091         size_t i;
2092 
2093         if (ucv_type(dest) == UC_ARRAY && ucv_type(src) == UC_ARRAY) {
2094                 for (i = 0; i < ucv_array_length(src); i++) {
2095                         e = ucv_array_get(dest, i);
2096                         v = ucv_array_get(src, i);
2097 
2098                         if (!e)
2099                                 ucv_array_set(dest, i, ucv_get(v));
2100                         else if (ucv_type(v) == UC_ARRAY)
2101                                 deep_merge_array(e, v);
2102                         else if (ucv_type(v) == UC_OBJECT)
2103                                 deep_merge_object(e, v);
2104                 }
2105         }
2106 }
2107 
2108 static void
2109 deep_merge_object(uc_value_t *dest, uc_value_t *src)
2110 {
2111         uc_value_t *e;
2112         bool exists;
2113 
2114         if (ucv_type(dest) == UC_OBJECT && ucv_type(src) == UC_OBJECT) {
2115                 ucv_object_foreach(src, k, v) {
2116                         e = ucv_object_get(dest, k, &exists);
2117 
2118                         if (!exists)
2119                                 ucv_object_add(dest, k, ucv_get(v));
2120                         else if (ucv_type(v) == UC_ARRAY)
2121                                 deep_merge_array(e, v);
2122                         else if (ucv_type(v) == UC_OBJECT)
2123                                 deep_merge_object(e, v);
2124                 }
2125         }
2126 }
2127 
2128 static int
2129 cb_reply(struct nl_msg *msg, void *arg)
2130 {
2131         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2132         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2133         request_state_t *s = arg;
2134         uc_value_t *o, *idx;
2135         int64_t i;
2136         bool rv;
2137 
2138         o = ucv_object_new(s->vm);
2139 
2140         rv = uc_nl_convert_attrs(msg,
2141                 genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0),
2142                 0, s->spec->attrs, s->spec->nattrs, s->vm, o);
2143 
2144         if (rv) {
2145                 if (hdr->nlmsg_flags & NLM_F_MULTI) {
2146                         if (s->merge_phy_info && s->single_phy_info) {
2147                                 if (!s->res) {
2148                                         s->res = o;
2149                                 }
2150                                 else {
2151                                         deep_merge_object(s->res, o);
2152                                         ucv_put(o);
2153                                 }
2154                         }
2155                         else if (s->merge_phy_info) {
2156                                 idx = ucv_object_get(o, "wiphy", NULL);
2157                                 i = idx ? ucv_int64_get(idx) : -1;
2158 
2159                                 if (i >= 0) {
2160                                         if (!s->res)
2161                                                 s->res = ucv_array_new(s->vm);
2162 
2163                                         idx = ucv_array_get(s->res, i);
2164 
2165                                         if (idx) {
2166                                                 deep_merge_object(idx, o);
2167                                                 ucv_put(o);
2168                                         }
2169                                         else {
2170                                                 ucv_array_set(s->res, i, o);
2171                                         }
2172                                 }
2173                         }
2174                         else {
2175                                 if (!s->res)
2176                                         s->res = ucv_array_new(s->vm);
2177 
2178                                 ucv_array_push(s->res, o);
2179                         }
2180                 }
2181                 else {
2182                         s->res = o;
2183                 }
2184         }
2185         else {
2186                 ucv_put(o);
2187         }
2188 
2189         s->state = STATE_CONTINUE;
2190 
2191         return NL_SKIP;
2192 }
2193 
2194 static bool
2195 uc_nl_connect_sock(struct nl_sock **sk, bool nonblocking)
2196 {
2197         int err, fd;
2198 
2199         if (*sk)
2200                 return true;
2201 
2202         *sk = nl_socket_alloc();
2203 
2204         if (!*sk) {
2205                 set_error(NLE_NOMEM, NULL);
2206                 goto err;
2207         }
2208 
2209         err = genl_connect(*sk);
2210 
2211         if (err != 0) {
2212                 set_error(err, NULL);
2213                 goto err;
2214         }
2215 
2216         fd = nl_socket_get_fd(*sk);
2217 
2218         if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) {
2219                 set_error(NLE_FAILURE, "unable to set FD_CLOEXEC flag on socket: %s", strerror(errno));
2220                 goto err;
2221         }
2222 
2223         if (nonblocking) {
2224                 err = nl_socket_set_nonblocking(*sk);
2225 
2226                 if (err != 0) {
2227                         set_error(err, NULL);
2228                         goto err;
2229                 }
2230         }
2231 
2232         return true;
2233 
2234 err:
2235         if (*sk) {
2236                 nl_socket_free(*sk);
2237                 *sk = NULL;
2238         }
2239 
2240         return false;
2241 }
2242 
2243 static int
2244 uc_nl_find_family_id(const char *name)
2245 {
2246         struct genl_family *fam;
2247 
2248         if (!nl80211_conn.cache && genl_ctrl_alloc_cache(nl80211_conn.sock, &nl80211_conn.cache))
2249                 return -NLE_NOMEM;
2250 
2251         fam = genl_ctrl_search_by_name(nl80211_conn.cache, name);
2252 
2253         if (!fam)
2254                 return -NLE_OBJ_NOTFOUND;
2255 
2256         return genl_family_get_id(fam);
2257 }
2258 
2259 static int
2260 cb_errno(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
2261 {
2262         int *ret = arg;
2263 
2264         if (err->error > 0) {
2265                 set_error(NLE_RANGE,
2266                         "Illegal error code %d in netlink reply", err->error);
2267 
2268                 *ret = -(NLE_MAX + 1);
2269         }
2270         else {
2271                 *ret = -nl_syserr2nlerr(err->error);
2272         }
2273 
2274         return NL_STOP;
2275 }
2276 
2277 static int
2278 cb_ack(struct nl_msg *msg, void *arg)
2279 {
2280         int *ret = arg;
2281 
2282         *ret = 0;
2283 
2284         return NL_STOP;
2285 }
2286 
2287 static int
2288 cb_subscribe(struct nl_msg *msg, void *arg)
2289 {
2290         struct nlattr *nla, *tb[CTRL_ATTR_MAX + 1], *grp[CTRL_ATTR_MCAST_GRP_MAX + 1];
2291         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
2292         struct { int id; const char *group; } *ret = arg;
2293         int rem;
2294 
2295         nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
2296 
2297         if (!tb[CTRL_ATTR_MCAST_GROUPS])
2298                 return NL_SKIP;
2299 
2300         nla_for_each_nested(nla, tb[CTRL_ATTR_MCAST_GROUPS], rem) {
2301                 nla_parse(grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(nla), nla_len(nla), NULL);
2302 
2303                 if (!grp[CTRL_ATTR_MCAST_GRP_NAME] || !grp[CTRL_ATTR_MCAST_GRP_ID])
2304                         continue;
2305 
2306                 if (strncmp(nla_data(grp[CTRL_ATTR_MCAST_GRP_NAME]),
2307                             ret->group, nla_len(grp[CTRL_ATTR_MCAST_GRP_NAME])))
2308                         continue;
2309 
2310                 ret->id = nla_get_u32(grp[CTRL_ATTR_MCAST_GRP_ID]);
2311 
2312                 break;
2313         }
2314 
2315         return NL_SKIP;
2316 }
2317 
2318 static bool
2319 uc_nl_subscribe(struct nl_sock *sk, const char *family, const char *group)
2320 {
2321         struct { int id; const char *group; } grp = { -NLE_OBJ_NOTFOUND, group };
2322         struct nl_msg *msg;
2323         struct nl_cb *cb;
2324         int id, ret;
2325 
2326         if (!uc_nl_connect_sock(&nl80211_conn.sock, false))
2327                 return NULL;
2328 
2329         msg = nlmsg_alloc();
2330 
2331         if (!msg)
2332                 err_return(NLE_NOMEM, NULL);
2333 
2334         id = uc_nl_find_family_id("nlctrl");
2335 
2336         if (id < 0)
2337                 err_return(-id, NULL);
2338 
2339         genlmsg_put(msg, 0, 0, id, 0, 0, CTRL_CMD_GETFAMILY, 0);
2340         nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family);
2341 
2342         cb = nl_cb_alloc(NL_CB_DEFAULT);
2343 
2344         if (!cb) {
2345                 nlmsg_free(msg);
2346                 err_return(NLE_NOMEM, NULL);
2347         }
2348 
2349         nl_send_auto_complete(nl80211_conn.sock, msg);
2350 
2351         ret = 1;
2352 
2353         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_ack, &ret);
2354         nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &ret);
2355         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_subscribe, &grp);
2356 
2357         while (ret > 0)
2358                 nl_recvmsgs(nl80211_conn.sock, cb);
2359 
2360         nlmsg_free(msg);
2361         nl_cb_put(cb);
2362 
2363         if (ret < 0)
2364                 err_return(ret, NULL);
2365 
2366         if (grp.id < 0)
2367                 err_return(grp.id, NULL);
2368 
2369         ret = nl_socket_add_membership(sk, grp.id);
2370 
2371         if (ret != 0)
2372                 err_return(ret, NULL);
2373 
2374         return true;
2375 }
2376 
2377 
2378 struct waitfor_ctx {
2379         uint8_t cmd;
2380         uc_vm_t *vm;
2381         uc_value_t *res;
2382         uint32_t cmds[NL80211_CMDS_BITMAP_SIZE];
2383 };
2384 
2385 static uc_value_t *
2386 uc_nl_prepare_event(uc_vm_t *vm, struct nl_msg *msg)
2387 {
2388         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2389         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2390         uc_value_t *o = ucv_object_new(vm);
2391         const uc_nl_attr_spec_t *attrs;
2392         size_t nattrs;
2393 
2394         if (hdr->nlmsg_type == uc_nl_find_family_id("MAC80211_HWSIM")) {
2395                 attrs = hwsim_msg.attrs;
2396                 nattrs = hwsim_msg.nattrs;
2397         }
2398         else {
2399                 attrs = nl80211_msg.attrs;
2400                 nattrs = nl80211_msg.nattrs;
2401         }
2402 
2403         if (!uc_nl_convert_attrs(msg, genlmsg_attrdata(gnlh, 0),
2404                 genlmsg_attrlen(gnlh, 0), 0, attrs, nattrs, vm, o)) {
2405                 ucv_put(o);
2406                 return NULL;
2407         }
2408 
2409         return o;
2410 }
2411 
2412 static int
2413 cb_listener_event(struct nl_msg *msg, void *arg)
2414 {
2415         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2416         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2417         uc_vm_t *vm = listener_vm;
2418 
2419         if (!nl80211_conn.evsock_fd.registered || !vm)
2420                 return NL_SKIP;
2421 
2422         for (size_t i = 0; i < ucv_array_length(listener_registry); i += 2) {
2423                 uc_value_t *this = ucv_array_get(listener_registry, i);
2424                 uc_value_t *func = ucv_array_get(listener_registry, i + 1);
2425                 uc_nl_listener_t *l;
2426                 uc_value_t *o, *data;
2427 
2428                 l = ucv_resource_data(this, "nl80211.listener");
2429                 if (!l)
2430                         continue;
2431 
2432                 if (gnlh->cmd > NL80211_CMD_MAX ||
2433                         !(l->cmds[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32))))
2434                         continue;
2435 
2436                 if (!ucv_is_callable(func))
2437                         continue;
2438 
2439                 data = uc_nl_prepare_event(vm, msg);
2440                 if (!data)
2441                         return NL_SKIP;
2442 
2443                 o = ucv_object_new(vm);
2444                 ucv_object_add(o, "cmd", ucv_int64_new(gnlh->cmd));
2445                 ucv_object_add(o, "msg", data);
2446 
2447                 uc_vm_stack_push(vm, ucv_get(this));
2448                 uc_vm_stack_push(vm, ucv_get(func));
2449                 uc_vm_stack_push(vm, o);
2450 
2451                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
2452                         uloop_end();
2453                         return NL_STOP;
2454                 }
2455 
2456                 ucv_put(uc_vm_stack_pop(vm));
2457         }
2458 
2459         return NL_SKIP;
2460 }
2461 
2462 static int
2463 cb_event(struct nl_msg *msg, void *arg)
2464 {
2465         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2466         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2467         struct waitfor_ctx *s = arg;
2468         uc_value_t *o;
2469 
2470         cb_listener_event(msg, arg);
2471 
2472         if (gnlh->cmd > NL80211_CMD_MAX ||
2473             !(s->cmds[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32))))
2474                 return NL_SKIP;
2475 
2476         o = uc_nl_prepare_event(s->vm, msg);
2477         if (o)
2478                 s->res = o;
2479 
2480         s->cmd = gnlh->cmd;
2481 
2482         return NL_SKIP;
2483 }
2484 
2485 static int
2486 cb_seq(struct nl_msg *msg, void *arg)
2487 {
2488         return NL_OK;
2489 }
2490 
2491 static bool
2492 uc_nl_fill_cmds(uint32_t *cmd_bits, uc_value_t *cmds)
2493 {
2494         if (ucv_type(cmds) == UC_ARRAY) {
2495                 for (size_t i = 0; i < ucv_array_length(cmds); i++) {
2496                         int64_t n = ucv_int64_get(ucv_array_get(cmds, i));
2497 
2498                         if (n >= HWSIM_CMD_OFFSET)
2499                                 n -= HWSIM_CMD_OFFSET;
2500 
2501                         if (errno || n < 0 || n > NL80211_CMD_MAX)
2502                                 return false;
2503 
2504                         cmd_bits[n / 32] |= (1 << (n % 32));
2505                 }
2506         }
2507         else if (ucv_type(cmds) == UC_INTEGER) {
2508                 int64_t n = ucv_int64_get(cmds);
2509 
2510                 if (n >= HWSIM_CMD_OFFSET)
2511                         n -= HWSIM_CMD_OFFSET;
2512 
2513                 if (errno || n < 0 || n > NL80211_CMD_MAX)
2514                         return false;
2515 
2516                 cmd_bits[n / 32] |= (1 << (n % 32));
2517         }
2518         else if (!cmds)
2519                 memset(cmd_bits, 0xff, NL80211_CMDS_BITMAP_SIZE * sizeof(*cmd_bits));
2520         else
2521                 return false;
2522 
2523         return true;
2524 }
2525 
2526 static bool
2527 uc_nl_evsock_init(void)
2528 {
2529         if (nl80211_conn.evsock)
2530                 return true;
2531 
2532         if (!uc_nl_connect_sock(&nl80211_conn.evsock, true))
2533                 return false;
2534 
2535         if (!uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "config") ||
2536             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "scan") ||
2537             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "regulatory") ||
2538             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "mlme") ||
2539             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "vendor") ||
2540             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "nan")) {
2541                 nl_socket_free(nl80211_conn.evsock);
2542                 nl80211_conn.evsock = NULL;
2543                 return false;
2544         }
2545 
2546         return true;
2547 }
2548 
2549 static uc_value_t *
2550 uc_nl_waitfor(uc_vm_t *vm, size_t nargs)
2551 {
2552         struct pollfd pfd = { .events = POLLIN };
2553         uc_value_t *cmds = uc_fn_arg(0);
2554         uc_value_t *timeout = uc_fn_arg(1);
2555         uc_value_t *rv = NULL;
2556         struct waitfor_ctx ctx = { .vm = vm };
2557         struct nl_cb *cb;
2558         int ms = -1, err;
2559 
2560         if (timeout) {
2561                 int64_t n = ucv_int64_get(timeout);
2562 
2563                 if (ucv_type(timeout) != UC_INTEGER || n < INT32_MIN || n > INT32_MAX)
2564                         err_return(NLE_INVAL, "Invalid timeout specified");
2565 
2566                 ms = (int)n;
2567         }
2568 
2569         if (!uc_nl_fill_cmds(ctx.cmds, cmds))
2570                 err_return(NLE_INVAL, "Invalid command ID specified");
2571 
2572         if (!uc_nl_evsock_init())
2573                 return NULL;
2574 
2575         cb = nl_cb_alloc(NL_CB_DEFAULT);
2576 
2577         if (!cb)
2578                 err_return(NLE_NOMEM, NULL);
2579 
2580         err = 0;
2581 
2582         nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL);
2583         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_event, &ctx);
2584         nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &err);
2585 
2586         pfd.fd = nl_socket_get_fd(nl80211_conn.evsock);
2587 
2588         if (poll(&pfd, 1, ms) == 1) {
2589                 while (err == 0 && ctx.cmd == 0)
2590                         nl_recvmsgs(nl80211_conn.evsock, cb);
2591         }
2592 
2593         nl_cb_put(cb);
2594 
2595         if (ctx.cmd) {
2596                 rv = ucv_object_new(vm);
2597 
2598                 ucv_object_add(rv, "cmd", ucv_int64_new(ctx.cmd));
2599                 ucv_object_add(rv, "msg", ctx.res);
2600 
2601                 return rv;
2602         }
2603         else if (err) {
2604                 err_return(err, NULL);
2605         }
2606         else {
2607                 err_return(NLE_FAILURE, "No event received");
2608         }
2609 }
2610 
2611 static uc_value_t *
2612 uc_nl_request_common(struct nl_sock *sock, uc_vm_t *vm, size_t nargs)
2613 {
2614         request_state_t st = { .vm = vm };
2615         uc_value_t *cmd = uc_fn_arg(0);
2616         uc_value_t *flags = uc_fn_arg(1);
2617         uc_value_t *payload = uc_fn_arg(2);
2618         uint16_t flagval = 0;
2619         struct nl_msg *msg;
2620         struct nl_cb *cb;
2621         int ret, id, cid;
2622 
2623         if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 ||
2624             (flags != NULL && ucv_type(flags) != UC_INTEGER) ||
2625             (payload != NULL && ucv_type(payload) != UC_OBJECT))
2626                 err_return(NLE_INVAL, NULL);
2627 
2628         if (flags) {
2629                 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff)
2630                         err_return(NLE_INVAL, NULL);
2631                 else
2632                         flagval = (uint16_t)ucv_int64_get(flags);
2633         }
2634 
2635         msg = nlmsg_alloc();
2636 
2637         if (!msg)
2638                 err_return(NLE_NOMEM, NULL);
2639 
2640         cid = ucv_int64_get(cmd);
2641 
2642         if (cid >= HWSIM_CMD_OFFSET) {
2643                 id = uc_nl_find_family_id("MAC80211_HWSIM");
2644                 cid -= HWSIM_CMD_OFFSET;
2645                 st.spec = &hwsim_msg;
2646         }
2647         else if (cid == NL80211_CMD_GET_WIPHY) {
2648                 id = uc_nl_find_family_id("nl80211");
2649                 st.spec = &nl80211_msg;
2650                 st.merge_phy_info = true;
2651 
2652                 if (ucv_object_get(payload, "wiphy", NULL) != NULL)
2653                         st.single_phy_info = true;
2654 
2655                 if (ucv_is_truish(ucv_object_get(payload, "split_wiphy_dump", NULL)))
2656                         flagval |= NLM_F_DUMP;
2657         }
2658         else {
2659                 id = uc_nl_find_family_id("nl80211");
2660                 st.spec = &nl80211_msg;
2661         }
2662 
2663         if (id < 0)
2664                 err_return(-id, NULL);
2665 
2666         genlmsg_put(msg, 0, 0, id, 0, flagval, cid, 0);
2667 
2668         if (!uc_nl_parse_attrs(msg, nlmsg_data(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) {
2669                 nlmsg_free(msg);
2670 
2671                 return NULL;
2672         }
2673 
2674         cb = nl_cb_alloc(NL_CB_DEFAULT);
2675 
2676         if (!cb) {
2677                 nlmsg_free(msg);
2678                 err_return(NLE_NOMEM, NULL);
2679         }
2680 
2681         ret = 1;
2682 
2683         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st);
2684         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
2685         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_done, &st);
2686         nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &ret);
2687 
2688         nl_send_auto_complete(sock, msg);
2689 
2690         while (ret > 0 && st.state < STATE_REPLIED)
2691                 nl_recvmsgs(sock, cb);
2692 
2693         nlmsg_free(msg);
2694         nl_cb_put(cb);
2695 
2696         if (ret < 0)
2697                 err_return(ret, NULL);
2698 
2699         switch (st.state) {
2700         case STATE_REPLIED:
2701                 return st.res;
2702 
2703         case STATE_UNREPLIED:
2704                 return ucv_boolean_new(true);
2705 
2706         default:
2707                 set_error(NLE_FAILURE, "Interrupted reply");
2708 
2709                 return ucv_boolean_new(false);
2710         }
2711 }
2712 
2713 static uc_value_t *
2714 uc_nl_request(uc_vm_t *vm, size_t nargs)
2715 {
2716         if (!uc_nl_connect_sock(&nl80211_conn.sock, false))
2717                 return NULL;
2718 
2719         return uc_nl_request_common(nl80211_conn.sock, vm, nargs);
2720 }
2721 
2722 static void
2723 uc_nl_listener_cb(struct uloop_fd *fd, unsigned int events)
2724 {
2725         while (nl_recvmsgs(nl80211_conn.evsock, nl80211_conn.evsock_cb) == 0)
2726                 ;
2727 }
2728 
2729 static uc_value_t *
2730 uc_nl_listener(uc_vm_t *vm, size_t nargs)
2731 {
2732         struct uloop_fd *fd = &nl80211_conn.evsock_fd;
2733         uc_nl_listener_t *l;
2734         uc_value_t *cb_func = uc_fn_arg(0);
2735         uc_value_t *cmds = uc_fn_arg(1);
2736         uc_value_t *rv;
2737         size_t i;
2738 
2739         if (!ucv_is_callable(cb_func)) {
2740                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid callback");
2741                 return NULL;
2742         }
2743 
2744         if (!uc_nl_evsock_init())
2745                 return NULL;
2746 
2747         if (!fd->registered) {
2748                 fd->fd = nl_socket_get_fd(nl80211_conn.evsock);
2749                 fd->cb = uc_nl_listener_cb;
2750                 uloop_fd_add(fd, ULOOP_READ);
2751         }
2752 
2753         if (!nl80211_conn.evsock_cb) {
2754                 struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
2755 
2756                 if (!cb)
2757                         err_return(NLE_NOMEM, NULL);
2758 
2759                 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL);
2760                 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_listener_event, NULL);
2761                 nl80211_conn.evsock_cb = cb;
2762         }
2763 
2764         for (i = 0; i < ucv_array_length(listener_registry); i += 2) {
2765                 if (!ucv_array_get(listener_registry, i))
2766                         break;
2767         }
2768 
2769         ucv_array_set(listener_registry, i + 1, ucv_get(cb_func));
2770         l = xalloc(sizeof(*l));
2771         l->index = i;
2772         if (!uc_nl_fill_cmds(l->cmds, cmds)) {
2773                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
2774                 free(l);
2775                 return NULL;
2776         }
2777 
2778         rv = uc_resource_new(listener_type, l);
2779         ucv_array_set(listener_registry, i, ucv_get(rv));
2780         listener_vm = vm;
2781 
2782         return rv;
2783 }
2784 
2785 static void
2786 uc_nl_listener_free(void *arg)
2787 {
2788         uc_nl_listener_t *l = arg;
2789 
2790         ucv_array_set(listener_registry, l->index, NULL);
2791         ucv_array_set(listener_registry, l->index + 1, NULL);
2792         free(l);
2793 }
2794 
2795 static uc_value_t *
2796 uc_nl_listener_set_commands(uc_vm_t *vm, size_t nargs)
2797 {
2798         uc_nl_listener_t *l = uc_fn_thisval("nl80211.listener");
2799         uc_value_t *cmds = uc_fn_arg(0);
2800 
2801         if (!l)
2802                 return NULL;
2803 
2804         memset(l->cmds, 0, sizeof(l->cmds));
2805         if (!uc_nl_fill_cmds(l->cmds, cmds))
2806                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
2807 
2808         return NULL;
2809 }
2810 
2811 static uc_value_t *
2812 uc_nl_listener_request(uc_vm_t *vm, size_t nargs)
2813 {
2814         return uc_nl_request_common(nl80211_conn.evsock, vm, nargs);
2815 }
2816 
2817 static uc_value_t *
2818 uc_nl_listener_close(uc_vm_t *vm, size_t nargs)
2819 {
2820         uc_nl_listener_t **lptr = uc_fn_this("nl80211.listener");
2821         uc_nl_listener_t *l;
2822 
2823         if (!lptr)
2824                 return NULL;
2825 
2826         l = *lptr;
2827         if (!l)
2828                 return NULL;
2829 
2830         *lptr = NULL;
2831         uc_nl_listener_free(l);
2832 
2833         return NULL;
2834 }
2835 
2836 
2837 static void
2838 register_constants(uc_vm_t *vm, uc_value_t *scope)
2839 {
2840         uc_value_t *c = ucv_object_new(vm);
2841 
2842 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x))
2843 
2844         ADD_CONST(NLM_F_ACK);
2845         ADD_CONST(NLM_F_ACK_TLVS);
2846         ADD_CONST(NLM_F_APPEND);
2847         ADD_CONST(NLM_F_ATOMIC);
2848         ADD_CONST(NLM_F_CAPPED);
2849         ADD_CONST(NLM_F_CREATE);
2850         ADD_CONST(NLM_F_DUMP);
2851         ADD_CONST(NLM_F_DUMP_FILTERED);
2852         ADD_CONST(NLM_F_DUMP_INTR);
2853         ADD_CONST(NLM_F_ECHO);
2854         ADD_CONST(NLM_F_EXCL);
2855         ADD_CONST(NLM_F_MATCH);
2856         ADD_CONST(NLM_F_MULTI);
2857         ADD_CONST(NLM_F_NONREC);
2858         ADD_CONST(NLM_F_REPLACE);
2859         ADD_CONST(NLM_F_REQUEST);
2860         ADD_CONST(NLM_F_ROOT);
2861 
2862         ADD_CONST(NL80211_CMD_GET_WIPHY);
2863         ADD_CONST(NL80211_CMD_SET_WIPHY);
2864         ADD_CONST(NL80211_CMD_NEW_WIPHY);
2865         ADD_CONST(NL80211_CMD_DEL_WIPHY);
2866         ADD_CONST(NL80211_CMD_GET_INTERFACE);
2867         ADD_CONST(NL80211_CMD_SET_INTERFACE);
2868         ADD_CONST(NL80211_CMD_NEW_INTERFACE);
2869         ADD_CONST(NL80211_CMD_DEL_INTERFACE);
2870         ADD_CONST(NL80211_CMD_GET_KEY);
2871         ADD_CONST(NL80211_CMD_SET_KEY);
2872         ADD_CONST(NL80211_CMD_NEW_KEY);
2873         ADD_CONST(NL80211_CMD_DEL_KEY);
2874         ADD_CONST(NL80211_CMD_GET_BEACON);
2875         ADD_CONST(NL80211_CMD_SET_BEACON);
2876         ADD_CONST(NL80211_CMD_START_AP);
2877         ADD_CONST(NL80211_CMD_NEW_BEACON);
2878         ADD_CONST(NL80211_CMD_STOP_AP);
2879         ADD_CONST(NL80211_CMD_DEL_BEACON);
2880         ADD_CONST(NL80211_CMD_GET_STATION);
2881         ADD_CONST(NL80211_CMD_SET_STATION);
2882         ADD_CONST(NL80211_CMD_NEW_STATION);
2883         ADD_CONST(NL80211_CMD_DEL_STATION);
2884         ADD_CONST(NL80211_CMD_GET_MPATH);
2885         ADD_CONST(NL80211_CMD_SET_MPATH);
2886         ADD_CONST(NL80211_CMD_NEW_MPATH);
2887         ADD_CONST(NL80211_CMD_DEL_MPATH);
2888         ADD_CONST(NL80211_CMD_SET_BSS);
2889         ADD_CONST(NL80211_CMD_SET_REG);
2890         ADD_CONST(NL80211_CMD_REQ_SET_REG);
2891         ADD_CONST(NL80211_CMD_GET_MESH_CONFIG);
2892         ADD_CONST(NL80211_CMD_SET_MESH_CONFIG);
2893         ADD_CONST(NL80211_CMD_GET_REG);
2894         ADD_CONST(NL80211_CMD_GET_SCAN);
2895         ADD_CONST(NL80211_CMD_TRIGGER_SCAN);
2896         ADD_CONST(NL80211_CMD_NEW_SCAN_RESULTS);
2897         ADD_CONST(NL80211_CMD_SCAN_ABORTED);
2898         ADD_CONST(NL80211_CMD_REG_CHANGE);
2899         ADD_CONST(NL80211_CMD_AUTHENTICATE);
2900         ADD_CONST(NL80211_CMD_ASSOCIATE);
2901         ADD_CONST(NL80211_CMD_DEAUTHENTICATE);
2902         ADD_CONST(NL80211_CMD_DISASSOCIATE);
2903         ADD_CONST(NL80211_CMD_MICHAEL_MIC_FAILURE);
2904         ADD_CONST(NL80211_CMD_REG_BEACON_HINT);
2905         ADD_CONST(NL80211_CMD_JOIN_IBSS);
2906         ADD_CONST(NL80211_CMD_LEAVE_IBSS);
2907         ADD_CONST(NL80211_CMD_TESTMODE);
2908         ADD_CONST(NL80211_CMD_CONNECT);
2909         ADD_CONST(NL80211_CMD_ROAM);
2910         ADD_CONST(NL80211_CMD_DISCONNECT);
2911         ADD_CONST(NL80211_CMD_SET_WIPHY_NETNS);
2912         ADD_CONST(NL80211_CMD_GET_SURVEY);
2913         ADD_CONST(NL80211_CMD_NEW_SURVEY_RESULTS);
2914         ADD_CONST(NL80211_CMD_SET_PMKSA);
2915         ADD_CONST(NL80211_CMD_DEL_PMKSA);
2916         ADD_CONST(NL80211_CMD_FLUSH_PMKSA);
2917         ADD_CONST(NL80211_CMD_REMAIN_ON_CHANNEL);
2918         ADD_CONST(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
2919         ADD_CONST(NL80211_CMD_SET_TX_BITRATE_MASK);
2920         ADD_CONST(NL80211_CMD_REGISTER_FRAME);
2921         ADD_CONST(NL80211_CMD_REGISTER_ACTION);
2922         ADD_CONST(NL80211_CMD_FRAME);
2923         ADD_CONST(NL80211_CMD_ACTION);
2924         ADD_CONST(NL80211_CMD_FRAME_TX_STATUS);
2925         ADD_CONST(NL80211_CMD_ACTION_TX_STATUS);
2926         ADD_CONST(NL80211_CMD_SET_POWER_SAVE);
2927         ADD_CONST(NL80211_CMD_GET_POWER_SAVE);
2928         ADD_CONST(NL80211_CMD_SET_CQM);
2929         ADD_CONST(NL80211_CMD_NOTIFY_CQM);
2930         ADD_CONST(NL80211_CMD_SET_CHANNEL);
2931         ADD_CONST(NL80211_CMD_SET_WDS_PEER);
2932         ADD_CONST(NL80211_CMD_FRAME_WAIT_CANCEL);
2933         ADD_CONST(NL80211_CMD_JOIN_MESH);
2934         ADD_CONST(NL80211_CMD_LEAVE_MESH);
2935         ADD_CONST(NL80211_CMD_UNPROT_DEAUTHENTICATE);
2936         ADD_CONST(NL80211_CMD_UNPROT_DISASSOCIATE);
2937         ADD_CONST(NL80211_CMD_NEW_PEER_CANDIDATE);
2938         ADD_CONST(NL80211_CMD_GET_WOWLAN);
2939         ADD_CONST(NL80211_CMD_SET_WOWLAN);
2940         ADD_CONST(NL80211_CMD_START_SCHED_SCAN);
2941         ADD_CONST(NL80211_CMD_STOP_SCHED_SCAN);
2942         ADD_CONST(NL80211_CMD_SCHED_SCAN_RESULTS);
2943         ADD_CONST(NL80211_CMD_SCHED_SCAN_STOPPED);
2944         ADD_CONST(NL80211_CMD_SET_REKEY_OFFLOAD);
2945         ADD_CONST(NL80211_CMD_PMKSA_CANDIDATE);
2946         ADD_CONST(NL80211_CMD_TDLS_OPER);
2947         ADD_CONST(NL80211_CMD_TDLS_MGMT);
2948         ADD_CONST(NL80211_CMD_UNEXPECTED_FRAME);
2949         ADD_CONST(NL80211_CMD_PROBE_CLIENT);
2950         ADD_CONST(NL80211_CMD_REGISTER_BEACONS);
2951         ADD_CONST(NL80211_CMD_UNEXPECTED_4ADDR_FRAME);
2952         ADD_CONST(NL80211_CMD_SET_NOACK_MAP);
2953         ADD_CONST(NL80211_CMD_CH_SWITCH_NOTIFY);
2954         ADD_CONST(NL80211_CMD_START_P2P_DEVICE);
2955         ADD_CONST(NL80211_CMD_STOP_P2P_DEVICE);
2956         ADD_CONST(NL80211_CMD_CONN_FAILED);
2957         ADD_CONST(NL80211_CMD_SET_MCAST_RATE);
2958         ADD_CONST(NL80211_CMD_SET_MAC_ACL);
2959         ADD_CONST(NL80211_CMD_RADAR_DETECT);
2960         ADD_CONST(NL80211_CMD_GET_PROTOCOL_FEATURES);
2961         ADD_CONST(NL80211_CMD_UPDATE_FT_IES);
2962         ADD_CONST(NL80211_CMD_FT_EVENT);
2963         ADD_CONST(NL80211_CMD_CRIT_PROTOCOL_START);
2964         ADD_CONST(NL80211_CMD_CRIT_PROTOCOL_STOP);
2965         ADD_CONST(NL80211_CMD_GET_COALESCE);
2966         ADD_CONST(NL80211_CMD_SET_COALESCE);
2967         ADD_CONST(NL80211_CMD_CHANNEL_SWITCH);
2968         ADD_CONST(NL80211_CMD_VENDOR);
2969         ADD_CONST(NL80211_CMD_SET_QOS_MAP);
2970         ADD_CONST(NL80211_CMD_ADD_TX_TS);
2971         ADD_CONST(NL80211_CMD_DEL_TX_TS);
2972         ADD_CONST(NL80211_CMD_GET_MPP);
2973         ADD_CONST(NL80211_CMD_JOIN_OCB);
2974         ADD_CONST(NL80211_CMD_LEAVE_OCB);
2975         ADD_CONST(NL80211_CMD_CH_SWITCH_STARTED_NOTIFY);
2976         ADD_CONST(NL80211_CMD_TDLS_CHANNEL_SWITCH);
2977         ADD_CONST(NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
2978 
2979         ADD_CONST(HWSIM_CMD_REGISTER),
2980         ADD_CONST(HWSIM_CMD_FRAME),
2981         ADD_CONST(HWSIM_CMD_TX_INFO_FRAME),
2982         ADD_CONST(HWSIM_CMD_NEW_RADIO),
2983         ADD_CONST(HWSIM_CMD_DEL_RADIO),
2984         ADD_CONST(HWSIM_CMD_GET_RADIO),
2985         ADD_CONST(HWSIM_CMD_ADD_MAC_ADDR),
2986         ADD_CONST(HWSIM_CMD_DEL_MAC_ADDR),
2987         ADD_CONST(HWSIM_CMD_START_PMSR),
2988         ADD_CONST(HWSIM_CMD_ABORT_PMSR),
2989         ADD_CONST(HWSIM_CMD_REPORT_PMSR),
2990 
2991         ADD_CONST(NL80211_IFTYPE_ADHOC);
2992         ADD_CONST(NL80211_IFTYPE_STATION);
2993         ADD_CONST(NL80211_IFTYPE_AP);
2994         ADD_CONST(NL80211_IFTYPE_AP_VLAN);
2995         ADD_CONST(NL80211_IFTYPE_WDS);
2996         ADD_CONST(NL80211_IFTYPE_MONITOR);
2997         ADD_CONST(NL80211_IFTYPE_MESH_POINT);
2998         ADD_CONST(NL80211_IFTYPE_P2P_CLIENT);
2999         ADD_CONST(NL80211_IFTYPE_P2P_GO);
3000         ADD_CONST(NL80211_IFTYPE_P2P_DEVICE);
3001         ADD_CONST(NL80211_IFTYPE_OCB);
3002 
3003         ucv_object_add(scope, "const", c);
3004 };
3005 
3006 static const uc_function_list_t global_fns[] = {
3007         { "error",              uc_nl_error },
3008         { "request",    uc_nl_request },
3009         { "waitfor",    uc_nl_waitfor },
3010         { "listener",   uc_nl_listener },
3011 };
3012 
3013 
3014 static const uc_function_list_t listener_fns[] = {
3015         { "set_commands",       uc_nl_listener_set_commands },
3016         { "request",            uc_nl_listener_request },
3017         { "close",                      uc_nl_listener_close },
3018 };
3019 
3020 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
3021 {
3022         uc_function_list_register(scope, global_fns);
3023 
3024         listener_type = uc_type_declare(vm, "nl80211.listener", listener_fns, uc_nl_listener_free);
3025         listener_registry = ucv_array_new(vm);
3026 
3027         uc_vm_registry_set(vm, "nl80211.registry", listener_registry);
3028 
3029         register_constants(vm, scope);
3030 }
3031 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt