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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt