blob: cbe27697ed6c421470baaf7c1d7819b761d65c46 [file] [log] [blame]
developer617abbd2024-04-23 14:50:01 +08001From ccb5628a9c5eae2b56cb88f43f850146863cba30 Mon Sep 17 00:00:00 2001
2From: Evelyn Tsai <evelyn.tsai@mediatek.com>
3Date: Tue, 23 Jan 2024 16:46:44 +0800
4Subject: [PATCH 027/104] hostapd: sync 2024-01-18 openwrt/trunk src folder
5
6---
7 hostapd/radius.c | 715 +++++++++++++
8 src/ap/ubus.c | 2005 ++++++++++++++++++++++++++++++++++++
9 src/ap/ubus.h | 154 +++
10 src/ap/ucode.c | 813 +++++++++++++++
11 src/ap/ucode.h | 54 +
12 src/utils/build_features.h | 65 ++
13 src/utils/ucode.c | 502 +++++++++
14 src/utils/ucode.h | 30 +
15 wpa_supplicant/ubus.c | 280 +++++
16 wpa_supplicant/ubus.h | 55 +
17 wpa_supplicant/ucode.c | 299 ++++++
18 wpa_supplicant/ucode.h | 49 +
19 12 files changed, 5021 insertions(+)
20 create mode 100644 hostapd/radius.c
21 create mode 100644 src/ap/ubus.c
22 create mode 100644 src/ap/ubus.h
23 create mode 100644 src/ap/ucode.c
24 create mode 100644 src/ap/ucode.h
25 create mode 100644 src/utils/build_features.h
26 create mode 100644 src/utils/ucode.c
27 create mode 100644 src/utils/ucode.h
28 create mode 100644 wpa_supplicant/ubus.c
29 create mode 100644 wpa_supplicant/ubus.h
30 create mode 100644 wpa_supplicant/ucode.c
31 create mode 100644 wpa_supplicant/ucode.h
32
33diff --git a/hostapd/radius.c b/hostapd/radius.c
34new file mode 100644
35index 000000000..362a22c27
36--- /dev/null
37+++ b/hostapd/radius.c
38@@ -0,0 +1,715 @@
39+#include "utils/includes.h"
40+#include "utils/common.h"
41+#include "utils/eloop.h"
42+#include "crypto/crypto.h"
43+#include "crypto/tls.h"
44+
45+#include "ap/ap_config.h"
46+#include "eap_server/eap.h"
47+#include "radius/radius.h"
48+#include "radius/radius_server.h"
49+#include "eap_register.h"
50+
51+#include <libubox/blobmsg_json.h>
52+#include <libubox/blobmsg.h>
53+#include <libubox/avl.h>
54+#include <libubox/avl-cmp.h>
55+#include <libubox/kvlist.h>
56+
57+#include <sys/stat.h>
58+#include <fnmatch.h>
59+
60+#define VENDOR_ID_WISPR 14122
61+#define VENDOR_ATTR_SIZE 6
62+
63+struct radius_parse_attr_data {
64+ unsigned int vendor;
65+ u8 type;
66+ int size;
67+ char format;
68+ const char *data;
69+};
70+
71+struct radius_parse_attr_state {
72+ struct hostapd_radius_attr *prev;
73+ struct hostapd_radius_attr *attr;
74+ struct wpabuf *buf;
75+ void *attrdata;
76+};
77+
78+struct radius_user_state {
79+ struct avl_node node;
80+ struct eap_user data;
81+};
82+
83+struct radius_user_data {
84+ struct kvlist users;
85+ struct avl_tree user_state;
86+ struct blob_attr *wildcard;
87+};
88+
89+struct radius_state {
90+ struct radius_server_data *radius;
91+ struct eap_config eap;
92+
93+ struct radius_user_data phase1, phase2;
94+ const char *user_file;
95+ time_t user_file_ts;
96+
97+ int n_attrs;
98+ struct hostapd_radius_attr *attrs;
99+};
100+
101+struct radius_config {
102+ struct tls_connection_params tls;
103+ struct radius_server_conf radius;
104+};
105+
106+enum {
107+ USER_ATTR_PASSWORD,
108+ USER_ATTR_HASH,
109+ USER_ATTR_SALT,
110+ USER_ATTR_METHODS,
111+ USER_ATTR_RADIUS,
112+ USER_ATTR_VLAN,
113+ USER_ATTR_MAX_RATE_UP,
114+ USER_ATTR_MAX_RATE_DOWN,
115+ __USER_ATTR_MAX
116+};
117+
118+static void radius_tls_event(void *ctx, enum tls_event ev,
119+ union tls_event_data *data)
120+{
121+ switch (ev) {
122+ case TLS_CERT_CHAIN_SUCCESS:
123+ wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
124+ break;
125+ case TLS_CERT_CHAIN_FAILURE:
126+ wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
127+ data->cert_fail.reason,
128+ data->cert_fail.depth,
129+ data->cert_fail.subject,
130+ data->cert_fail.reason_txt);
131+ break;
132+ case TLS_PEER_CERTIFICATE:
133+ wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
134+ data->peer_cert.depth,
135+ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
136+ data->peer_cert.subject);
137+ break;
138+ case TLS_ALERT:
139+ if (data->alert.is_local)
140+ wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
141+ data->alert.description);
142+ else
143+ wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
144+ data->alert.description);
145+ break;
146+ case TLS_UNSAFE_RENEGOTIATION_DISABLED:
147+ /* Not applicable to TLS server */
148+ break;
149+ }
150+}
151+
152+static void radius_userdata_init(struct radius_user_data *u)
153+{
154+ kvlist_init(&u->users, kvlist_blob_len);
155+ avl_init(&u->user_state, avl_strcmp, false, NULL);
156+}
157+
158+static void radius_userdata_free(struct radius_user_data *u)
159+{
160+ struct radius_user_state *s, *tmp;
161+
162+ kvlist_free(&u->users);
163+ free(u->wildcard);
164+ u->wildcard = NULL;
165+ avl_remove_all_elements(&u->user_state, s, node, tmp)
166+ free(s);
167+}
168+
169+static void
170+radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
171+{
172+ enum {
173+ USERSTATE_USERS,
174+ USERSTATE_WILDCARD,
175+ __USERSTATE_MAX,
176+ };
177+ static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
178+ [USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
179+ [USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
180+ };
181+ struct blob_attr *tb[__USERSTATE_MAX], *cur;
182+ int rem;
183+
184+ if (!data)
185+ return;
186+
187+ blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
188+
189+ blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
190+ kvlist_set(&u->users, blobmsg_name(cur), cur);
191+
192+ if (tb[USERSTATE_WILDCARD])
193+ u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
194+}
195+
196+static void
197+load_userfile(struct radius_state *s)
198+{
199+ enum {
200+ USERDATA_PHASE1,
201+ USERDATA_PHASE2,
202+ __USERDATA_MAX
203+ };
204+ static const struct blobmsg_policy policy[__USERDATA_MAX] = {
205+ [USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
206+ [USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
207+ };
208+ struct blob_attr *tb[__USERDATA_MAX], *cur;
209+ static struct blob_buf b;
210+ struct stat st;
211+ int rem;
212+
213+ if (stat(s->user_file, &st))
214+ return;
215+
216+ if (s->user_file_ts == st.st_mtime)
217+ return;
218+
219+ s->user_file_ts = st.st_mtime;
220+ radius_userdata_free(&s->phase1);
221+ radius_userdata_free(&s->phase2);
222+
223+ blob_buf_init(&b, 0);
224+ blobmsg_add_json_from_file(&b, s->user_file);
225+ blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
226+ radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
227+ radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
228+
229+ blob_buf_free(&b);
230+}
231+
232+static struct blob_attr *
233+radius_user_get(struct radius_user_data *s, const char *name)
234+{
235+ struct blob_attr *cur;
236+ int rem;
237+
238+ cur = kvlist_get(&s->users, name);
239+ if (cur)
240+ return cur;
241+
242+ blobmsg_for_each_attr(cur, s->wildcard, rem) {
243+ static const struct blobmsg_policy policy = {
244+ "name", BLOBMSG_TYPE_STRING
245+ };
246+ struct blob_attr *pattern;
247+
248+ if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
249+ continue;
250+
251+ blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
252+ if (!name)
253+ continue;
254+
255+ if (!fnmatch(blobmsg_get_string(pattern), name, 0))
256+ return cur;
257+ }
258+
259+ return NULL;
260+}
261+
262+static struct radius_parse_attr_data *
263+radius_parse_attr(struct blob_attr *attr)
264+{
265+ static const struct blobmsg_policy policy[4] = {
266+ { .type = BLOBMSG_TYPE_INT32 },
267+ { .type = BLOBMSG_TYPE_INT32 },
268+ { .type = BLOBMSG_TYPE_STRING },
269+ { .type = BLOBMSG_TYPE_STRING },
270+ };
271+ static struct radius_parse_attr_data data;
272+ struct blob_attr *tb[4];
273+ const char *format;
274+
275+ blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
276+
277+ if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
278+ return NULL;
279+
280+ format = blobmsg_get_string(tb[2]);
281+ if (strlen(format) != 1)
282+ return NULL;
283+
284+ data.vendor = blobmsg_get_u32(tb[0]);
285+ data.type = blobmsg_get_u32(tb[1]);
286+ data.format = format[0];
287+ data.data = blobmsg_get_string(tb[3]);
288+ data.size = strlen(data.data);
289+
290+ switch (data.format) {
291+ case 's':
292+ break;
293+ case 'x':
294+ if (data.size & 1)
295+ return NULL;
296+ data.size /= 2;
297+ break;
298+ case 'd':
299+ data.size = 4;
300+ break;
301+ default:
302+ return NULL;
303+ }
304+
305+ return &data;
306+}
307+
308+static void
309+radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
310+{
311+ struct blob_attr *data = tb[USER_ATTR_RADIUS];
312+ struct blob_attr *cur;
313+ int rem;
314+
315+ blobmsg_for_each_attr(cur, data, rem) {
316+ struct radius_parse_attr_data *data;
317+ size_t prev = *attr_size;
318+
319+ data = radius_parse_attr(cur);
320+ if (!data)
321+ continue;
322+
323+ *attr_size += data->size;
324+ if (data->vendor)
325+ *attr_size += VENDOR_ATTR_SIZE;
326+
327+ (*n_attr)++;
328+ }
329+
330+ *n_attr += !!tb[USER_ATTR_VLAN] * 3 +
331+ !!tb[USER_ATTR_MAX_RATE_UP] +
332+ !!tb[USER_ATTR_MAX_RATE_DOWN];
333+ *attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
334+ !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
335+ !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
336+}
337+
338+static void *
339+radius_add_attr(struct radius_parse_attr_state *state,
340+ u32 vendor, u8 type, u8 len)
341+{
342+ struct hostapd_radius_attr *attr;
343+ struct wpabuf *buf;
344+ void *val;
345+
346+ val = state->attrdata;
347+
348+ buf = state->buf++;
349+ buf->buf = val;
350+
351+ attr = state->attr++;
352+ attr->val = buf;
353+ attr->type = type;
354+
355+ if (state->prev)
356+ state->prev->next = attr;
357+ state->prev = attr;
358+
359+ if (vendor) {
360+ u8 *vendor_hdr = val + 4;
361+
362+ WPA_PUT_BE32(val, vendor);
363+ vendor_hdr[0] = type;
364+ vendor_hdr[1] = len + 2;
365+
366+ len += VENDOR_ATTR_SIZE;
367+ val += VENDOR_ATTR_SIZE;
368+ attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
369+ }
370+
371+ buf->size = buf->used = len;
372+ state->attrdata += len;
373+
374+ return val;
375+}
376+
377+static void
378+radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
379+{
380+ struct blob_attr *data = tb[USER_ATTR_RADIUS];
381+ struct hostapd_radius_attr *prev = NULL;
382+ struct blob_attr *cur;
383+ int len, rem;
384+ void *val;
385+
386+ if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
387+ char buf[5];
388+
389+ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
390+ WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
391+
392+ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
393+ WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
394+
395+ len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
396+ val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
397+ memcpy(val, buf, len);
398+ }
399+
400+ if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
401+ val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
402+ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
403+ }
404+
405+ if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
406+ val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
407+ WPA_PUT_BE32(val, blobmsg_get_u32(cur));
408+ }
409+
410+ blobmsg_for_each_attr(cur, data, rem) {
411+ struct radius_parse_attr_data *data;
412+ void *val;
413+ int size;
414+
415+ data = radius_parse_attr(cur);
416+ if (!data)
417+ continue;
418+
419+ val = radius_add_attr(state, data->vendor, data->type, data->size);
420+ switch (data->format) {
421+ case 's':
422+ memcpy(val, data->data, data->size);
423+ break;
424+ case 'x':
425+ hexstr2bin(data->data, val, data->size);
426+ break;
427+ case 'd':
428+ WPA_PUT_BE32(val, atoi(data->data));
429+ break;
430+ }
431+ }
432+}
433+
434+static void
435+radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
436+{
437+ struct blob_attr *cur;
438+ int rem, n = 0;
439+
440+ if (!data)
441+ return;
442+
443+ blobmsg_for_each_attr(cur, data, rem) {
444+ const char *method;
445+
446+ if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
447+ continue;
448+
449+ if (n == EAP_MAX_METHODS)
450+ break;
451+
452+ method = blobmsg_get_string(cur);
453+ eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
454+ if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
455+ eap->methods[n].method == EAP_TYPE_NONE) {
456+ if (!strcmp(method, "TTLS-PAP")) {
457+ eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
458+ continue;
459+ }
460+ if (!strcmp(method, "TTLS-CHAP")) {
461+ eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
462+ continue;
463+ }
464+ if (!strcmp(method, "TTLS-MSCHAP")) {
465+ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
466+ continue;
467+ }
468+ if (!strcmp(method, "TTLS-MSCHAPV2")) {
469+ eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
470+ continue;
471+ }
472+ }
473+ n++;
474+ }
475+}
476+
477+static struct eap_user *
478+radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
479+ const char *id)
480+{
481+ static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
482+ [USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
483+ [USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
484+ [USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
485+ [USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
486+ [USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
487+ [USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
488+ [USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
489+ [USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
490+ };
491+ struct blob_attr *tb[__USER_ATTR_MAX], *cur;
492+ char *password_buf, *salt_buf, *name_buf;
493+ struct radius_parse_attr_state astate = {};
494+ struct hostapd_radius_attr *attr;
495+ struct radius_user_state *state;
496+ int pw_len = 0, salt_len = 0;
497+ struct eap_user *eap;
498+ struct wpabuf *val;
499+ size_t attrsize = 0;
500+ void *attrdata;
501+ int n_attr = 0;
502+
503+ state = avl_find_element(&u->user_state, id, state, node);
504+ if (state)
505+ return &state->data;
506+
507+ blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
508+
509+ if ((cur = tb[USER_ATTR_SALT]) != NULL)
510+ salt_len = strlen(blobmsg_get_string(cur)) / 2;
511+ if ((cur = tb[USER_ATTR_HASH]) != NULL)
512+ pw_len = strlen(blobmsg_get_string(cur)) / 2;
513+ else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
514+ pw_len = blobmsg_len(cur) - 1;
515+ radius_count_attrs(tb, &n_attr, &attrsize);
516+
517+ state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
518+ &password_buf, pw_len,
519+ &salt_buf, salt_len,
520+ &astate.attr, n_attr * sizeof(*astate.attr),
521+ &astate.buf, n_attr * sizeof(*astate.buf),
522+ &astate.attrdata, attrsize);
523+ eap = &state->data;
524+ eap->salt = salt_len ? salt_buf : NULL;
525+ eap->salt_len = salt_len;
526+ eap->password = pw_len ? password_buf : NULL;
527+ eap->password_len = pw_len;
528+ eap->force_version = -1;
529+
530+ if ((cur = tb[USER_ATTR_SALT]) != NULL)
531+ hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
532+ if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
533+ memcpy(password_buf, blobmsg_get_string(cur), pw_len);
534+ else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
535+ hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
536+ eap->password_hash = 1;
537+ }
538+ radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
539+
540+ if (n_attr > 0) {
541+ cur = tb[USER_ATTR_RADIUS];
542+ eap->accept_attr = astate.attr;
543+ radius_parse_attrs(tb, &astate);
544+ }
545+
546+ state->node.key = strcpy(name_buf, id);
547+ avl_insert(&u->user_state, &state->node);
548+
549+ return &state->data;
550+
551+free:
552+ free(state);
553+ return NULL;
554+}
555+
556+static int radius_get_eap_user(void *ctx, const u8 *identity,
557+ size_t identity_len, int phase2,
558+ struct eap_user *user)
559+{
560+ struct radius_state *s = ctx;
561+ struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
562+ struct blob_attr *entry;
563+ struct eap_user *data;
564+ char *id;
565+
566+ if (identity_len > 512)
567+ return -1;
568+
569+ load_userfile(s);
570+
571+ id = alloca(identity_len + 1);
572+ memcpy(id, identity, identity_len);
573+ id[identity_len] = 0;
574+
575+ entry = radius_user_get(u, id);
576+ if (!entry)
577+ return -1;
578+
579+ if (!user)
580+ return 0;
581+
582+ data = radius_user_get_state(u, entry, id);
583+ if (!data)
584+ return -1;
585+
586+ *user = *data;
587+ if (user->password_len > 0)
588+ user->password = os_memdup(user->password, user->password_len);
589+ if (user->salt_len > 0)
590+ user->salt = os_memdup(user->salt, user->salt_len);
591+ user->phase2 = phase2;
592+
593+ return 0;
594+}
595+
596+static int radius_setup(struct radius_state *s, struct radius_config *c)
597+{
598+ struct eap_config *eap = &s->eap;
599+ struct tls_config conf = {
600+ .event_cb = radius_tls_event,
601+ .tls_flags = TLS_CONN_DISABLE_TLSv1_3,
602+ .cb_ctx = s,
603+ };
604+
605+ eap->eap_server = 1;
606+ eap->max_auth_rounds = 100;
607+ eap->max_auth_rounds_short = 50;
608+ eap->ssl_ctx = tls_init(&conf);
609+ if (!eap->ssl_ctx) {
610+ wpa_printf(MSG_INFO, "TLS init failed\n");
611+ return 1;
612+ }
613+
614+ if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
615+ wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
616+ return 1;
617+ }
618+
619+ c->radius.eap_cfg = eap;
620+ c->radius.conf_ctx = s;
621+ c->radius.get_eap_user = radius_get_eap_user;
622+ s->radius = radius_server_init(&c->radius);
623+ if (!s->radius) {
624+ wpa_printf(MSG_INFO, "failed to initialize radius server\n");
625+ return 1;
626+ }
627+
628+ return 0;
629+}
630+
631+static int radius_init(struct radius_state *s)
632+{
633+ memset(s, 0, sizeof(*s));
634+ radius_userdata_init(&s->phase1);
635+ radius_userdata_init(&s->phase2);
636+}
637+
638+static void radius_deinit(struct radius_state *s)
639+{
640+ if (s->radius)
641+ radius_server_deinit(s->radius);
642+
643+ if (s->eap.ssl_ctx)
644+ tls_deinit(s->eap.ssl_ctx);
645+
646+ radius_userdata_free(&s->phase1);
647+ radius_userdata_free(&s->phase2);
648+}
649+
650+static int usage(const char *progname)
651+{
652+ fprintf(stderr, "Usage: %s <options>\n",
653+ progname);
654+}
655+
656+int radius_main(int argc, char **argv)
657+{
658+ static struct radius_state state = {};
659+ static struct radius_config config = {};
660+ const char *progname = argv[0];
661+ int ret = 0;
662+ int ch;
663+
664+ wpa_debug_setup_stdout();
665+ wpa_debug_level = 0;
666+
667+ if (eloop_init()) {
668+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
669+ return 1;
670+ }
671+
672+ eap_server_register_methods();
673+ radius_init(&state);
674+
675+ while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
676+ switch (ch) {
677+ case '6':
678+ config.radius.ipv6 = 1;
679+ break;
680+ case 'C':
681+ config.tls.ca_cert = optarg;
682+ break;
683+ case 'c':
684+ if (config.tls.client_cert2)
685+ return usage(progname);
686+
687+ if (config.tls.client_cert)
688+ config.tls.client_cert2 = optarg;
689+ else
690+ config.tls.client_cert = optarg;
691+ break;
692+ case 'd':
693+ config.tls.dh_file = optarg;
694+ break;
695+ case 'i':
696+ state.eap.server_id = optarg;
697+ state.eap.server_id_len = strlen(optarg);
698+ break;
699+ case 'k':
700+ if (config.tls.private_key2)
701+ return usage(progname);
702+
703+ if (config.tls.private_key)
704+ config.tls.private_key2 = optarg;
705+ else
706+ config.tls.private_key = optarg;
707+ break;
708+ case 'K':
709+ if (config.tls.private_key_passwd2)
710+ return usage(progname);
711+
712+ if (config.tls.private_key_passwd)
713+ config.tls.private_key_passwd2 = optarg;
714+ else
715+ config.tls.private_key_passwd = optarg;
716+ break;
717+ case 'p':
718+ config.radius.auth_port = atoi(optarg);
719+ break;
720+ case 'P':
721+ config.radius.acct_port = atoi(optarg);
722+ break;
723+ case 's':
724+ config.radius.client_file = optarg;
725+ break;
726+ case 'u':
727+ state.user_file = optarg;
728+ break;
729+ default:
730+ return usage(progname);
731+ }
732+ }
733+
734+ if (!config.tls.client_cert || !config.tls.private_key ||
735+ !config.radius.client_file || !state.eap.server_id ||
736+ !state.user_file) {
737+ wpa_printf(MSG_INFO, "missing options\n");
738+ goto out;
739+ }
740+
741+ ret = radius_setup(&state, &config);
742+ if (ret)
743+ goto out;
744+
745+ load_userfile(&state);
746+ eloop_run();
747+
748+out:
749+ radius_deinit(&state);
750+ os_program_deinit();
751+
752+ return ret;
753+}
754diff --git a/src/ap/ubus.c b/src/ap/ubus.c
755new file mode 100644
756index 000000000..f2041a0c9
757--- /dev/null
758+++ b/src/ap/ubus.c
759@@ -0,0 +1,2005 @@
760+/*
761+ * hostapd / ubus support
762+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
763+ *
764+ * This software may be distributed under the terms of the BSD license.
765+ * See README for more details.
766+ */
767+
768+#include "utils/includes.h"
769+#include "utils/common.h"
770+#include "utils/eloop.h"
771+#include "utils/wpabuf.h"
772+#include "common/ieee802_11_defs.h"
773+#include "common/hw_features_common.h"
774+#include "hostapd.h"
775+#include "neighbor_db.h"
776+#include "wps_hostapd.h"
777+#include "sta_info.h"
778+#include "ubus.h"
779+#include "ap_drv_ops.h"
780+#include "beacon.h"
781+#include "rrm.h"
782+#include "wnm_ap.h"
783+#include "taxonomy.h"
784+#include "airtime_policy.h"
785+#include "hw_features.h"
786+
787+static struct ubus_context *ctx;
788+static struct blob_buf b;
789+static int ctx_ref;
790+
791+static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
792+{
793+ return container_of(obj, struct hostapd_data, ubus.obj);
794+}
795+
796+struct ubus_banned_client {
797+ struct avl_node avl;
798+ u8 addr[ETH_ALEN];
799+};
800+
801+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
802+{
803+ if (ubus_reconnect(ctx, NULL)) {
804+ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
805+ return;
806+ }
807+
808+ ubus_add_uloop(ctx);
809+}
810+
811+static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
812+{
813+ uloop_fd_delete(&ctx->sock);
814+ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
815+}
816+
817+static bool hostapd_ubus_init(void)
818+{
819+ if (ctx)
820+ return true;
821+
822+ eloop_add_uloop();
823+ ctx = ubus_connect(NULL);
824+ if (!ctx)
825+ return false;
826+
827+ ctx->connection_lost = hostapd_ubus_connection_lost;
828+ ubus_add_uloop(ctx);
829+
830+ return true;
831+}
832+
833+static void hostapd_ubus_ref_inc(void)
834+{
835+ ctx_ref++;
836+}
837+
838+static void hostapd_ubus_ref_dec(void)
839+{
840+ ctx_ref--;
841+ if (!ctx)
842+ return;
843+
844+ if (ctx_ref)
845+ return;
846+
847+ uloop_fd_delete(&ctx->sock);
848+ ubus_free(ctx);
849+ ctx = NULL;
850+}
851+
852+void hostapd_ubus_add_iface(struct hostapd_iface *iface)
853+{
854+ if (!hostapd_ubus_init())
855+ return;
856+}
857+
858+void hostapd_ubus_free_iface(struct hostapd_iface *iface)
859+{
860+ if (!ctx)
861+ return;
862+}
863+
864+static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
865+{
866+ char *event_type;
867+
868+ if (!ctx || !obj)
869+ return;
870+
871+ if (asprintf(&event_type, "bss.%s", event) < 0)
872+ return;
873+
874+ blob_buf_init(&b, 0);
875+ blobmsg_add_string(&b, "name", bssname);
876+ ubus_notify(ctx, obj, event_type, b.head, -1);
877+ free(event_type);
878+}
879+
880+static void
881+hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
882+{
883+ struct ubus_banned_client *ban = eloop_data;
884+ struct hostapd_data *hapd = user_ctx;
885+
886+ avl_delete(&hapd->ubus.banned, &ban->avl);
887+ free(ban);
888+}
889+
890+static void
891+hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
892+{
893+ struct ubus_banned_client *ban;
894+
895+ if (time < 0)
896+ time = 0;
897+
898+ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
899+ if (!ban) {
900+ if (!time)
901+ return;
902+
903+ ban = os_zalloc(sizeof(*ban));
904+ memcpy(ban->addr, addr, sizeof(ban->addr));
905+ ban->avl.key = ban->addr;
906+ avl_insert(&hapd->ubus.banned, &ban->avl);
907+ } else {
908+ eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
909+ if (!time) {
910+ hostapd_bss_del_ban(ban, hapd);
911+ return;
912+ }
913+ }
914+
915+ eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
916+}
917+
918+static int
919+hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
920+ struct ubus_request_data *req, const char *method,
921+ struct blob_attr *msg)
922+{
923+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
924+
925+ return hostapd_reload_config(hapd->iface);
926+}
927+
928+
929+static void
930+hostapd_parse_vht_map_blobmsg(uint16_t map)
931+{
932+ char label[4];
933+ int16_t val;
934+ int i;
935+
936+ for (i = 0; i < 8; i++) {
937+ snprintf(label, 4, "%dss", i + 1);
938+
939+ val = (map & (BIT(1) | BIT(0))) + 7;
940+ blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
941+ map = map >> 2;
942+ }
943+}
944+
945+static void
946+hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
947+{
948+ void *supported_mcs;
949+ void *map;
950+ int i;
951+
952+ static const struct {
953+ const char *name;
954+ uint32_t flag;
955+ } vht_capas[] = {
956+ { "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
957+ { "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
958+ };
959+
960+ for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
961+ blobmsg_add_u8(&b, vht_capas[i].name,
962+ !!(vhtc->vht_capabilities_info & vht_capas[i].flag));
963+
964+ supported_mcs = blobmsg_open_table(&b, "mcs_map");
965+
966+ /* RX map */
967+ map = blobmsg_open_table(&b, "rx");
968+ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
969+ blobmsg_close_table(&b, map);
970+
971+ /* TX map */
972+ map = blobmsg_open_table(&b, "tx");
973+ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
974+ blobmsg_close_table(&b, map);
975+
976+ blobmsg_close_table(&b, supported_mcs);
977+}
978+
979+static void
980+hostapd_parse_capab_blobmsg(struct sta_info *sta)
981+{
982+ void *r, *v;
983+
984+ v = blobmsg_open_table(&b, "capabilities");
985+
986+ if (sta->vht_capabilities) {
987+ r = blobmsg_open_table(&b, "vht");
988+ hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
989+ blobmsg_close_table(&b, r);
990+ }
991+
992+ /* ToDo: Add HT / HE capability parsing */
993+
994+ blobmsg_close_table(&b, v);
995+}
996+
997+static int
998+hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
999+ struct ubus_request_data *req, const char *method,
1000+ struct blob_attr *msg)
1001+{
1002+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1003+ struct hostap_sta_driver_data sta_driver_data;
1004+ struct sta_info *sta;
1005+ void *list, *c;
1006+ char mac_buf[20];
1007+ static const struct {
1008+ const char *name;
1009+ uint32_t flag;
1010+ } sta_flags[] = {
1011+ { "auth", WLAN_STA_AUTH },
1012+ { "assoc", WLAN_STA_ASSOC },
1013+ { "authorized", WLAN_STA_AUTHORIZED },
1014+ { "preauth", WLAN_STA_PREAUTH },
1015+ { "wds", WLAN_STA_WDS },
1016+ { "wmm", WLAN_STA_WMM },
1017+ { "ht", WLAN_STA_HT },
1018+ { "vht", WLAN_STA_VHT },
1019+ { "he", WLAN_STA_HE },
1020+ { "wps", WLAN_STA_WPS },
1021+ { "mfp", WLAN_STA_MFP },
1022+ };
1023+
1024+ blob_buf_init(&b, 0);
1025+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
1026+ list = blobmsg_open_table(&b, "clients");
1027+ for (sta = hapd->sta_list; sta; sta = sta->next) {
1028+ void *r;
1029+ int i;
1030+
1031+ sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
1032+ c = blobmsg_open_table(&b, mac_buf);
1033+ for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
1034+ blobmsg_add_u8(&b, sta_flags[i].name,
1035+ !!(sta->flags & sta_flags[i].flag));
1036+
1037+#ifdef CONFIG_MBO
1038+ blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
1039+#endif
1040+
1041+ r = blobmsg_open_array(&b, "rrm");
1042+ for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
1043+ blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
1044+ blobmsg_close_array(&b, r);
1045+
1046+ r = blobmsg_open_array(&b, "extended_capabilities");
1047+ /* Check if client advertises extended capabilities */
1048+ if (sta->ext_capability && sta->ext_capability[0] > 0) {
1049+ for (i = 0; i < sta->ext_capability[0]; i++) {
1050+ blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
1051+ }
1052+ }
1053+ blobmsg_close_array(&b, r);
1054+
1055+ blobmsg_add_u32(&b, "aid", sta->aid);
1056+#ifdef CONFIG_TAXONOMY
1057+ r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
1058+ if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
1059+ blobmsg_add_string_buffer(&b);
1060+#endif
1061+
1062+ /* Driver information */
1063+ if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
1064+ r = blobmsg_open_table(&b, "bytes");
1065+ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
1066+ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
1067+ blobmsg_close_table(&b, r);
1068+ r = blobmsg_open_table(&b, "airtime");
1069+ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
1070+ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
1071+ blobmsg_close_table(&b, r);
1072+ r = blobmsg_open_table(&b, "packets");
1073+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
1074+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
1075+ blobmsg_close_table(&b, r);
1076+ r = blobmsg_open_table(&b, "rate");
1077+ /* Rate in kbits */
1078+ blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
1079+ blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
1080+ blobmsg_close_table(&b, r);
1081+ blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
1082+ }
1083+
1084+ hostapd_parse_capab_blobmsg(sta);
1085+
1086+ blobmsg_close_table(&b, c);
1087+ }
1088+ blobmsg_close_array(&b, list);
1089+ ubus_send_reply(ctx, req, b.head);
1090+
1091+ return 0;
1092+}
1093+
1094+static int
1095+hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
1096+ struct ubus_request_data *req, const char *method,
1097+ struct blob_attr *msg)
1098+{
1099+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1100+
1101+ blob_buf_init(&b, 0);
1102+ blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
1103+ blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
1104+ ubus_send_reply(ctx, req, b.head);
1105+
1106+ return 0;
1107+}
1108+
1109+static int
1110+hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
1111+ struct ubus_request_data *req, const char *method,
1112+ struct blob_attr *msg)
1113+{
1114+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1115+ void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
1116+ struct os_reltime now;
1117+ char ssid[SSID_MAX_LEN + 1];
1118+ char phy_name[17];
1119+ size_t ssid_len = SSID_MAX_LEN;
1120+ u8 channel = 0, op_class = 0;
1121+
1122+ if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
1123+ ssid_len = hapd->conf->ssid.ssid_len;
1124+
1125+ ieee80211_freq_to_channel_ext(hapd->iface->freq,
1126+ hapd->iconf->secondary_channel,
1127+ hostapd_get_oper_chwidth(hapd->iconf),
1128+ &op_class, &channel);
1129+
1130+ blob_buf_init(&b, 0);
1131+ blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
1132+ blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
1133+
1134+ memset(ssid, 0, SSID_MAX_LEN + 1);
1135+ memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
1136+ blobmsg_add_string(&b, "ssid", ssid);
1137+
1138+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
1139+ blobmsg_add_u32(&b, "channel", channel);
1140+ blobmsg_add_u32(&b, "op_class", op_class);
1141+ blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
1142+#ifdef CONFIG_IEEE80211AX
1143+ blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
1144+ hapd->iface->conf->he_op.he_bss_color);
1145+#else
1146+ blobmsg_add_u32(&b, "bss_color", -1);
1147+#endif
1148+
1149+ snprintf(phy_name, 17, "%s", hapd->iface->phy);
1150+ blobmsg_add_string(&b, "phy", phy_name);
1151+
1152+ /* RRM */
1153+ rrm_table = blobmsg_open_table(&b, "rrm");
1154+ blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
1155+ blobmsg_close_table(&b, rrm_table);
1156+
1157+ /* WNM */
1158+ wnm_table = blobmsg_open_table(&b, "wnm");
1159+ blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
1160+ blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
1161+ blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
1162+ blobmsg_close_table(&b, wnm_table);
1163+
1164+ /* Airtime */
1165+ airtime_table = blobmsg_open_table(&b, "airtime");
1166+ blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
1167+ blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
1168+ blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
1169+ blobmsg_close_table(&b, airtime_table);
1170+
1171+ /* DFS */
1172+ dfs_table = blobmsg_open_table(&b, "dfs");
1173+ blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
1174+ blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
1175+ os_reltime_age(&hapd->iface->dfs_cac_start, &now);
1176+ blobmsg_add_u32(&b, "cac_seconds_left",
1177+ hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
1178+ blobmsg_close_table(&b, dfs_table);
1179+
1180+ ubus_send_reply(ctx, req, b.head);
1181+
1182+ return 0;
1183+}
1184+
1185+enum {
1186+ NOTIFY_RESPONSE,
1187+ __NOTIFY_MAX
1188+};
1189+
1190+static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
1191+ [NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
1192+};
1193+
1194+static int
1195+hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
1196+ struct ubus_request_data *req, const char *method,
1197+ struct blob_attr *msg)
1198+{
1199+ struct blob_attr *tb[__NOTIFY_MAX];
1200+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1201+ struct wpabuf *elems;
1202+ const char *pos;
1203+ size_t len;
1204+
1205+ blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
1206+ blob_data(msg), blob_len(msg));
1207+
1208+ if (!tb[NOTIFY_RESPONSE])
1209+ return UBUS_STATUS_INVALID_ARGUMENT;
1210+
1211+ hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
1212+
1213+ return UBUS_STATUS_OK;
1214+}
1215+
1216+enum {
1217+ DEL_CLIENT_ADDR,
1218+ DEL_CLIENT_REASON,
1219+ DEL_CLIENT_DEAUTH,
1220+ DEL_CLIENT_BAN_TIME,
1221+ __DEL_CLIENT_MAX
1222+};
1223+
1224+static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
1225+ [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
1226+ [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
1227+ [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
1228+ [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
1229+};
1230+
1231+static int
1232+hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
1233+ struct ubus_request_data *req, const char *method,
1234+ struct blob_attr *msg)
1235+{
1236+ struct blob_attr *tb[__DEL_CLIENT_MAX];
1237+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1238+ struct sta_info *sta;
1239+ bool deauth = false;
1240+ int reason;
1241+ u8 addr[ETH_ALEN];
1242+
1243+ blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
1244+
1245+ if (!tb[DEL_CLIENT_ADDR])
1246+ return UBUS_STATUS_INVALID_ARGUMENT;
1247+
1248+ if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
1249+ return UBUS_STATUS_INVALID_ARGUMENT;
1250+
1251+ if (tb[DEL_CLIENT_REASON])
1252+ reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
1253+
1254+ if (tb[DEL_CLIENT_DEAUTH])
1255+ deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
1256+
1257+ sta = ap_get_sta(hapd, addr);
1258+ if (sta) {
1259+ if (deauth) {
1260+ hostapd_drv_sta_deauth(hapd, addr, reason);
1261+ ap_sta_deauthenticate(hapd, sta, reason);
1262+ } else {
1263+ hostapd_drv_sta_disassoc(hapd, addr, reason);
1264+ ap_sta_disassociate(hapd, sta, reason);
1265+ }
1266+ }
1267+
1268+ if (tb[DEL_CLIENT_BAN_TIME])
1269+ hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
1270+
1271+ return 0;
1272+}
1273+
1274+static void
1275+blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
1276+{
1277+ char *s;
1278+
1279+ s = blobmsg_alloc_string_buffer(buf, name, 20);
1280+ sprintf(s, MACSTR, MAC2STR(addr));
1281+ blobmsg_add_string_buffer(buf);
1282+}
1283+
1284+static int
1285+hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
1286+ struct ubus_request_data *req, const char *method,
1287+ struct blob_attr *msg)
1288+{
1289+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1290+ struct ubus_banned_client *ban;
1291+ void *c;
1292+
1293+ blob_buf_init(&b, 0);
1294+ c = blobmsg_open_array(&b, "clients");
1295+ avl_for_each_element(&hapd->ubus.banned, ban, avl)
1296+ blobmsg_add_macaddr(&b, NULL, ban->addr);
1297+ blobmsg_close_array(&b, c);
1298+ ubus_send_reply(ctx, req, b.head);
1299+
1300+ return 0;
1301+}
1302+
1303+#ifdef CONFIG_WPS
1304+static int
1305+hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
1306+ struct ubus_request_data *req, const char *method,
1307+ struct blob_attr *msg)
1308+{
1309+ int rc;
1310+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1311+
1312+ rc = hostapd_wps_button_pushed(hapd, NULL);
1313+
1314+ if (rc != 0)
1315+ return UBUS_STATUS_NOT_SUPPORTED;
1316+
1317+ return 0;
1318+}
1319+
1320+
1321+static const char * pbc_status_enum_str(enum pbc_status status)
1322+{
1323+ switch (status) {
1324+ case WPS_PBC_STATUS_DISABLE:
1325+ return "Disabled";
1326+ case WPS_PBC_STATUS_ACTIVE:
1327+ return "Active";
1328+ case WPS_PBC_STATUS_TIMEOUT:
1329+ return "Timed-out";
1330+ case WPS_PBC_STATUS_OVERLAP:
1331+ return "Overlap";
1332+ default:
1333+ return "Unknown";
1334+ }
1335+}
1336+
1337+static int
1338+hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
1339+ struct ubus_request_data *req, const char *method,
1340+ struct blob_attr *msg)
1341+{
1342+ int rc;
1343+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1344+
1345+ blob_buf_init(&b, 0);
1346+
1347+ blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
1348+ blobmsg_add_string(&b, "last_wps_result",
1349+ (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
1350+ "Success":
1351+ (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
1352+ "Failed" : "None")));
1353+
1354+ /* If status == Failure - Add possible Reasons */
1355+ if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
1356+ hapd->wps_stats.failure_reason > 0)
1357+ blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
1358+
1359+ if (hapd->wps_stats.status)
1360+ blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
1361+
1362+ ubus_send_reply(ctx, req, b.head);
1363+
1364+ return 0;
1365+}
1366+
1367+static int
1368+hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
1369+ struct ubus_request_data *req, const char *method,
1370+ struct blob_attr *msg)
1371+{
1372+ int rc;
1373+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1374+
1375+ rc = hostapd_wps_cancel(hapd);
1376+
1377+ if (rc != 0)
1378+ return UBUS_STATUS_NOT_SUPPORTED;
1379+
1380+ return 0;
1381+}
1382+#endif /* CONFIG_WPS */
1383+
1384+static int
1385+hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
1386+ struct ubus_request_data *req, const char *method,
1387+ struct blob_attr *msg)
1388+{
1389+ int rc;
1390+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1391+
1392+ rc = ieee802_11_set_beacon(hapd);
1393+
1394+ if (rc != 0)
1395+ return UBUS_STATUS_NOT_SUPPORTED;
1396+
1397+ return 0;
1398+}
1399+
1400+enum {
1401+ CONFIG_IFACE,
1402+ CONFIG_FILE,
1403+ __CONFIG_MAX
1404+};
1405+
1406+enum {
1407+ CSA_FREQ,
1408+ CSA_BCN_COUNT,
1409+ CSA_CENTER_FREQ1,
1410+ CSA_CENTER_FREQ2,
1411+ CSA_BANDWIDTH,
1412+ CSA_SEC_CHANNEL_OFFSET,
1413+ CSA_HT,
1414+ CSA_VHT,
1415+ CSA_HE,
1416+ CSA_BLOCK_TX,
1417+ CSA_FORCE,
1418+ __CSA_MAX
1419+};
1420+
1421+static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
1422+ [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
1423+ [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
1424+ [CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
1425+ [CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
1426+ [CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
1427+ [CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
1428+ [CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
1429+ [CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
1430+ [CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
1431+ [CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
1432+ [CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
1433+};
1434+
1435+
1436+static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
1437+{
1438+ struct hostapd_iface *iface = eloop_data;
1439+ struct hostapd_freq_params *freq_params = user_ctx;
1440+
1441+ hostapd_switch_channel_fallback(iface, freq_params);
1442+}
1443+
1444+#ifdef NEED_AP_MLME
1445+static int
1446+hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
1447+ struct ubus_request_data *req, const char *method,
1448+ struct blob_attr *msg)
1449+{
1450+ struct blob_attr *tb[__CSA_MAX];
1451+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1452+ struct hostapd_config *iconf = hapd->iface->conf;
1453+ struct hostapd_freq_params *freq_params;
1454+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
1455+ struct csa_settings css = {
1456+ .freq_params = {
1457+ .ht_enabled = iconf->ieee80211n,
1458+ .vht_enabled = iconf->ieee80211ac,
1459+ .he_enabled = iconf->ieee80211ax,
1460+ .sec_channel_offset = iconf->secondary_channel,
1461+ }
1462+ };
1463+ u8 chwidth = hostapd_get_oper_chwidth(iconf);
1464+ u8 seg0 = 0, seg1 = 0;
1465+ int ret = UBUS_STATUS_OK;
1466+ int i;
1467+
1468+ blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
1469+
1470+ if (!tb[CSA_FREQ])
1471+ return UBUS_STATUS_INVALID_ARGUMENT;
1472+
1473+ switch (iconf->vht_oper_chwidth) {
1474+ case CHANWIDTH_USE_HT:
1475+ if (iconf->secondary_channel)
1476+ css.freq_params.bandwidth = 40;
1477+ else
1478+ css.freq_params.bandwidth = 20;
1479+ break;
1480+ case CHANWIDTH_160MHZ:
1481+ css.freq_params.bandwidth = 160;
1482+ break;
1483+ default:
1484+ css.freq_params.bandwidth = 80;
1485+ break;
1486+ }
1487+
1488+ css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
1489+
1490+#define SET_CSA_SETTING(name, field, type) \
1491+ do { \
1492+ if (tb[name]) \
1493+ css.field = blobmsg_get_ ## type(tb[name]); \
1494+ } while(0)
1495+
1496+ SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
1497+ SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
1498+ SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
1499+ SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
1500+ SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
1501+ SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
1502+ SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
1503+ SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
1504+ SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
1505+
1506+ css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
1507+ if (!css.freq_params.channel)
1508+ return UBUS_STATUS_NOT_SUPPORTED;
1509+
1510+ switch (css.freq_params.bandwidth) {
1511+ case 160:
1512+ chwidth = CHANWIDTH_160MHZ;
1513+ break;
1514+ case 80:
1515+ chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
1516+ break;
1517+ default:
1518+ chwidth = CHANWIDTH_USE_HT;
1519+ break;
1520+ }
1521+
1522+ hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
1523+ css.freq_params.freq,
1524+ css.freq_params.channel, iconf->enable_edmg,
1525+ iconf->edmg_channel,
1526+ css.freq_params.ht_enabled,
1527+ css.freq_params.vht_enabled,
1528+ css.freq_params.he_enabled,
1529+ css.freq_params.eht_enabled,
1530+ css.freq_params.sec_channel_offset,
1531+ chwidth, seg0, seg1,
1532+ iconf->vht_capab,
1533+ mode ? &mode->he_capab[IEEE80211_MODE_AP] :
1534+ NULL,
1535+ mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
1536+ NULL, hostapd_get_punct_bitmap(hapd));
1537+
1538+ for (i = 0; i < hapd->iface->num_bss; i++) {
1539+ struct hostapd_data *bss = hapd->iface->bss[i];
1540+
1541+ if (hostapd_switch_channel(bss, &css) != 0)
1542+ ret = UBUS_STATUS_NOT_SUPPORTED;
1543+ }
1544+
1545+ if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
1546+ return ret;
1547+
1548+ freq_params = malloc(sizeof(*freq_params));
1549+ memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
1550+ eloop_register_timeout(0, 1, switch_chan_fallback_cb,
1551+ hapd->iface, freq_params);
1552+
1553+ return 0;
1554+#undef SET_CSA_SETTING
1555+}
1556+#endif
1557+
1558+enum {
1559+ VENDOR_ELEMENTS,
1560+ __VENDOR_ELEMENTS_MAX
1561+};
1562+
1563+static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
1564+ /* vendor elements are provided as hex-string */
1565+ [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
1566+};
1567+
1568+static int
1569+hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
1570+ struct ubus_request_data *req, const char *method,
1571+ struct blob_attr *msg)
1572+{
1573+ struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
1574+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1575+ struct hostapd_bss_config *bss = hapd->conf;
1576+ struct wpabuf *elems;
1577+ const char *pos;
1578+ size_t len;
1579+
1580+ blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
1581+ blob_data(msg), blob_len(msg));
1582+
1583+ if (!tb[VENDOR_ELEMENTS])
1584+ return UBUS_STATUS_INVALID_ARGUMENT;
1585+
1586+ pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
1587+ len = os_strlen(pos);
1588+ if (len & 0x01)
1589+ return UBUS_STATUS_INVALID_ARGUMENT;
1590+
1591+ len /= 2;
1592+ if (len == 0) {
1593+ wpabuf_free(bss->vendor_elements);
1594+ bss->vendor_elements = NULL;
1595+ return 0;
1596+ }
1597+
1598+ elems = wpabuf_alloc(len);
1599+ if (elems == NULL)
1600+ return 1;
1601+
1602+ if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
1603+ wpabuf_free(elems);
1604+ return UBUS_STATUS_INVALID_ARGUMENT;
1605+ }
1606+
1607+ wpabuf_free(bss->vendor_elements);
1608+ bss->vendor_elements = elems;
1609+
1610+ /* update beacons if vendor elements were set successfully */
1611+ if (ieee802_11_update_beacons(hapd->iface) != 0)
1612+ return UBUS_STATUS_NOT_SUPPORTED;
1613+ return UBUS_STATUS_OK;
1614+}
1615+
1616+static void
1617+hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
1618+{
1619+ const u8 *data;
1620+ char *str;
1621+ int len;
1622+
1623+ blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
1624+
1625+ str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
1626+ memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
1627+ str[nr->ssid.ssid_len] = 0;
1628+ blobmsg_add_string_buffer(&b);
1629+
1630+ len = wpabuf_len(nr->nr);
1631+ str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
1632+ wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
1633+ blobmsg_add_string_buffer(&b);
1634+}
1635+
1636+enum {
1637+ BSS_MGMT_EN_NEIGHBOR,
1638+ BSS_MGMT_EN_BEACON,
1639+ BSS_MGMT_EN_LINK_MEASUREMENT,
1640+#ifdef CONFIG_WNM_AP
1641+ BSS_MGMT_EN_BSS_TRANSITION,
1642+#endif
1643+ __BSS_MGMT_EN_MAX
1644+};
1645+
1646+static bool
1647+__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
1648+{
1649+ struct hostapd_bss_config *bss = hapd->conf;
1650+ uint32_t flags;
1651+
1652+ switch (flag) {
1653+ case BSS_MGMT_EN_NEIGHBOR:
1654+ if (bss->radio_measurements[0] &
1655+ WLAN_RRM_CAPS_NEIGHBOR_REPORT)
1656+ return false;
1657+
1658+ bss->radio_measurements[0] |=
1659+ WLAN_RRM_CAPS_NEIGHBOR_REPORT;
1660+ hostapd_neighbor_set_own_report(hapd);
1661+ return true;
1662+ case BSS_MGMT_EN_BEACON:
1663+ flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
1664+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
1665+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
1666+
1667+ if (bss->radio_measurements[0] & flags == flags)
1668+ return false;
1669+
1670+ bss->radio_measurements[0] |= (u8) flags;
1671+ return true;
1672+ case BSS_MGMT_EN_LINK_MEASUREMENT:
1673+ flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
1674+
1675+ if (bss->radio_measurements[0] & flags == flags)
1676+ return false;
1677+
1678+ bss->radio_measurements[0] |= (u8) flags;
1679+ return true;
1680+#ifdef CONFIG_WNM_AP
1681+ case BSS_MGMT_EN_BSS_TRANSITION:
1682+ if (bss->bss_transition)
1683+ return false;
1684+
1685+ bss->bss_transition = 1;
1686+ return true;
1687+#endif
1688+ }
1689+}
1690+
1691+static void
1692+__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
1693+{
1694+ bool update = false;
1695+ int i;
1696+
1697+ for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
1698+ if (!(flags & (1 << i)))
1699+ continue;
1700+
1701+ update |= __hostapd_bss_mgmt_enable_f(hapd, i);
1702+ }
1703+
1704+ if (update)
1705+ ieee802_11_update_beacons(hapd->iface);
1706+}
1707+
1708+
1709+static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
1710+ [BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
1711+ [BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
1712+ [BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
1713+#ifdef CONFIG_WNM_AP
1714+ [BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
1715+#endif
1716+};
1717+
1718+static int
1719+hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
1720+ struct ubus_request_data *req, const char *method,
1721+ struct blob_attr *msg)
1722+
1723+{
1724+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1725+ struct blob_attr *tb[__BSS_MGMT_EN_MAX];
1726+ struct blob_attr *cur;
1727+ uint32_t flags = 0;
1728+ int i;
1729+ bool neigh = false, beacon = false;
1730+
1731+ blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
1732+
1733+ for (i = 0; i < ARRAY_SIZE(tb); i++) {
1734+ if (!tb[i] || !blobmsg_get_bool(tb[i]))
1735+ continue;
1736+
1737+ flags |= (1 << i);
1738+ }
1739+
1740+ __hostapd_bss_mgmt_enable(hapd, flags);
1741+
1742+ return 0;
1743+}
1744+
1745+
1746+static void
1747+hostapd_rrm_nr_enable(struct hostapd_data *hapd)
1748+{
1749+ __hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
1750+}
1751+
1752+static int
1753+hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
1754+ struct ubus_request_data *req, const char *method,
1755+ struct blob_attr *msg)
1756+{
1757+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1758+ struct hostapd_neighbor_entry *nr;
1759+ void *c;
1760+
1761+ hostapd_rrm_nr_enable(hapd);
1762+
1763+ nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
1764+ if (!nr)
1765+ return UBUS_STATUS_NOT_FOUND;
1766+
1767+ blob_buf_init(&b, 0);
1768+
1769+ c = blobmsg_open_array(&b, "value");
1770+ hostapd_rrm_print_nr(nr);
1771+ blobmsg_close_array(&b, c);
1772+
1773+ ubus_send_reply(ctx, req, b.head);
1774+
1775+ return 0;
1776+}
1777+
1778+static int
1779+hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
1780+ struct ubus_request_data *req, const char *method,
1781+ struct blob_attr *msg)
1782+{
1783+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1784+ struct hostapd_neighbor_entry *nr;
1785+ void *c;
1786+
1787+ hostapd_rrm_nr_enable(hapd);
1788+ blob_buf_init(&b, 0);
1789+
1790+ c = blobmsg_open_array(&b, "list");
1791+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
1792+ void *cur;
1793+
1794+ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
1795+ continue;
1796+
1797+ cur = blobmsg_open_array(&b, NULL);
1798+ hostapd_rrm_print_nr(nr);
1799+ blobmsg_close_array(&b, cur);
1800+ }
1801+ blobmsg_close_array(&b, c);
1802+
1803+ ubus_send_reply(ctx, req, b.head);
1804+
1805+ return 0;
1806+}
1807+
1808+enum {
1809+ NR_SET_LIST,
1810+ __NR_SET_LIST_MAX
1811+};
1812+
1813+static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
1814+ [NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
1815+};
1816+
1817+
1818+static void
1819+hostapd_rrm_nr_clear(struct hostapd_data *hapd)
1820+{
1821+ struct hostapd_neighbor_entry *nr;
1822+
1823+restart:
1824+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
1825+ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
1826+ continue;
1827+
1828+ hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
1829+ goto restart;
1830+ }
1831+}
1832+
1833+static int
1834+hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
1835+ struct ubus_request_data *req, const char *method,
1836+ struct blob_attr *msg)
1837+{
1838+ static const struct blobmsg_policy nr_e_policy[] = {
1839+ { .type = BLOBMSG_TYPE_STRING },
1840+ { .type = BLOBMSG_TYPE_STRING },
1841+ { .type = BLOBMSG_TYPE_STRING },
1842+ };
1843+ struct hostapd_data *hapd = get_hapd_from_object(obj);
1844+ struct blob_attr *tb_l[__NR_SET_LIST_MAX];
1845+ struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
1846+ struct blob_attr *cur;
1847+ int rem;
1848+
1849+ hostapd_rrm_nr_enable(hapd);
1850+
1851+ blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
1852+ if (!tb_l[NR_SET_LIST])
1853+ return UBUS_STATUS_INVALID_ARGUMENT;
1854+
1855+ hostapd_rrm_nr_clear(hapd);
1856+ blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
1857+ struct wpa_ssid_value ssid;
1858+ struct wpabuf *data;
1859+ u8 bssid[ETH_ALEN];
1860+ char *s, *nr_s;
1861+
1862+ blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
1863+ if (!tb[0] || !tb[1] || !tb[2])
1864+ goto invalid;
1865+
1866+ /* Neighbor Report binary */
1867+ nr_s = blobmsg_get_string(tb[2]);
1868+ data = wpabuf_parse_bin(nr_s);
1869+ if (!data)
1870+ goto invalid;
1871+
1872+ /* BSSID */
1873+ s = blobmsg_get_string(tb[0]);
1874+ if (strlen(s) == 0) {
1875+ /* Copy BSSID from neighbor report */
1876+ if (hwaddr_compact_aton(nr_s, bssid))
1877+ goto invalid;
1878+ } else if (hwaddr_aton(s, bssid)) {
1879+ goto invalid;
1880+ }
1881+
1882+ /* SSID */
1883+ s = blobmsg_get_string(tb[1]);
1884+ if (strlen(s) == 0) {
1885+ /* Copy SSID from hostapd BSS conf */
1886+ memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
1887+ } else {
1888+ ssid.ssid_len = strlen(s);
1889+ if (ssid.ssid_len > sizeof(ssid.ssid))
1890+ goto invalid;
1891+
1892+ memcpy(&ssid, s, ssid.ssid_len);
1893+ }
1894+
1895+ hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
1896+ wpabuf_free(data);
1897+ continue;
1898+
1899+invalid:
1900+ return UBUS_STATUS_INVALID_ARGUMENT;
1901+ }
1902+
1903+ return 0;
1904+}
1905+
1906+enum {
1907+ BEACON_REQ_ADDR,
1908+ BEACON_REQ_MODE,
1909+ BEACON_REQ_OP_CLASS,
1910+ BEACON_REQ_CHANNEL,
1911+ BEACON_REQ_DURATION,
1912+ BEACON_REQ_BSSID,
1913+ BEACON_REQ_SSID,
1914+ __BEACON_REQ_MAX,
1915+};
1916+
1917+static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
1918+ [BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
1919+ [BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
1920+ [BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
1921+ [BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
1922+ [BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
1923+ [BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
1924+ [BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
1925+};
1926+
1927+static int
1928+hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
1929+ struct ubus_request_data *ureq, const char *method,
1930+ struct blob_attr *msg)
1931+{
1932+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
1933+ struct blob_attr *tb[__BEACON_REQ_MAX];
1934+ struct blob_attr *cur;
1935+ struct wpabuf *req;
1936+ u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1937+ u8 addr[ETH_ALEN];
1938+ int mode, rem, ret;
1939+ int buf_len = 13;
1940+
1941+ blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
1942+
1943+ if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
1944+ !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
1945+ return UBUS_STATUS_INVALID_ARGUMENT;
1946+
1947+ if (tb[BEACON_REQ_SSID])
1948+ buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
1949+
1950+ mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
1951+ if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
1952+ return UBUS_STATUS_INVALID_ARGUMENT;
1953+
1954+ if (tb[BEACON_REQ_BSSID] &&
1955+ hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
1956+ return UBUS_STATUS_INVALID_ARGUMENT;
1957+
1958+ req = wpabuf_alloc(buf_len);
1959+ if (!req)
1960+ return UBUS_STATUS_UNKNOWN_ERROR;
1961+
1962+ /* 1: regulatory class */
1963+ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
1964+
1965+ /* 2: channel number */
1966+ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
1967+
1968+ /* 3-4: randomization interval */
1969+ wpabuf_put_le16(req, 0);
1970+
1971+ /* 5-6: duration */
1972+ wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
1973+
1974+ /* 7: mode */
1975+ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
1976+
1977+ /* 8-13: BSSID */
1978+ wpabuf_put_data(req, bssid, ETH_ALEN);
1979+
1980+ if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
1981+ wpabuf_put_u8(req, WLAN_EID_SSID);
1982+ wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
1983+ wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
1984+ }
1985+
1986+ ret = hostapd_send_beacon_req(hapd, addr, 0, req);
1987+ if (ret < 0)
1988+ return -ret;
1989+
1990+ return 0;
1991+}
1992+
1993+enum {
1994+ LM_REQ_ADDR,
1995+ LM_REQ_TX_POWER_USED,
1996+ LM_REQ_TX_POWER_MAX,
1997+ __LM_REQ_MAX,
1998+};
1999+
2000+static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
2001+ [LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
2002+ [LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
2003+ [LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
2004+};
2005+
2006+static int
2007+hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
2008+ struct ubus_request_data *ureq, const char *method,
2009+ struct blob_attr *msg)
2010+{
2011+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
2012+ struct blob_attr *tb[__LM_REQ_MAX];
2013+ struct wpabuf *buf;
2014+ u8 addr[ETH_ALEN];
2015+ int ret;
2016+ int8_t txp_used, txp_max;
2017+
2018+ txp_used = 0;
2019+ txp_max = 0;
2020+
2021+ blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
2022+
2023+ if (!tb[LM_REQ_ADDR])
2024+ return UBUS_STATUS_INVALID_ARGUMENT;
2025+
2026+ if (tb[LM_REQ_TX_POWER_USED])
2027+ txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
2028+
2029+ if (tb[LM_REQ_TX_POWER_MAX])
2030+ txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
2031+
2032+ if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
2033+ return UBUS_STATUS_INVALID_ARGUMENT;
2034+
2035+ buf = wpabuf_alloc(5);
2036+ if (!buf)
2037+ return UBUS_STATUS_UNKNOWN_ERROR;
2038+
2039+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
2040+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
2041+ wpabuf_put_u8(buf, 1);
2042+ /* TX-Power used */
2043+ wpabuf_put_u8(buf, txp_used);
2044+ /* Max TX Power */
2045+ wpabuf_put_u8(buf, txp_max);
2046+
2047+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
2048+ wpabuf_head(buf), wpabuf_len(buf));
2049+
2050+ wpabuf_free(buf);
2051+ if (ret < 0)
2052+ return -ret;
2053+
2054+ return 0;
2055+}
2056+
2057+
2058+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
2059+{
2060+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
2061+ const u8 *pos, *end;
2062+ u8 token;
2063+
2064+ end = data + len;
2065+ token = mgmt->u.action.u.rrm.dialog_token;
2066+ pos = mgmt->u.action.u.rrm.variable;
2067+
2068+ if (end - pos < 8)
2069+ return;
2070+
2071+ if (!hapd->ubus.obj.has_subscribers)
2072+ return;
2073+
2074+ blob_buf_init(&b, 0);
2075+ blobmsg_add_macaddr(&b, "address", mgmt->sa);
2076+ blobmsg_add_u16(&b, "dialog-token", token);
2077+ blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
2078+ blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
2079+ blobmsg_add_u16(&b, "rcpi", pos[6]);
2080+ blobmsg_add_u16(&b, "rsni", pos[7]);
2081+
2082+ ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
2083+}
2084+
2085+
2086+#ifdef CONFIG_WNM_AP
2087+
2088+static int
2089+hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
2090+ u16 disassoc_timer, u8 validity_period, u8 dialog_token,
2091+ struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
2092+{
2093+ struct blob_attr *cur;
2094+ struct sta_info *sta;
2095+ int nr_len = 0;
2096+ int rem;
2097+ u8 *nr = NULL;
2098+ u8 req_mode = 0;
2099+ u8 mbo[10];
2100+ size_t mbo_len = 0;
2101+
2102+ sta = ap_get_sta(hapd, addr);
2103+ if (!sta)
2104+ return UBUS_STATUS_NOT_FOUND;
2105+
2106+ if (neighbors) {
2107+ u8 *nr_cur;
2108+
2109+ if (blobmsg_check_array(neighbors,
2110+ BLOBMSG_TYPE_STRING) < 0)
2111+ return UBUS_STATUS_INVALID_ARGUMENT;
2112+
2113+ blobmsg_for_each_attr(cur, neighbors, rem) {
2114+ int len = strlen(blobmsg_get_string(cur));
2115+
2116+ if (len % 2)
2117+ return UBUS_STATUS_INVALID_ARGUMENT;
2118+
2119+ nr_len += (len / 2) + 2;
2120+ }
2121+
2122+ if (nr_len) {
2123+ nr = os_zalloc(nr_len);
2124+ if (!nr)
2125+ return UBUS_STATUS_UNKNOWN_ERROR;
2126+ }
2127+
2128+ nr_cur = nr;
2129+ blobmsg_for_each_attr(cur, neighbors, rem) {
2130+ int len = strlen(blobmsg_get_string(cur)) / 2;
2131+
2132+ *nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
2133+ *nr_cur++ = (u8) len;
2134+ if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
2135+ free(nr);
2136+ return UBUS_STATUS_INVALID_ARGUMENT;
2137+ }
2138+
2139+ nr_cur += len;
2140+ }
2141+ }
2142+
2143+ if (nr)
2144+ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
2145+
2146+ if (abridged)
2147+ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
2148+
2149+ if (disassoc_imminent)
2150+ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
2151+
2152+#ifdef CONFIG_MBO
2153+ u8 *mbo_pos = mbo;
2154+
2155+ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
2156+ return UBUS_STATUS_INVALID_ARGUMENT;
2157+
2158+ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
2159+ return UBUS_STATUS_INVALID_ARGUMENT;
2160+
2161+ if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
2162+ return UBUS_STATUS_INVALID_ARGUMENT;
2163+
2164+ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
2165+ *mbo_pos++ = 1;
2166+ *mbo_pos++ = mbo_reason;
2167+ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
2168+ *mbo_pos++ = 1;
2169+ *mbo_pos++ = cell_pref;
2170+
2171+ if (reassoc_delay) {
2172+ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
2173+ *mbo_pos++ = 2;
2174+ WPA_PUT_LE16(mbo_pos, reassoc_delay);
2175+ mbo_pos += 2;
2176+ }
2177+
2178+ mbo_len = mbo_pos - mbo;
2179+#endif
2180+
2181+ if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
2182+ dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
2183+ return UBUS_STATUS_UNKNOWN_ERROR;
2184+
2185+ return 0;
2186+}
2187+
2188+enum {
2189+ BSS_TR_ADDR,
2190+ BSS_TR_DA_IMMINENT,
2191+ BSS_TR_DA_TIMER,
2192+ BSS_TR_VALID_PERIOD,
2193+ BSS_TR_NEIGHBORS,
2194+ BSS_TR_ABRIDGED,
2195+ BSS_TR_DIALOG_TOKEN,
2196+#ifdef CONFIG_MBO
2197+ BSS_TR_MBO_REASON,
2198+ BSS_TR_CELL_PREF,
2199+ BSS_TR_REASSOC_DELAY,
2200+#endif
2201+ __BSS_TR_DISASSOC_MAX
2202+};
2203+
2204+static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
2205+ [BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
2206+ [BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
2207+ [BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
2208+ [BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
2209+ [BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
2210+ [BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
2211+ [BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
2212+#ifdef CONFIG_MBO
2213+ [BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
2214+ [BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
2215+ [BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
2216+#endif
2217+};
2218+
2219+static int
2220+hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
2221+ struct ubus_request_data *ureq, const char *method,
2222+ struct blob_attr *msg)
2223+{
2224+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
2225+ struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
2226+ struct sta_info *sta;
2227+ u32 da_timer = 0;
2228+ u32 valid_period = 0;
2229+ u8 addr[ETH_ALEN];
2230+ u32 dialog_token = 1;
2231+ bool abridged;
2232+ bool da_imminent;
2233+ u8 mbo_reason;
2234+ u8 cell_pref;
2235+ u8 reassoc_delay;
2236+
2237+ blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
2238+
2239+ if (!tb[BSS_TR_ADDR])
2240+ return UBUS_STATUS_INVALID_ARGUMENT;
2241+
2242+ if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
2243+ return UBUS_STATUS_INVALID_ARGUMENT;
2244+
2245+ if (tb[BSS_TR_DA_TIMER])
2246+ da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
2247+
2248+ if (tb[BSS_TR_VALID_PERIOD])
2249+ valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
2250+
2251+ if (tb[BSS_TR_DIALOG_TOKEN])
2252+ dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
2253+
2254+ da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
2255+ abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
2256+
2257+#ifdef CONFIG_MBO
2258+ if (tb[BSS_TR_MBO_REASON])
2259+ mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
2260+
2261+ if (tb[BSS_TR_CELL_PREF])
2262+ cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
2263+
2264+ if (tb[BSS_TR_REASSOC_DELAY])
2265+ reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
2266+#endif
2267+
2268+ return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
2269+ dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
2270+}
2271+#endif
2272+
2273+#ifdef CONFIG_AIRTIME_POLICY
2274+enum {
2275+ UPDATE_AIRTIME_STA,
2276+ UPDATE_AIRTIME_WEIGHT,
2277+ __UPDATE_AIRTIME_MAX,
2278+};
2279+
2280+
2281+static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
2282+ [UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
2283+ [UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
2284+};
2285+
2286+static int
2287+hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
2288+ struct ubus_request_data *ureq, const char *method,
2289+ struct blob_attr *msg)
2290+{
2291+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
2292+ struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
2293+ struct sta_info *sta = NULL;
2294+ u8 addr[ETH_ALEN];
2295+ int weight;
2296+
2297+ blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
2298+
2299+ if (!tb[UPDATE_AIRTIME_WEIGHT])
2300+ return UBUS_STATUS_INVALID_ARGUMENT;
2301+
2302+ weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
2303+
2304+ if (!tb[UPDATE_AIRTIME_STA]) {
2305+ if (!weight)
2306+ return UBUS_STATUS_INVALID_ARGUMENT;
2307+
2308+ hapd->conf->airtime_weight = weight;
2309+ return 0;
2310+ }
2311+
2312+ if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
2313+ return UBUS_STATUS_INVALID_ARGUMENT;
2314+
2315+ sta = ap_get_sta(hapd, addr);
2316+ if (!sta)
2317+ return UBUS_STATUS_NOT_FOUND;
2318+
2319+ sta->dyn_airtime_weight = weight;
2320+ airtime_policy_new_sta(hapd, sta);
2321+
2322+ return 0;
2323+}
2324+#endif
2325+
2326+#ifdef CONFIG_TAXONOMY
2327+static const struct blobmsg_policy addr_policy[] = {
2328+ { "address", BLOBMSG_TYPE_STRING }
2329+};
2330+
2331+static bool
2332+hostapd_add_b64_data(const char *name, const struct wpabuf *buf)
2333+{
2334+ char *str;
2335+
2336+ if (!buf)
2337+ return false;
2338+
2339+ str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf)));
2340+ b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf)));
2341+ blobmsg_add_string_buffer(&b);
2342+
2343+ return true;
2344+}
2345+
2346+static int
2347+hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj,
2348+ struct ubus_request_data *req, const char *method,
2349+ struct blob_attr *msg)
2350+{
2351+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
2352+ struct blob_attr *tb;
2353+ struct sta_info *sta;
2354+ u8 addr[ETH_ALEN];
2355+
2356+ blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
2357+
2358+ if (!tb || hwaddr_aton(blobmsg_data(tb), addr))
2359+ return UBUS_STATUS_INVALID_ARGUMENT;
2360+
2361+ sta = ap_get_sta(hapd, addr);
2362+ if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy))
2363+ return UBUS_STATUS_NOT_FOUND;
2364+
2365+ blob_buf_init(&b, 0);
2366+ hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy);
2367+ hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy);
2368+ ubus_send_reply(ctx, req, b.head);
2369+
2370+ return 0;
2371+}
2372+#endif
2373+
2374+
2375+static const struct ubus_method bss_methods[] = {
2376+ UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
2377+ UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
2378+#ifdef CONFIG_TAXONOMY
2379+ UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy),
2380+#endif
2381+ UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
2382+ UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
2383+#ifdef CONFIG_AIRTIME_POLICY
2384+ UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
2385+#endif
2386+ UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
2387+#ifdef CONFIG_WPS
2388+ UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
2389+ UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
2390+ UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
2391+#endif
2392+ UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
2393+ UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
2394+#ifdef NEED_AP_MLME
2395+ UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
2396+#endif
2397+ UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
2398+ UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
2399+ UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
2400+ UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
2401+ UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
2402+ UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
2403+ UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
2404+ UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
2405+#ifdef CONFIG_WNM_AP
2406+ UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
2407+#endif
2408+};
2409+
2410+static struct ubus_object_type bss_object_type =
2411+ UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
2412+
2413+static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
2414+{
2415+ return memcmp(k1, k2, ETH_ALEN);
2416+}
2417+
2418+void hostapd_ubus_add_bss(struct hostapd_data *hapd)
2419+{
2420+ struct ubus_object *obj = &hapd->ubus.obj;
2421+ char *name;
2422+ int ret;
2423+
2424+#ifdef CONFIG_MESH
2425+ if (hapd->conf->mesh & MESH_ENABLED)
2426+ return;
2427+#endif
2428+
2429+ if (!hostapd_ubus_init())
2430+ return;
2431+
2432+ if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
2433+ return;
2434+
2435+ avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
2436+ obj->name = name;
2437+ obj->type = &bss_object_type;
2438+ obj->methods = bss_object_type.methods;
2439+ obj->n_methods = bss_object_type.n_methods;
2440+ ret = ubus_add_object(ctx, obj);
2441+ hostapd_ubus_ref_inc();
2442+}
2443+
2444+void hostapd_ubus_free_bss(struct hostapd_data *hapd)
2445+{
2446+ struct ubus_object *obj = &hapd->ubus.obj;
2447+ char *name = (char *) obj->name;
2448+
2449+#ifdef CONFIG_MESH
2450+ if (hapd->conf->mesh & MESH_ENABLED)
2451+ return;
2452+#endif
2453+
2454+ if (!ctx)
2455+ return;
2456+
2457+ if (obj->id) {
2458+ ubus_remove_object(ctx, obj);
2459+ hostapd_ubus_ref_dec();
2460+ }
2461+
2462+ free(name);
2463+}
2464+
2465+static void
2466+hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
2467+ const char *action)
2468+{
2469+ struct vlan_description *desc = &vlan->vlan_desc;
2470+ void *c;
2471+ int i;
2472+
2473+ if (!hapd->ubus.obj.has_subscribers)
2474+ return;
2475+
2476+ blob_buf_init(&b, 0);
2477+ blobmsg_add_string(&b, "ifname", vlan->ifname);
2478+ blobmsg_add_string(&b, "bridge", vlan->bridge);
2479+ blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
2480+
2481+ if (desc->notempty) {
2482+ blobmsg_add_u32(&b, "untagged", desc->untagged);
2483+ c = blobmsg_open_array(&b, "tagged");
2484+ for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
2485+ blobmsg_add_u32(&b, "", desc->tagged[i]);
2486+ blobmsg_close_array(&b, c);
2487+ }
2488+
2489+ ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
2490+}
2491+
2492+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
2493+{
2494+ hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
2495+}
2496+
2497+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
2498+{
2499+ hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
2500+}
2501+
2502+struct ubus_event_req {
2503+ struct ubus_notify_request nreq;
2504+ int resp;
2505+};
2506+
2507+static void
2508+ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
2509+{
2510+ struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
2511+
2512+ ureq->resp = ret;
2513+}
2514+
2515+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
2516+{
2517+ struct ubus_banned_client *ban;
2518+ const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
2519+ [HOSTAPD_UBUS_PROBE_REQ] = "probe",
2520+ [HOSTAPD_UBUS_AUTH_REQ] = "auth",
2521+ [HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
2522+ };
2523+ const char *type = "mgmt";
2524+ struct ubus_event_req ureq = {};
2525+ const u8 *addr;
2526+
2527+ if (req->mgmt_frame)
2528+ addr = req->mgmt_frame->sa;
2529+ else
2530+ addr = req->addr;
2531+
2532+ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
2533+ if (ban)
2534+ return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
2535+
2536+ if (!hapd->ubus.obj.has_subscribers)
2537+ return WLAN_STATUS_SUCCESS;
2538+
2539+ if (req->type < ARRAY_SIZE(types))
2540+ type = types[req->type];
2541+
2542+ blob_buf_init(&b, 0);
2543+ blobmsg_add_macaddr(&b, "address", addr);
2544+ if (req->mgmt_frame)
2545+ blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
2546+ if (req->ssi_signal)
2547+ blobmsg_add_u32(&b, "signal", req->ssi_signal);
2548+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
2549+
2550+ if (req->elems) {
2551+ if(req->elems->ht_capabilities)
2552+ {
2553+ struct ieee80211_ht_capabilities *ht_capabilities;
2554+ void *ht_cap, *ht_cap_mcs_set, *mcs_set;
2555+
2556+
2557+ ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
2558+ ht_cap = blobmsg_open_table(&b, "ht_capabilities");
2559+ blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
2560+ ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
2561+ blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
2562+ blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
2563+ blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
2564+ blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
2565+ mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
2566+ for (int i = 0; i < 16; i++) {
2567+ blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
2568+ }
2569+ blobmsg_close_array(&b, mcs_set);
2570+ blobmsg_close_table(&b, ht_cap_mcs_set);
2571+ blobmsg_close_table(&b, ht_cap);
2572+ }
2573+ if(req->elems->vht_capabilities)
2574+ {
2575+ struct ieee80211_vht_capabilities *vht_capabilities;
2576+ void *vht_cap, *vht_cap_mcs_set;
2577+
2578+ vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
2579+ vht_cap = blobmsg_open_table(&b, "vht_capabilities");
2580+ blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
2581+ vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
2582+ blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
2583+ blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
2584+ blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
2585+ blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
2586+ blobmsg_close_table(&b, vht_cap_mcs_set);
2587+ blobmsg_close_table(&b, vht_cap);
2588+ }
2589+ }
2590+
2591+ if (!hapd->ubus.notify_response) {
2592+ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
2593+ return WLAN_STATUS_SUCCESS;
2594+ }
2595+
2596+ if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
2597+ return WLAN_STATUS_SUCCESS;
2598+
2599+ ureq.nreq.status_cb = ubus_event_cb;
2600+ ubus_complete_request(ctx, &ureq.nreq.req, 100);
2601+
2602+ if (ureq.resp)
2603+ return ureq.resp;
2604+
2605+ return WLAN_STATUS_SUCCESS;
2606+}
2607+
2608+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
2609+{
2610+ if (!hapd->ubus.obj.has_subscribers)
2611+ return;
2612+
2613+ if (!addr)
2614+ return;
2615+
2616+ blob_buf_init(&b, 0);
2617+ blobmsg_add_macaddr(&b, "address", addr);
2618+
2619+ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
2620+}
2621+
2622+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
2623+ const char *auth_alg)
2624+{
2625+ if (!hapd->ubus.obj.has_subscribers)
2626+ return;
2627+
2628+ blob_buf_init(&b, 0);
2629+ blobmsg_add_macaddr(&b, "address", sta->addr);
2630+ if (auth_alg)
2631+ blobmsg_add_string(&b, "auth-alg", auth_alg);
2632+
2633+ ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
2634+}
2635+
2636+void hostapd_ubus_notify_beacon_report(
2637+ struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
2638+ struct rrm_measurement_beacon_report *rep, size_t len)
2639+{
2640+ if (!hapd->ubus.obj.has_subscribers)
2641+ return;
2642+
2643+ if (!addr || !rep)
2644+ return;
2645+
2646+ blob_buf_init(&b, 0);
2647+ blobmsg_add_macaddr(&b, "address", addr);
2648+ blobmsg_add_u16(&b, "op-class", rep->op_class);
2649+ blobmsg_add_u16(&b, "channel", rep->channel);
2650+ blobmsg_add_u64(&b, "start-time", rep->start_time);
2651+ blobmsg_add_u16(&b, "duration", rep->duration);
2652+ blobmsg_add_u16(&b, "report-info", rep->report_info);
2653+ blobmsg_add_u16(&b, "rcpi", rep->rcpi);
2654+ blobmsg_add_u16(&b, "rsni", rep->rsni);
2655+ blobmsg_add_macaddr(&b, "bssid", rep->bssid);
2656+ blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
2657+ blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
2658+ blobmsg_add_u16(&b, "rep-mode", rep_mode);
2659+
2660+ ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
2661+}
2662+
2663+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
2664+ int chan_width, int cf1, int cf2)
2665+{
2666+ struct hostapd_data *hapd;
2667+ int i;
2668+
2669+ if (!ctx)
2670+ return;
2671+
2672+ blob_buf_init(&b, 0);
2673+ blobmsg_add_u16(&b, "frequency", frequency);
2674+ blobmsg_add_u16(&b, "width", chan_width);
2675+ blobmsg_add_u16(&b, "center1", cf1);
2676+ blobmsg_add_u16(&b, "center2", cf2);
2677+
2678+ for (i = 0; i < iface->num_bss; i++) {
2679+ hapd = iface->bss[i];
2680+ ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
2681+ }
2682+}
2683+
2684+#ifdef CONFIG_WNM_AP
2685+static void hostapd_ubus_notify_bss_transition_add_candidate_list(
2686+ const u8 *candidate_list, u16 candidate_list_len)
2687+{
2688+ char *cl_str;
2689+ int i;
2690+
2691+ if (candidate_list_len == 0)
2692+ return;
2693+
2694+ cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
2695+ for (i = 0; i < candidate_list_len; i++)
2696+ snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
2697+ blobmsg_add_string_buffer(&b);
2698+
2699+}
2700+#endif
2701+
2702+void hostapd_ubus_notify_bss_transition_response(
2703+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
2704+ u8 bss_termination_delay, const u8 *target_bssid,
2705+ const u8 *candidate_list, u16 candidate_list_len)
2706+{
2707+#ifdef CONFIG_WNM_AP
2708+ u16 i;
2709+
2710+ if (!hapd->ubus.obj.has_subscribers)
2711+ return;
2712+
2713+ if (!addr)
2714+ return;
2715+
2716+ blob_buf_init(&b, 0);
2717+ blobmsg_add_macaddr(&b, "address", addr);
2718+ blobmsg_add_u8(&b, "dialog-token", dialog_token);
2719+ blobmsg_add_u8(&b, "status-code", status_code);
2720+ blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
2721+ if (target_bssid)
2722+ blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
2723+
2724+ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
2725+
2726+ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
2727+#endif
2728+}
2729+
2730+int hostapd_ubus_notify_bss_transition_query(
2731+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
2732+ const u8 *candidate_list, u16 candidate_list_len)
2733+{
2734+#ifdef CONFIG_WNM_AP
2735+ struct ubus_event_req ureq = {};
2736+ char *cl_str;
2737+ u16 i;
2738+
2739+ if (!hapd->ubus.obj.has_subscribers)
2740+ return 0;
2741+
2742+ if (!addr)
2743+ return 0;
2744+
2745+ blob_buf_init(&b, 0);
2746+ blobmsg_add_macaddr(&b, "address", addr);
2747+ blobmsg_add_u8(&b, "dialog-token", dialog_token);
2748+ blobmsg_add_u8(&b, "reason", reason);
2749+ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
2750+
2751+ if (!hapd->ubus.notify_response) {
2752+ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
2753+ return 0;
2754+ }
2755+
2756+ if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
2757+ return 0;
2758+
2759+ ureq.nreq.status_cb = ubus_event_cb;
2760+ ubus_complete_request(ctx, &ureq.nreq.req, 100);
2761+
2762+ return ureq.resp;
2763+#endif
2764+}
2765diff --git a/src/ap/ubus.h b/src/ap/ubus.h
2766new file mode 100644
2767index 000000000..b0f7c44ab
2768--- /dev/null
2769+++ b/src/ap/ubus.h
2770@@ -0,0 +1,154 @@
2771+/*
2772+ * hostapd / ubus support
2773+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
2774+ *
2775+ * This software may be distributed under the terms of the BSD license.
2776+ * See README for more details.
2777+ */
2778+#ifndef __HOSTAPD_UBUS_H
2779+#define __HOSTAPD_UBUS_H
2780+
2781+enum hostapd_ubus_event_type {
2782+ HOSTAPD_UBUS_PROBE_REQ,
2783+ HOSTAPD_UBUS_AUTH_REQ,
2784+ HOSTAPD_UBUS_ASSOC_REQ,
2785+ HOSTAPD_UBUS_TYPE_MAX
2786+};
2787+
2788+struct hostapd_ubus_request {
2789+ enum hostapd_ubus_event_type type;
2790+ const struct ieee80211_mgmt *mgmt_frame;
2791+ const struct ieee802_11_elems *elems;
2792+ int ssi_signal; /* dBm */
2793+ const u8 *addr;
2794+};
2795+
2796+struct hostapd_iface;
2797+struct hostapd_data;
2798+struct hapd_interfaces;
2799+struct rrm_measurement_beacon_report;
2800+
2801+#ifdef UBUS_SUPPORT
2802+
2803+#include <libubox/avl.h>
2804+#include <libubus.h>
2805+
2806+struct hostapd_ubus_bss {
2807+ struct ubus_object obj;
2808+ struct avl_tree banned;
2809+ int notify_response;
2810+};
2811+
2812+void hostapd_ubus_add_iface(struct hostapd_iface *iface);
2813+void hostapd_ubus_free_iface(struct hostapd_iface *iface);
2814+void hostapd_ubus_add_bss(struct hostapd_data *hapd);
2815+void hostapd_ubus_free_bss(struct hostapd_data *hapd);
2816+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
2817+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
2818+
2819+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
2820+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
2821+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
2822+void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
2823+ const u8 *addr, u8 token, u8 rep_mode,
2824+ struct rrm_measurement_beacon_report *rep,
2825+ size_t len);
2826+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
2827+ int chan_width, int cf1, int cf2);
2828+
2829+void hostapd_ubus_notify_bss_transition_response(
2830+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
2831+ u8 bss_termination_delay, const u8 *target_bssid,
2832+ const u8 *candidate_list, u16 candidate_list_len);
2833+void hostapd_ubus_add(struct hapd_interfaces *interfaces);
2834+void hostapd_ubus_free(struct hapd_interfaces *interfaces);
2835+int hostapd_ubus_notify_bss_transition_query(
2836+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
2837+ const u8 *candidate_list, u16 candidate_list_len);
2838+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
2839+ const char *auth_alg);
2840+
2841+#else
2842+
2843+struct hostapd_ubus_bss {};
2844+
2845+static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
2846+{
2847+}
2848+
2849+static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
2850+{
2851+}
2852+
2853+static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
2854+{
2855+}
2856+
2857+static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
2858+{
2859+}
2860+
2861+static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
2862+{
2863+}
2864+
2865+static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
2866+{
2867+}
2868+
2869+static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
2870+{
2871+ return 0;
2872+}
2873+
2874+static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
2875+{
2876+}
2877+
2878+static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
2879+{
2880+}
2881+
2882+static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
2883+ const u8 *addr, u8 token,
2884+ u8 rep_mode,
2885+ struct rrm_measurement_beacon_report *rep,
2886+ size_t len)
2887+{
2888+}
2889+static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
2890+ int chan_width, int cf1, int cf2)
2891+{
2892+}
2893+
2894+static inline void hostapd_ubus_notify_bss_transition_response(
2895+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
2896+ u8 bss_termination_delay, const u8 *target_bssid,
2897+ const u8 *candidate_list, u16 candidate_list_len)
2898+{
2899+}
2900+
2901+static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
2902+{
2903+}
2904+
2905+static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
2906+{
2907+}
2908+
2909+static inline int hostapd_ubus_notify_bss_transition_query(
2910+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
2911+ const u8 *candidate_list, u16 candidate_list_len)
2912+{
2913+ return 0;
2914+}
2915+
2916+static inline void
2917+hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
2918+ const char *auth_alg)
2919+{
2920+}
2921+
2922+#endif
2923+
2924+#endif
2925diff --git a/src/ap/ucode.c b/src/ap/ucode.c
2926new file mode 100644
2927index 000000000..16d1b5153
2928--- /dev/null
2929+++ b/src/ap/ucode.c
2930@@ -0,0 +1,813 @@
2931+#include <sys/un.h>
2932+
2933+#include "utils/includes.h"
2934+#include "utils/common.h"
2935+#include "utils/ucode.h"
2936+#include "hostapd.h"
2937+#include "beacon.h"
2938+#include "hw_features.h"
2939+#include "ap_drv_ops.h"
2940+#include "dfs.h"
2941+#include "acs.h"
2942+#include <libubox/uloop.h>
2943+
2944+static uc_resource_type_t *global_type, *bss_type, *iface_type;
2945+static struct hapd_interfaces *interfaces;
2946+static uc_value_t *global, *bss_registry, *iface_registry;
2947+static uc_vm_t *vm;
2948+
2949+static uc_value_t *
2950+hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
2951+{
2952+ uc_value_t *val;
2953+
2954+ if (hapd->ucode.idx)
2955+ return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
2956+
2957+ val = uc_resource_new(bss_type, hapd);
2958+ hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
2959+
2960+ return val;
2961+}
2962+
2963+static uc_value_t *
2964+hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
2965+{
2966+ uc_value_t *val;
2967+
2968+ if (hapd->ucode.idx)
2969+ return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
2970+
2971+ val = uc_resource_new(iface_type, hapd);
2972+ hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
2973+
2974+ return val;
2975+}
2976+
2977+static void
2978+hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
2979+{
2980+ uc_value_t *list;
2981+ int i;
2982+
2983+ list = ucv_array_new(vm);
2984+ for (i = 0; i < iface->num_bss; i++) {
2985+ struct hostapd_data *hapd = iface->bss[i];
2986+ uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
2987+
2988+ ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
2989+ ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
2990+ }
2991+ ucv_object_add(if_bss, iface->phy, ucv_get(list));
2992+}
2993+
2994+static void
2995+hostapd_ucode_update_interfaces(void)
2996+{
2997+ uc_value_t *ifs = ucv_object_new(vm);
2998+ uc_value_t *if_bss = ucv_array_new(vm);
2999+ uc_value_t *bss = ucv_object_new(vm);
3000+ int i;
3001+
3002+ for (i = 0; i < interfaces->count; i++) {
3003+ struct hostapd_iface *iface = interfaces->iface[i];
3004+
3005+ ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
3006+ hostapd_ucode_update_bss_list(iface, if_bss, bss);
3007+ }
3008+
3009+ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
3010+ ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
3011+ ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
3012+ ucv_gc(vm);
3013+}
3014+
3015+static uc_value_t *
3016+uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
3017+{
3018+ uc_value_t *iface = uc_fn_arg(0);
3019+ int ret;
3020+
3021+ if (ucv_type(iface) != UC_STRING)
3022+ return ucv_int64_new(-1);
3023+
3024+ ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
3025+ hostapd_ucode_update_interfaces();
3026+
3027+ return ucv_int64_new(ret);
3028+}
3029+
3030+static uc_value_t *
3031+uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
3032+{
3033+ uc_value_t *iface = uc_fn_arg(0);
3034+
3035+ if (ucv_type(iface) != UC_STRING)
3036+ return NULL;
3037+
3038+ hostapd_remove_iface(interfaces, ucv_string_get(iface));
3039+ hostapd_ucode_update_interfaces();
3040+
3041+ return NULL;
3042+}
3043+
3044+static struct hostapd_vlan *
3045+bss_conf_find_vlan(struct hostapd_bss_config *bss, int id)
3046+{
3047+ struct hostapd_vlan *vlan;
3048+
3049+ for (vlan = bss->vlan; vlan; vlan = vlan->next)
3050+ if (vlan->vlan_id == id)
3051+ return vlan;
3052+
3053+ return NULL;
3054+}
3055+
3056+static int
3057+bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
3058+ const char *ifname)
3059+{
3060+ if (!strcmp(ifname, vlan->ifname))
3061+ return 0;
3062+
3063+ hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname);
3064+ os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname));
3065+
3066+ return 0;
3067+}
3068+
3069+static int
3070+bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
3071+{
3072+ struct hostapd_bss_config *old_bss = hapd->conf;
3073+ struct hostapd_vlan *vlan, *vlan_new, *wildcard;
3074+ char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos;
3075+ int ret;
3076+
3077+ vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD);
3078+ wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD);
3079+ if (!!vlan != !!wildcard)
3080+ return -1;
3081+
3082+ if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0)
3083+ strcpy(vlan->ifname, wildcard->ifname);
3084+ else
3085+ wildcard = NULL;
3086+
3087+ for (vlan = bss->vlan; vlan; vlan = vlan->next) {
3088+ if (vlan->vlan_id == VLAN_ID_WILDCARD ||
3089+ vlan->dynamic_vlan > 0)
3090+ continue;
3091+
3092+ if (!bss_conf_find_vlan(old_bss, vlan->vlan_id))
3093+ return -1;
3094+ }
3095+
3096+ for (vlan = old_bss->vlan; vlan; vlan = vlan->next) {
3097+ if (vlan->vlan_id == VLAN_ID_WILDCARD)
3098+ continue;
3099+
3100+ if (vlan->dynamic_vlan == 0) {
3101+ vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id);
3102+ if (!vlan_new)
3103+ return -1;
3104+
3105+ if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname))
3106+ return -1;
3107+
3108+ continue;
3109+ }
3110+
3111+ if (!wildcard)
3112+ continue;
3113+
3114+ os_strlcpy(ifname, wildcard->ifname, sizeof(ifname));
3115+ pos = os_strchr(ifname, '#');
3116+ if (!pos)
3117+ return -1;
3118+
3119+ *pos++ = '\0';
3120+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s",
3121+ ifname, vlan->vlan_id, pos);
3122+ if (os_snprintf_error(sizeof(vlan_ifname), ret))
3123+ return -1;
3124+
3125+ if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname))
3126+ return -1;
3127+ }
3128+
3129+ return 0;
3130+}
3131+
3132+static uc_value_t *
3133+uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
3134+{
3135+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
3136+ struct hostapd_bss_config *old_bss;
3137+ struct hostapd_iface *iface;
3138+ struct hostapd_config *conf;
3139+ uc_value_t *file = uc_fn_arg(0);
3140+ uc_value_t *index = uc_fn_arg(1);
3141+ uc_value_t *files_only = uc_fn_arg(2);
3142+ unsigned int i, idx = 0;
3143+ int ret = -1;
3144+
3145+ if (!hapd || ucv_type(file) != UC_STRING)
3146+ goto out;
3147+
3148+ if (ucv_type(index) == UC_INTEGER)
3149+ idx = ucv_int64_get(index);
3150+
3151+ iface = hapd->iface;
3152+ conf = interfaces->config_read_cb(ucv_string_get(file));
3153+ if (!conf)
3154+ goto out;
3155+
3156+ if (idx > conf->num_bss || !conf->bss[idx])
3157+ goto free;
3158+
3159+ if (ucv_boolean_get(files_only)) {
3160+ struct hostapd_bss_config *bss = conf->bss[idx];
3161+ struct hostapd_bss_config *old_bss = hapd->conf;
3162+
3163+#define swap_field(name) \
3164+ do { \
3165+ void *ptr = old_bss->name; \
3166+ old_bss->name = bss->name; \
3167+ bss->name = ptr; \
3168+ } while (0)
3169+
3170+ swap_field(ssid.wpa_psk_file);
3171+ ret = bss_reload_vlans(hapd, bss);
3172+ goto done;
3173+ }
3174+
3175+ hostapd_bss_deinit_no_free(hapd);
3176+ hostapd_drv_stop_ap(hapd);
3177+ hostapd_free_hapd_data(hapd);
3178+
3179+ old_bss = hapd->conf;
3180+ for (i = 0; i < iface->conf->num_bss; i++)
3181+ if (iface->conf->bss[i] == hapd->conf)
3182+ iface->conf->bss[i] = conf->bss[idx];
3183+ hapd->conf = conf->bss[idx];
3184+ conf->bss[idx] = old_bss;
3185+
3186+ hostapd_setup_bss(hapd, hapd == iface->bss[0], true);
3187+ hostapd_ucode_update_interfaces();
3188+
3189+done:
3190+ ret = 0;
3191+free:
3192+ hostapd_config_free(conf);
3193+out:
3194+ return ucv_int64_new(ret);
3195+}
3196+
3197+static void
3198+hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
3199+ struct hostapd_bss_config *conf)
3200+{
3201+ int i;
3202+
3203+ for (i = 0; i < iconf->num_bss; i++)
3204+ if (iconf->bss[i] == conf)
3205+ break;
3206+
3207+ if (i == iconf->num_bss)
3208+ return;
3209+
3210+ for (i++; i < iconf->num_bss; i++)
3211+ iconf->bss[i - 1] = iconf->bss[i];
3212+ iconf->num_bss--;
3213+}
3214+
3215+
3216+static uc_value_t *
3217+uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
3218+{
3219+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
3220+ struct hostapd_iface *iface;
3221+ int i, idx;
3222+
3223+ if (!hapd)
3224+ return NULL;
3225+
3226+ iface = hapd->iface;
3227+ if (iface->num_bss == 1) {
3228+ wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
3229+ return NULL;
3230+ }
3231+
3232+ for (idx = 0; idx < iface->num_bss; idx++)
3233+ if (iface->bss[idx] == hapd)
3234+ break;
3235+
3236+ if (idx == iface->num_bss)
3237+ return NULL;
3238+
3239+ for (i = idx + 1; i < iface->num_bss; i++)
3240+ iface->bss[i - 1] = iface->bss[i];
3241+
3242+ iface->num_bss--;
3243+
3244+ iface->bss[0]->interface_added = 0;
3245+ hostapd_drv_set_first_bss(iface->bss[0]);
3246+ hapd->interface_added = 1;
3247+
3248+ hostapd_drv_stop_ap(hapd);
3249+ hostapd_bss_deinit(hapd);
3250+ hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
3251+ hostapd_config_free_bss(hapd->conf);
3252+ os_free(hapd);
3253+
3254+ hostapd_ucode_update_interfaces();
3255+ ucv_gc(vm);
3256+
3257+ return NULL;
3258+}
3259+
3260+static uc_value_t *
3261+uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
3262+{
3263+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
3264+ struct hostapd_bss_config *bss;
3265+ struct hostapd_config *conf;
3266+ struct hostapd_data *hapd;
3267+ uc_value_t *file = uc_fn_arg(0);
3268+ uc_value_t *index = uc_fn_arg(1);
3269+ unsigned int idx = 0;
3270+ uc_value_t *ret = NULL;
3271+
3272+ if (!iface || ucv_type(file) != UC_STRING)
3273+ goto out;
3274+
3275+ if (ucv_type(index) == UC_INTEGER)
3276+ idx = ucv_int64_get(index);
3277+
3278+ conf = interfaces->config_read_cb(ucv_string_get(file));
3279+ if (!conf || idx > conf->num_bss || !conf->bss[idx])
3280+ goto out;
3281+
3282+ bss = conf->bss[idx];
3283+ hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
3284+ if (!hapd)
3285+ goto out;
3286+
3287+ hapd->driver = iface->bss[0]->driver;
3288+ hapd->drv_priv = iface->bss[0]->drv_priv;
3289+ if (interfaces->ctrl_iface_init &&
3290+ interfaces->ctrl_iface_init(hapd) < 0)
3291+ goto free_hapd;
3292+
3293+ if (iface->state == HAPD_IFACE_ENABLED &&
3294+ hostapd_setup_bss(hapd, -1, true))
3295+ goto deinit_ctrl;
3296+
3297+ iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
3298+ sizeof(*iface->bss));
3299+ iface->bss[iface->num_bss++] = hapd;
3300+
3301+ iface->conf->bss = os_realloc_array(iface->conf->bss,
3302+ iface->conf->num_bss + 1,
3303+ sizeof(*iface->conf->bss));
3304+ iface->conf->bss[iface->conf->num_bss] = bss;
3305+ conf->bss[idx] = NULL;
3306+ ret = hostapd_ucode_bss_get_uval(hapd);
3307+ hostapd_ucode_update_interfaces();
3308+ goto out;
3309+
3310+deinit_ctrl:
3311+ if (interfaces->ctrl_iface_deinit)
3312+ interfaces->ctrl_iface_deinit(hapd);
3313+free_hapd:
3314+ hostapd_free_hapd_data(hapd);
3315+ os_free(hapd);
3316+out:
3317+ hostapd_config_free(conf);
3318+ return ret;
3319+}
3320+
3321+static uc_value_t *
3322+uc_hostapd_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
3323+{
3324+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
3325+ uc_value_t *bss_list = uc_fn_arg(0);
3326+ struct hostapd_data **new_bss;
3327+ struct hostapd_bss_config **new_conf;
3328+
3329+ if (!iface)
3330+ return NULL;
3331+
3332+ if (ucv_type(bss_list) != UC_ARRAY ||
3333+ ucv_array_length(bss_list) != iface->num_bss)
3334+ return NULL;
3335+
3336+ new_bss = calloc(iface->num_bss, sizeof(*new_bss));
3337+ new_conf = calloc(iface->num_bss, sizeof(*new_conf));
3338+ for (size_t i = 0; i < iface->num_bss; i++) {
3339+ struct hostapd_data *bss;
3340+
3341+ bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
3342+ if (bss->iface != iface)
3343+ goto free;
3344+
3345+ for (size_t k = 0; k < i; k++)
3346+ if (new_bss[k] == bss)
3347+ goto free;
3348+
3349+ new_bss[i] = bss;
3350+ new_conf[i] = bss->conf;
3351+ }
3352+
3353+ new_bss[0]->interface_added = 0;
3354+ for (size_t i = 1; i < iface->num_bss; i++)
3355+ new_bss[i]->interface_added = 1;
3356+
3357+ free(iface->bss);
3358+ iface->bss = new_bss;
3359+
3360+ free(iface->conf->bss);
3361+ iface->conf->bss = new_conf;
3362+ iface->conf->num_bss = iface->num_bss;
3363+ hostapd_drv_set_first_bss(iface->bss[0]);
3364+
3365+ return ucv_boolean_new(true);
3366+
3367+free:
3368+ free(new_bss);
3369+ free(new_conf);
3370+ return NULL;
3371+}
3372+
3373+static uc_value_t *
3374+uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
3375+{
3376+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
3377+ uc_value_t *arg = uc_fn_arg(0);
3378+ struct sockaddr_storage from = {};
3379+ static char reply[4096];
3380+ int reply_len;
3381+
3382+ if (!hapd || !interfaces->ctrl_iface_recv ||
3383+ ucv_type(arg) != UC_STRING)
3384+ return NULL;
3385+
3386+ reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
3387+ reply, sizeof(reply),
3388+ &from, sizeof(from));
3389+ if (reply_len < 0)
3390+ return NULL;
3391+
3392+ if (reply_len && reply[reply_len - 1] == '\n')
3393+ reply_len--;
3394+
3395+ return ucv_string_new_length(reply, reply_len);
3396+}
3397+
3398+static void
3399+uc_hostapd_disable_iface(struct hostapd_iface *iface)
3400+{
3401+ switch (iface->state) {
3402+ case HAPD_IFACE_DISABLED:
3403+ break;
3404+#ifdef CONFIG_ACS
3405+ case HAPD_IFACE_ACS:
3406+ acs_cleanup(iface);
3407+ iface->scan_cb = NULL;
3408+ /* fallthrough */
3409+#endif
3410+ default:
3411+ hostapd_disable_iface(iface);
3412+ break;
3413+ }
3414+}
3415+
3416+static uc_value_t *
3417+uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
3418+{
3419+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
3420+ int i;
3421+
3422+ if (!iface)
3423+ return NULL;
3424+
3425+ if (iface->state != HAPD_IFACE_ENABLED)
3426+ uc_hostapd_disable_iface(iface);
3427+
3428+ for (i = 0; i < iface->num_bss; i++) {
3429+ struct hostapd_data *hapd = iface->bss[i];
3430+
3431+ hostapd_drv_stop_ap(hapd);
3432+ hapd->beacon_set_done = 0;
3433+ }
3434+
3435+ return NULL;
3436+}
3437+
3438+static uc_value_t *
3439+uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
3440+{
3441+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
3442+ uc_value_t *info = uc_fn_arg(0);
3443+ struct hostapd_config *conf;
3444+ bool changed = false;
3445+ uint64_t intval;
3446+ int i;
3447+
3448+ if (!iface)
3449+ return NULL;
3450+
3451+ if (!info) {
3452+ iface->freq = 0;
3453+ goto out;
3454+ }
3455+
3456+ if (ucv_type(info) != UC_OBJECT)
3457+ return NULL;
3458+
3459+#define UPDATE_VAL(field, name) \
3460+ if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \
3461+ !errno && intval != conf->field) do { \
3462+ conf->field = intval; \
3463+ changed = true; \
3464+ } while(0)
3465+
3466+ conf = iface->conf;
3467+ UPDATE_VAL(op_class, "op_class");
3468+ UPDATE_VAL(hw_mode, "hw_mode");
3469+ UPDATE_VAL(channel, "channel");
3470+ UPDATE_VAL(secondary_channel, "sec_channel");
3471+ if (!changed &&
3472+ (iface->bss[0]->beacon_set_done ||
3473+ iface->state == HAPD_IFACE_DFS))
3474+ return ucv_boolean_new(true);
3475+
3476+ intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
3477+ if (!errno)
3478+ hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
3479+
3480+ intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
3481+ if (!errno)
3482+ hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
3483+
3484+ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
3485+ if (!errno)
3486+ hostapd_set_oper_chwidth(conf, intval);
3487+
3488+ intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL));
3489+ if (!errno)
3490+ iface->freq = intval;
3491+ else
3492+ iface->freq = 0;
3493+ conf->acs = 0;
3494+
3495+out:
3496+ switch (iface->state) {
3497+ case HAPD_IFACE_ENABLED:
3498+ if (!hostapd_is_dfs_required(iface) ||
3499+ hostapd_is_dfs_chan_available(iface))
3500+ break;
3501+ wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
3502+ /* fallthrough */
3503+ default:
3504+ uc_hostapd_disable_iface(iface);
3505+ break;
3506+ }
3507+
3508+ if (conf->channel && !iface->freq)
3509+ iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
3510+
3511+ if (iface->state != HAPD_IFACE_ENABLED) {
3512+ hostapd_enable_iface(iface);
3513+ return ucv_boolean_new(true);
3514+ }
3515+
3516+ for (i = 0; i < iface->num_bss; i++) {
3517+ struct hostapd_data *hapd = iface->bss[i];
3518+ int ret;
3519+
3520+ hapd->conf->start_disabled = 0;
3521+ hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
3522+ conf->channel,
3523+ conf->enable_edmg,
3524+ conf->edmg_channel,
3525+ conf->ieee80211n,
3526+ conf->ieee80211ac,
3527+ conf->ieee80211ax,
3528+ conf->ieee80211be,
3529+ conf->secondary_channel,
3530+ hostapd_get_oper_chwidth(conf),
3531+ hostapd_get_oper_centr_freq_seg0_idx(conf),
3532+ hostapd_get_oper_centr_freq_seg1_idx(conf));
3533+
3534+ ieee802_11_set_beacon(hapd);
3535+ }
3536+
3537+ return ucv_boolean_new(true);
3538+}
3539+
3540+static uc_value_t *
3541+uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
3542+{
3543+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
3544+ uc_value_t *info = uc_fn_arg(0);
3545+ struct hostapd_config *conf;
3546+ struct csa_settings csa = {};
3547+ uint64_t intval;
3548+ int i, ret = 0;
3549+
3550+ if (!iface || ucv_type(info) != UC_OBJECT)
3551+ return NULL;
3552+
3553+ conf = iface->conf;
3554+ if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
3555+ csa.cs_count = intval;
3556+ if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
3557+ csa.freq_params.sec_channel_offset = intval;
3558+
3559+ csa.freq_params.ht_enabled = conf->ieee80211n;
3560+ csa.freq_params.vht_enabled = conf->ieee80211ac;
3561+ csa.freq_params.he_enabled = conf->ieee80211ax;
3562+#ifdef CONFIG_IEEE80211BE
3563+ csa.freq_params.eht_enabled = conf->ieee80211be;
3564+#endif
3565+ intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
3566+ if (errno)
3567+ intval = hostapd_get_oper_chwidth(conf);
3568+ if (intval)
3569+ csa.freq_params.bandwidth = 40 << intval;
3570+ else
3571+ csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
3572+
3573+ if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
3574+ csa.freq_params.freq = intval;
3575+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
3576+ csa.freq_params.center_freq1 = intval;
3577+ if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
3578+ csa.freq_params.center_freq2 = intval;
3579+
3580+ for (i = 0; i < iface->num_bss; i++)
3581+ ret = hostapd_switch_channel(iface->bss[i], &csa);
3582+
3583+ return ucv_boolean_new(!ret);
3584+}
3585+
3586+static uc_value_t *
3587+uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
3588+{
3589+ struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
3590+ uc_value_t *ifname_arg = uc_fn_arg(0);
3591+ char prev_ifname[IFNAMSIZ + 1];
3592+ struct sta_info *sta;
3593+ const char *ifname;
3594+ int ret;
3595+
3596+ if (!hapd || ucv_type(ifname_arg) != UC_STRING)
3597+ return NULL;
3598+
3599+ os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
3600+ ifname = ucv_string_get(ifname_arg);
3601+
3602+ hostapd_ubus_free_bss(hapd);
3603+ if (interfaces->ctrl_iface_deinit)
3604+ interfaces->ctrl_iface_deinit(hapd);
3605+
3606+ ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
3607+ if (ret)
3608+ goto out;
3609+
3610+ for (sta = hapd->sta_list; sta; sta = sta->next) {
3611+ char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
3612+
3613+ if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
3614+ continue;
3615+
3616+ snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
3617+ snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
3618+ hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
3619+ }
3620+
3621+ if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
3622+ os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
3623+ os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
3624+ hostapd_ubus_add_bss(hapd);
3625+
3626+ hostapd_ucode_update_interfaces();
3627+out:
3628+ if (interfaces->ctrl_iface_init)
3629+ interfaces->ctrl_iface_init(hapd);
3630+
3631+ return ret ? NULL : ucv_boolean_new(true);
3632+}
3633+
3634+
3635+int hostapd_ucode_init(struct hapd_interfaces *ifaces)
3636+{
3637+ static const uc_function_list_t global_fns[] = {
3638+ { "printf", uc_wpa_printf },
3639+ { "getpid", uc_wpa_getpid },
3640+ { "sha1", uc_wpa_sha1 },
3641+ { "freq_info", uc_wpa_freq_info },
3642+ { "add_iface", uc_hostapd_add_iface },
3643+ { "remove_iface", uc_hostapd_remove_iface },
3644+ { "udebug_set", uc_wpa_udebug_set },
3645+ };
3646+ static const uc_function_list_t bss_fns[] = {
3647+ { "ctrl", uc_hostapd_bss_ctrl },
3648+ { "set_config", uc_hostapd_bss_set_config },
3649+ { "rename", uc_hostapd_bss_rename },
3650+ { "delete", uc_hostapd_bss_delete },
3651+ };
3652+ static const uc_function_list_t iface_fns[] = {
3653+ { "set_bss_order", uc_hostapd_iface_set_bss_order },
3654+ { "add_bss", uc_hostapd_iface_add_bss },
3655+ { "stop", uc_hostapd_iface_stop },
3656+ { "start", uc_hostapd_iface_start },
3657+ { "switch_channel", uc_hostapd_iface_switch_channel },
3658+ };
3659+ uc_value_t *data, *proto;
3660+
3661+ interfaces = ifaces;
3662+ vm = wpa_ucode_create_vm();
3663+
3664+ global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
3665+ bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
3666+ iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
3667+
3668+ bss_registry = ucv_array_new(vm);
3669+ uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
3670+
3671+ iface_registry = ucv_array_new(vm);
3672+ uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
3673+
3674+ global = wpa_ucode_global_init("hostapd", global_type);
3675+
3676+ if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
3677+ goto free_vm;
3678+ ucv_gc(vm);
3679+
3680+ return 0;
3681+
3682+free_vm:
3683+ wpa_ucode_free_vm();
3684+ return -1;
3685+}
3686+
3687+void hostapd_ucode_free(void)
3688+{
3689+ if (wpa_ucode_call_prepare("shutdown") == 0)
3690+ ucv_put(wpa_ucode_call(0));
3691+ wpa_ucode_free_vm();
3692+}
3693+
3694+void hostapd_ucode_free_iface(struct hostapd_iface *iface)
3695+{
3696+ wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
3697+}
3698+
3699+void hostapd_ucode_add_bss(struct hostapd_data *hapd)
3700+{
3701+ uc_value_t *val;
3702+
3703+ if (wpa_ucode_call_prepare("bss_add"))
3704+ return;
3705+
3706+ val = hostapd_ucode_bss_get_uval(hapd);
3707+ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
3708+ uc_value_push(ucv_get(val));
3709+ ucv_put(wpa_ucode_call(2));
3710+ ucv_gc(vm);
3711+}
3712+
3713+void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
3714+{
3715+ uc_value_t *val;
3716+
3717+ if (wpa_ucode_call_prepare("bss_reload"))
3718+ return;
3719+
3720+ val = hostapd_ucode_bss_get_uval(hapd);
3721+ uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
3722+ uc_value_push(ucv_get(val));
3723+ ucv_put(wpa_ucode_call(2));
3724+ ucv_gc(vm);
3725+}
3726+
3727+void hostapd_ucode_free_bss(struct hostapd_data *hapd)
3728+{
3729+ uc_value_t *val;
3730+
3731+ val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
3732+ if (!val)
3733+ return;
3734+
3735+ hapd->ucode.idx = 0;
3736+ if (wpa_ucode_call_prepare("bss_remove"))
3737+ return;
3738+
3739+ uc_value_push(ucv_string_new(hapd->conf->iface));
3740+ uc_value_push(ucv_get(val));
3741+ ucv_put(wpa_ucode_call(2));
3742+ ucv_gc(vm);
3743+}
3744diff --git a/src/ap/ucode.h b/src/ap/ucode.h
3745new file mode 100644
3746index 000000000..d00b78716
3747--- /dev/null
3748+++ b/src/ap/ucode.h
3749@@ -0,0 +1,54 @@
3750+#ifndef __HOSTAPD_AP_UCODE_H
3751+#define __HOSTAPD_AP_UCODE_H
3752+
3753+#include "utils/ucode.h"
3754+
3755+struct hostapd_data;
3756+
3757+struct hostapd_ucode_bss {
3758+#ifdef UCODE_SUPPORT
3759+ int idx;
3760+#endif
3761+};
3762+
3763+struct hostapd_ucode_iface {
3764+#ifdef UCODE_SUPPORT
3765+ int idx;
3766+#endif
3767+};
3768+
3769+#ifdef UCODE_SUPPORT
3770+
3771+int hostapd_ucode_init(struct hapd_interfaces *ifaces);
3772+
3773+void hostapd_ucode_free(void);
3774+void hostapd_ucode_free_iface(struct hostapd_iface *iface);
3775+void hostapd_ucode_add_bss(struct hostapd_data *hapd);
3776+void hostapd_ucode_free_bss(struct hostapd_data *hapd);
3777+void hostapd_ucode_reload_bss(struct hostapd_data *hapd);
3778+
3779+#else
3780+
3781+static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
3782+{
3783+ return -EINVAL;
3784+}
3785+static inline void hostapd_ucode_free(void)
3786+{
3787+}
3788+static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface)
3789+{
3790+}
3791+static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
3792+{
3793+}
3794+static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd)
3795+{
3796+}
3797+static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
3798+{
3799+}
3800+
3801+#endif
3802+
3803+#endif
3804diff --git a/src/utils/build_features.h b/src/utils/build_features.h
3805new file mode 100644
3806index 000000000..553769ece
3807--- /dev/null
3808+++ b/src/utils/build_features.h
3809@@ -0,0 +1,65 @@
3810+#ifndef BUILD_FEATURES_H
3811+#define BUILD_FEATURES_H
3812+
3813+static inline int has_feature(const char *feat)
3814+{
3815+#if defined(IEEE8021X_EAPOL) || (defined(HOSTAPD) && !defined(CONFIG_NO_RADIUS))
3816+ if (!strcmp(feat, "eap"))
3817+ return 1;
3818+#endif
3819+#ifdef CONFIG_IEEE80211AC
3820+ if (!strcmp(feat, "11ac"))
3821+ return 1;
3822+#endif
3823+#ifdef CONFIG_IEEE80211AX
3824+ if (!strcmp(feat, "11ax"))
3825+ return 1;
3826+#endif
3827+#ifdef CONFIG_IEEE80211R
3828+ if (!strcmp(feat, "11r"))
3829+ return 1;
3830+#endif
3831+#ifdef CONFIG_ACS
3832+ if (!strcmp(feat, "acs"))
3833+ return 1;
3834+#endif
3835+#ifdef CONFIG_SAE
3836+ if (!strcmp(feat, "sae"))
3837+ return 1;
3838+#endif
3839+#ifdef CONFIG_OWE
3840+ if (!strcmp(feat, "owe"))
3841+ return 1;
3842+#endif
3843+#ifdef CONFIG_SUITEB192
3844+ if (!strcmp(feat, "suiteb192"))
3845+ return 1;
3846+#endif
3847+#ifdef CONFIG_WEP
3848+ if (!strcmp(feat, "wep"))
3849+ return 1;
3850+#endif
3851+#ifdef CONFIG_HS20
3852+ if (!strcmp(feat, "hs20"))
3853+ return 1;
3854+#endif
3855+#ifdef CONFIG_WPS
3856+ if (!strcmp(feat, "wps"))
3857+ return 1;
3858+#endif
3859+#ifdef CONFIG_FILS
3860+ if (!strcmp(feat, "fils"))
3861+ return 1;
3862+#endif
3863+#ifdef CONFIG_OCV
3864+ if (!strcmp(feat, "ocv"))
3865+ return 1;
3866+#endif
3867+#ifdef CONFIG_MESH
3868+ if (!strcmp(feat, "mesh"))
3869+ return 1;
3870+#endif
3871+ return 0;
3872+}
3873+
3874+#endif /* BUILD_FEATURES_H */
3875diff --git a/src/utils/ucode.c b/src/utils/ucode.c
3876new file mode 100644
3877index 000000000..29c753c32
3878--- /dev/null
3879+++ b/src/utils/ucode.c
3880@@ -0,0 +1,502 @@
3881+#include <unistd.h>
3882+#include "ucode.h"
3883+#include "utils/eloop.h"
3884+#include "crypto/crypto.h"
3885+#include "crypto/sha1.h"
3886+#include "common/ieee802_11_common.h"
3887+#include <linux/netlink.h>
3888+#include <linux/genetlink.h>
3889+#include <linux/nl80211.h>
3890+#include <libubox/uloop.h>
3891+#include <ucode/compiler.h>
3892+#include <udebug.h>
3893+
3894+static uc_value_t *registry;
3895+static uc_vm_t vm;
3896+static struct uloop_timeout gc_timer;
3897+static struct udebug ud;
3898+static struct udebug_buf ud_log, ud_nl[3];
3899+static const struct udebug_buf_meta meta_log = {
3900+ .name = "wpa_log",
3901+ .format = UDEBUG_FORMAT_STRING,
3902+};
3903+static const struct udebug_buf_meta meta_nl_ll = {
3904+ .name = "wpa_nl_ctrl",
3905+ .format = UDEBUG_FORMAT_PACKET,
3906+ .sub_format = UDEBUG_DLT_NETLINK,
3907+};
3908+static const struct udebug_buf_meta meta_nl_tx = {
3909+ .name = "wpa_nl_tx",
3910+ .format = UDEBUG_FORMAT_PACKET,
3911+ .sub_format = UDEBUG_DLT_NETLINK,
3912+};
3913+#define UDEBUG_FLAG_RX_FRAME (1ULL << 0)
3914+static const struct udebug_buf_flag rx_flags[] = {
3915+ { "rx_frame", UDEBUG_FLAG_RX_FRAME },
3916+};
3917+static const struct udebug_buf_meta meta_nl_rx = {
3918+ .name = "wpa_nl_rx",
3919+ .format = UDEBUG_FORMAT_PACKET,
3920+ .sub_format = UDEBUG_DLT_NETLINK,
3921+ .flags = rx_flags,
3922+ .n_flags = ARRAY_SIZE(rx_flags),
3923+};
3924+static struct udebug_ubus_ring udebug_rings[] = {
3925+ {
3926+ .buf = &ud_log,
3927+ .meta = &meta_log,
3928+ .default_entries = 1024,
3929+ .default_size = 64 * 1024
3930+ },
3931+ {
3932+ .buf = &ud_nl[0],
3933+ .meta = &meta_nl_rx,
3934+ .default_entries = 1024,
3935+ .default_size = 256 * 1024,
3936+ },
3937+ {
3938+ .buf = &ud_nl[1],
3939+ .meta = &meta_nl_tx,
3940+ .default_entries = 1024,
3941+ .default_size = 64 * 1024,
3942+ },
3943+ {
3944+ .buf = &ud_nl[2],
3945+ .meta = &meta_nl_ll,
3946+ .default_entries = 1024,
3947+ .default_size = 32 * 1024,
3948+ }
3949+};
3950+char *udebug_service;
3951+struct udebug_ubus ud_ubus;
3952+
3953+static void uc_gc_timer(struct uloop_timeout *timeout)
3954+{
3955+ ucv_gc(&vm);
3956+}
3957+
3958+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
3959+{
3960+ uc_value_t *level = uc_fn_arg(0);
3961+ uc_value_t *ret, **args;
3962+ uc_cfn_ptr_t _sprintf;
3963+ int l = MSG_INFO;
3964+ int i, start = 0;
3965+
3966+ _sprintf = uc_stdlib_function("sprintf");
3967+ if (!sprintf)
3968+ return NULL;
3969+
3970+ if (ucv_type(level) == UC_INTEGER) {
3971+ l = ucv_int64_get(level);
3972+ start++;
3973+ }
3974+
3975+ if (nargs <= start)
3976+ return NULL;
3977+
3978+ ret = _sprintf(vm, nargs - start);
3979+ if (ucv_type(ret) != UC_STRING)
3980+ return NULL;
3981+
3982+ wpa_printf(l, "%s", ucv_string_get(ret));
3983+ ucv_put(ret);
3984+
3985+ return NULL;
3986+}
3987+
3988+uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
3989+{
3990+ uc_value_t *freq = uc_fn_arg(0);
3991+ uc_value_t *sec = uc_fn_arg(1);
3992+ int width = ucv_uint64_get(uc_fn_arg(2));
3993+ int freq_val, center_idx, center_ofs;
3994+ enum oper_chan_width chanwidth;
3995+ enum hostapd_hw_mode hw_mode;
3996+ u8 op_class, channel, tmp_channel;
3997+ const char *modestr;
3998+ int sec_channel = 0;
3999+ uc_value_t *ret;
4000+
4001+ if (ucv_type(freq) != UC_INTEGER)
4002+ return NULL;
4003+
4004+ freq_val = ucv_int64_get(freq);
4005+ if (ucv_type(sec) == UC_INTEGER)
4006+ sec_channel = ucv_int64_get(sec);
4007+ else if (sec)
4008+ return NULL;
4009+ else if (freq_val > 4000)
4010+ sec_channel = (freq_val / 20) & 1 ? 1 : -1;
4011+ else
4012+ sec_channel = freq_val < 2442 ? 1 : -1;
4013+
4014+ if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
4015+ return NULL;
4016+
4017+ switch (width) {
4018+ case 0:
4019+ chanwidth = CONF_OPER_CHWIDTH_USE_HT;
4020+ break;
4021+ case 1:
4022+ chanwidth = CONF_OPER_CHWIDTH_80MHZ;
4023+ break;
4024+ case 2:
4025+ chanwidth = CONF_OPER_CHWIDTH_160MHZ;
4026+ break;
4027+ default:
4028+ return NULL;
4029+ }
4030+
4031+ hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
4032+ chanwidth, &op_class, &channel);
4033+ switch (hw_mode) {
4034+ case HOSTAPD_MODE_IEEE80211B:
4035+ modestr = "b";
4036+ break;
4037+ case HOSTAPD_MODE_IEEE80211G:
4038+ modestr = "g";
4039+ break;
4040+ case HOSTAPD_MODE_IEEE80211A:
4041+ modestr = "a";
4042+ break;
4043+ case HOSTAPD_MODE_IEEE80211AD:
4044+ modestr = "ad";
4045+ break;
4046+ default:
4047+ return NULL;
4048+ }
4049+
4050+ ret = ucv_object_new(vm);
4051+ ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
4052+ ucv_object_add(ret, "channel", ucv_int64_new(channel));
4053+ ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
4054+ ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
4055+ ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
4056+ ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
4057+
4058+ if (!sec_channel)
4059+ return ret;
4060+
4061+ if (freq_val >= 5900)
4062+ center_ofs = 0;
4063+ else if (freq_val >= 5745)
4064+ center_ofs = 20;
4065+ else
4066+ center_ofs = 35;
4067+ tmp_channel = channel - center_ofs;
4068+ tmp_channel &= ~((8 << width) - 1);
4069+ center_idx = tmp_channel + center_ofs + (4 << width) - 1;
4070+
4071+ if (freq_val < 3000)
4072+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
4073+ else
4074+ ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
4075+ center_idx = (center_idx - channel) * 5 + freq_val;
4076+ ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
4077+
4078+out:
4079+ return ret;
4080+}
4081+
4082+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
4083+{
4084+ return ucv_int64_new(getpid());
4085+}
4086+
4087+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
4088+{
4089+ u8 hash[SHA1_MAC_LEN];
4090+ char hash_hex[2 * ARRAY_SIZE(hash) + 1];
4091+ uc_value_t *val;
4092+ size_t *lens;
4093+ const u8 **args;
4094+ int i;
4095+
4096+ if (!nargs)
4097+ return NULL;
4098+
4099+ args = alloca(nargs * sizeof(*args));
4100+ lens = alloca(nargs * sizeof(*lens));
4101+ for (i = 0; i < nargs; i++) {
4102+ val = uc_fn_arg(i);
4103+ if (ucv_type(val) != UC_STRING)
4104+ return NULL;
4105+
4106+ args[i] = ucv_string_get(val);
4107+ lens[i] = ucv_string_length(val);
4108+ }
4109+
4110+ if (sha1_vector(nargs, args, lens, hash))
4111+ return NULL;
4112+
4113+ for (i = 0; i < ARRAY_SIZE(hash); i++)
4114+ sprintf(hash_hex + 2 * i, "%02x", hash[i]);
4115+
4116+ return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
4117+}
4118+
4119+uc_vm_t *wpa_ucode_create_vm(void)
4120+{
4121+ static uc_parse_config_t config = {
4122+ .strict_declarations = true,
4123+ .lstrip_blocks = true,
4124+ .trim_blocks = true,
4125+ .raw_mode = true
4126+ };
4127+
4128+ uc_search_path_init(&config.module_search_path);
4129+ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
4130+ uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
4131+
4132+ uc_vm_init(&vm, &config);
4133+
4134+ uc_stdlib_load(uc_vm_scope_get(&vm));
4135+ eloop_add_uloop();
4136+ gc_timer.cb = uc_gc_timer;
4137+
4138+ return &vm;
4139+}
4140+
4141+int wpa_ucode_run(const char *script)
4142+{
4143+ uc_source_t *source;
4144+ uc_program_t *prog;
4145+ uc_value_t *ops;
4146+ char *err;
4147+ int ret;
4148+
4149+ source = uc_source_new_file(script);
4150+ if (!source)
4151+ return -1;
4152+
4153+ prog = uc_compile(vm.config, source, &err);
4154+ uc_source_put(source);
4155+ if (!prog) {
4156+ wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
4157+ return -1;
4158+ }
4159+
4160+ ret = uc_vm_execute(&vm, prog, &ops);
4161+ uc_program_put(prog);
4162+ if (ret || !ops)
4163+ return -1;
4164+
4165+ registry = ucv_array_new(&vm);
4166+ uc_vm_registry_set(&vm, "hostap.registry", registry);
4167+ ucv_array_set(registry, 0, ucv_get(ops));
4168+
4169+ return 0;
4170+}
4171+
4172+int wpa_ucode_call_prepare(const char *fname)
4173+{
4174+ uc_value_t *obj, *func;
4175+
4176+ if (!registry)
4177+ return -1;
4178+
4179+ obj = ucv_array_get(registry, 0);
4180+ if (!obj)
4181+ return -1;
4182+
4183+ func = ucv_object_get(obj, fname, NULL);
4184+ if (!ucv_is_callable(func))
4185+ return -1;
4186+
4187+ uc_vm_stack_push(&vm, ucv_get(obj));
4188+ uc_vm_stack_push(&vm, ucv_get(func));
4189+
4190+ return 0;
4191+}
4192+
4193+static void udebug_printf_hook(int level, const char *fmt, va_list ap)
4194+{
4195+ udebug_entry_init(&ud_log);
4196+ udebug_entry_vprintf(&ud_log, fmt, ap);
4197+ udebug_entry_add(&ud_log);
4198+}
4199+
4200+static void udebug_hexdump_hook(int level, const char *title,
4201+ const void *data, size_t len)
4202+{
4203+ char *buf;
4204+
4205+ udebug_entry_init(&ud_log);
4206+ udebug_entry_printf(&ud_log, "%s - hexdump:", title);
4207+ buf = udebug_entry_append(&ud_log, NULL, 3 * len);
4208+ for (size_t i = 0; i < len; i++)
4209+ buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
4210+ udebug_entry_add(&ud_log);
4211+}
4212+
4213+static void udebug_netlink_hook(int tx, const void *data, size_t len)
4214+{
4215+ struct {
4216+ uint16_t pkttype;
4217+ uint16_t arphdr;
4218+ uint16_t _pad[5];
4219+ uint16_t proto;
4220+ } hdr = {
4221+ .pkttype = host_to_be16(tx ? 7 : 6),
4222+ .arphdr = host_to_be16(824),
4223+ .proto = host_to_be16(16),
4224+ };
4225+ const struct nlmsghdr *nlh = data;
4226+ const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
4227+ struct udebug_buf *buf = &ud_nl[!!tx];
4228+
4229+ if (nlh->nlmsg_type == 0x10)
4230+ buf = &ud_nl[2];
4231+ else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
4232+ !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
4233+ return;
4234+
4235+ if (!udebug_buf_valid(buf))
4236+ return;
4237+
4238+ udebug_entry_init(buf);
4239+ udebug_entry_append(buf, &hdr, sizeof(hdr));
4240+ udebug_entry_append(buf, data, len);
4241+ udebug_entry_add(buf);
4242+}
4243+
4244+static void
4245+wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
4246+ bool enabled)
4247+{
4248+ udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
4249+ data, enabled);
4250+
4251+ if (udebug_buf_valid(&ud_log)) {
4252+ wpa_printf_hook = udebug_printf_hook;
4253+ wpa_hexdump_hook = udebug_hexdump_hook;
4254+ } else {
4255+ wpa_printf_hook = NULL;
4256+ wpa_hexdump_hook = NULL;
4257+ }
4258+
4259+ if (udebug_buf_valid(&ud_nl[0]) ||
4260+ udebug_buf_valid(&ud_nl[1]) ||
4261+ udebug_buf_valid(&ud_nl[2]))
4262+ wpa_netlink_hook = udebug_netlink_hook;
4263+ else
4264+ wpa_netlink_hook = NULL;
4265+}
4266+
4267+uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
4268+{
4269+ uc_value_t *name = uc_fn_arg(0);
4270+ uc_value_t *ubus = uc_fn_arg(1);
4271+ static bool enabled = false;
4272+ struct ubus_context *ctx;
4273+ bool cur_en;
4274+
4275+ cur_en = ucv_type(name) == UC_STRING;
4276+ ctx = ucv_resource_data(ubus, "ubus.connection");
4277+ if (!ctx)
4278+ cur_en = false;
4279+
4280+ if (enabled == cur_en)
4281+ return ucv_boolean_new(true);
4282+
4283+ enabled = cur_en;
4284+ if (enabled) {
4285+ udebug_service = strdup(ucv_string_get(name));
4286+ udebug_init(&ud);
4287+ udebug_auto_connect(&ud, NULL);
4288+ udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
4289+ } else {
4290+ udebug_ubus_free(&ud_ubus);
4291+ for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
4292+ if (udebug_buf_valid(udebug_rings[i].buf))
4293+ udebug_buf_free(udebug_rings[i].buf);
4294+ udebug_free(&ud);
4295+ free(udebug_service);
4296+ }
4297+
4298+ return ucv_boolean_new(true);
4299+}
4300+
4301+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
4302+{
4303+ uc_value_t *global = uc_resource_new(global_type, NULL);
4304+ uc_value_t *proto;
4305+
4306+ uc_vm_registry_set(&vm, "hostap.global", global);
4307+ proto = ucv_prototype_get(global);
4308+ ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
4309+
4310+#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
4311+ ADD_CONST(MSG_EXCESSIVE);
4312+ ADD_CONST(MSG_MSGDUMP);
4313+ ADD_CONST(MSG_DEBUG);
4314+ ADD_CONST(MSG_INFO);
4315+ ADD_CONST(MSG_WARNING);
4316+ ADD_CONST(MSG_ERROR);
4317+#undef ADD_CONST
4318+
4319+ ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
4320+
4321+ return global;
4322+}
4323+
4324+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
4325+{
4326+ uc_value_t *data;
4327+ int i = 0;
4328+
4329+ while (ucv_array_get(reg, i))
4330+ i++;
4331+
4332+ ucv_array_set(reg, i, ucv_get(val));
4333+
4334+ return i + 1;
4335+}
4336+
4337+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
4338+{
4339+ if (!idx)
4340+ return NULL;
4341+
4342+ return ucv_array_get(reg, idx - 1);
4343+}
4344+
4345+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
4346+{
4347+ uc_value_t *val = wpa_ucode_registry_get(reg, idx);
4348+ void **dataptr;
4349+
4350+ if (!val)
4351+ return NULL;
4352+
4353+ ucv_array_set(reg, idx - 1, NULL);
4354+ dataptr = ucv_resource_dataptr(val, NULL);
4355+ if (dataptr)
4356+ *dataptr = NULL;
4357+
4358+ return val;
4359+}
4360+
4361+
4362+uc_value_t *wpa_ucode_call(size_t nargs)
4363+{
4364+ if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
4365+ return NULL;
4366+
4367+ if (!gc_timer.pending)
4368+ uloop_timeout_set(&gc_timer, 10);
4369+
4370+ return uc_vm_stack_pop(&vm);
4371+}
4372+
4373+void wpa_ucode_free_vm(void)
4374+{
4375+ if (!vm.config)
4376+ return;
4377+
4378+ uc_search_path_free(&vm.config->module_search_path);
4379+ uc_vm_free(&vm);
4380+ registry = NULL;
4381+ vm = (uc_vm_t){};
4382+}
4383diff --git a/src/utils/ucode.h b/src/utils/ucode.h
4384new file mode 100644
4385index 000000000..c083241e0
4386--- /dev/null
4387+++ b/src/utils/ucode.h
4388@@ -0,0 +1,30 @@
4389+#ifndef __HOSTAPD_UTILS_UCODE_H
4390+#define __HOSTAPD_UTILS_UCODE_H
4391+
4392+#include "utils/includes.h"
4393+#include "utils/common.h"
4394+#include <ucode/lib.h>
4395+#include <ucode/vm.h>
4396+
4397+#define HOSTAPD_UC_PATH "/usr/share/hostap/"
4398+
4399+extern uc_value_t *uc_registry;
4400+uc_vm_t *wpa_ucode_create_vm(void);
4401+int wpa_ucode_run(const char *script);
4402+int wpa_ucode_call_prepare(const char *fname);
4403+uc_value_t *wpa_ucode_call(size_t nargs);
4404+void wpa_ucode_free_vm(void);
4405+
4406+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type);
4407+
4408+int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val);
4409+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx);
4410+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx);
4411+
4412+uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs);
4413+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
4414+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
4415+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
4416+uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
4417+
4418+#endif
4419diff --git a/wpa_supplicant/ubus.c b/wpa_supplicant/ubus.c
4420new file mode 100644
4421index 000000000..1c477f0c0
4422--- /dev/null
4423+++ b/wpa_supplicant/ubus.c
4424@@ -0,0 +1,280 @@
4425+/*
4426+ * wpa_supplicant / ubus support
4427+ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
4428+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
4429+ *
4430+ * This software may be distributed under the terms of the BSD license.
4431+ * See README for more details.
4432+ */
4433+
4434+#include "utils/includes.h"
4435+#include "utils/common.h"
4436+#include "utils/eloop.h"
4437+#include "utils/wpabuf.h"
4438+#include "common/ieee802_11_defs.h"
4439+#include "wpa_supplicant_i.h"
4440+#include "wps_supplicant.h"
4441+#include "ubus.h"
4442+
4443+static struct ubus_context *ctx;
4444+static struct blob_buf b;
4445+static int ctx_ref;
4446+
4447+static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
4448+{
4449+ return container_of(obj, struct wpa_global, ubus_global);
4450+}
4451+
4452+static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
4453+{
4454+ return container_of(obj, struct wpa_supplicant, ubus.obj);
4455+}
4456+
4457+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
4458+{
4459+ if (ubus_reconnect(ctx, NULL)) {
4460+ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
4461+ return;
4462+ }
4463+
4464+ ubus_add_uloop(ctx);
4465+}
4466+
4467+static void wpas_ubus_connection_lost(struct ubus_context *ctx)
4468+{
4469+ uloop_fd_delete(&ctx->sock);
4470+ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
4471+}
4472+
4473+static bool wpas_ubus_init(void)
4474+{
4475+ if (ctx)
4476+ return true;
4477+
4478+ eloop_add_uloop();
4479+ ctx = ubus_connect(NULL);
4480+ if (!ctx)
4481+ return false;
4482+
4483+ ctx->connection_lost = wpas_ubus_connection_lost;
4484+ ubus_add_uloop(ctx);
4485+
4486+ return true;
4487+}
4488+
4489+static void wpas_ubus_ref_inc(void)
4490+{
4491+ ctx_ref++;
4492+}
4493+
4494+static void wpas_ubus_ref_dec(void)
4495+{
4496+ ctx_ref--;
4497+ if (!ctx)
4498+ return;
4499+
4500+ if (ctx_ref)
4501+ return;
4502+
4503+ uloop_fd_delete(&ctx->sock);
4504+ ubus_free(ctx);
4505+ ctx = NULL;
4506+}
4507+
4508+static int
4509+wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
4510+ struct ubus_request_data *req, const char *method,
4511+ struct blob_attr *msg)
4512+{
4513+ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
4514+
4515+ blob_buf_init(&b, 0);
4516+ blobmsg_add_u8(&b, "ht_supported", ht_supported(wpa_s->hw.modes));
4517+ blobmsg_add_u8(&b, "vht_supported", vht_supported(wpa_s->hw.modes));
4518+ ubus_send_reply(ctx, req, b.head);
4519+
4520+ return 0;
4521+}
4522+
4523+static int
4524+wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
4525+ struct ubus_request_data *req, const char *method,
4526+ struct blob_attr *msg)
4527+{
4528+ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
4529+
4530+ if (wpa_supplicant_reload_configuration(wpa_s))
4531+ return UBUS_STATUS_UNKNOWN_ERROR;
4532+ else
4533+ return 0;
4534+}
4535+
4536+#ifdef CONFIG_WPS
4537+enum {
4538+ WPS_START_MULTI_AP,
4539+ __WPS_START_MAX
4540+};
4541+
4542+static const struct blobmsg_policy wps_start_policy[] = {
4543+ [WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
4544+};
4545+
4546+static int
4547+wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
4548+ struct ubus_request_data *req, const char *method,
4549+ struct blob_attr *msg)
4550+{
4551+ int rc;
4552+ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
4553+ struct blob_attr *tb[__WPS_START_MAX], *cur;
4554+ int multi_ap = 0;
4555+
4556+ blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
4557+
4558+ if (tb[WPS_START_MULTI_AP])
4559+ multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
4560+
4561+ rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
4562+
4563+ if (rc != 0)
4564+ return UBUS_STATUS_NOT_SUPPORTED;
4565+
4566+ return 0;
4567+}
4568+
4569+static int
4570+wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
4571+ struct ubus_request_data *req, const char *method,
4572+ struct blob_attr *msg)
4573+{
4574+ int rc;
4575+ struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
4576+
4577+ rc = wpas_wps_cancel(wpa_s);
4578+
4579+ if (rc != 0)
4580+ return UBUS_STATUS_NOT_SUPPORTED;
4581+
4582+ return 0;
4583+}
4584+#endif
4585+
4586+static const struct ubus_method bss_methods[] = {
4587+ UBUS_METHOD_NOARG("reload", wpas_bss_reload),
4588+ UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
4589+#ifdef CONFIG_WPS
4590+ UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
4591+ UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
4592+#endif
4593+};
4594+
4595+static struct ubus_object_type bss_object_type =
4596+ UBUS_OBJECT_TYPE("wpas_bss", bss_methods);
4597+
4598+void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
4599+{
4600+ struct ubus_object *obj = &wpa_s->ubus.obj;
4601+ char *name;
4602+ int ret;
4603+
4604+ if (!wpas_ubus_init())
4605+ return;
4606+
4607+ if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
4608+ return;
4609+
4610+ obj->name = name;
4611+ obj->type = &bss_object_type;
4612+ obj->methods = bss_object_type.methods;
4613+ obj->n_methods = bss_object_type.n_methods;
4614+ ret = ubus_add_object(ctx, obj);
4615+ wpas_ubus_ref_inc();
4616+}
4617+
4618+void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
4619+{
4620+ struct ubus_object *obj = &wpa_s->ubus.obj;
4621+ char *name = (char *) obj->name;
4622+
4623+ if (!ctx)
4624+ return;
4625+
4626+ if (obj->id) {
4627+ ubus_remove_object(ctx, obj);
4628+ wpas_ubus_ref_dec();
4629+ }
4630+
4631+ free(name);
4632+}
4633+
4634+#ifdef CONFIG_WPS
4635+void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
4636+{
4637+ u16 auth_type;
4638+ char *ifname, *encryption, *ssid, *key;
4639+ size_t ifname_len;
4640+
4641+ if (!cred)
4642+ return;
4643+
4644+ auth_type = cred->auth_type;
4645+
4646+ if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))
4647+ auth_type = WPS_AUTH_WPA2PSK;
4648+
4649+ if (auth_type != WPS_AUTH_OPEN &&
4650+ auth_type != WPS_AUTH_WPAPSK &&
4651+ auth_type != WPS_AUTH_WPA2PSK) {
4652+ wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
4653+ "unsupported authentication type 0x%x",
4654+ auth_type);
4655+ return;
4656+ }
4657+
4658+ if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
4659+ if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
4660+ wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
4661+ "invalid Network Key length %lu",
4662+ (unsigned long) cred->key_len);
4663+ return;
4664+ }
4665+ }
4666+
4667+ blob_buf_init(&b, 0);
4668+
4669+ ifname_len = strlen(wpa_s->ifname);
4670+ ifname = blobmsg_alloc_string_buffer(&b, "ifname", ifname_len + 1);
4671+ memcpy(ifname, wpa_s->ifname, ifname_len + 1);
4672+ ifname[ifname_len] = '\0';
4673+ blobmsg_add_string_buffer(&b);
4674+
4675+ switch (auth_type) {
4676+ case WPS_AUTH_WPA2PSK:
4677+ encryption = "psk2";
4678+ break;
4679+ case WPS_AUTH_WPAPSK:
4680+ encryption = "psk";
4681+ break;
4682+ default:
4683+ encryption = "none";
4684+ break;
4685+ }
4686+
4687+ blobmsg_add_string(&b, "encryption", encryption);
4688+
4689+ ssid = blobmsg_alloc_string_buffer(&b, "ssid", cred->ssid_len + 1);
4690+ memcpy(ssid, cred->ssid, cred->ssid_len);
4691+ ssid[cred->ssid_len] = '\0';
4692+ blobmsg_add_string_buffer(&b);
4693+
4694+ if (cred->key_len > 0) {
4695+ key = blobmsg_alloc_string_buffer(&b, "key", cred->key_len + 1);
4696+ memcpy(key, cred->key, cred->key_len);
4697+ key[cred->key_len] = '\0';
4698+ blobmsg_add_string_buffer(&b);
4699+ }
4700+
4701+// ubus_notify(ctx, &wpa_s->ubus.obj, "wps_credentials", b.head, -1);
4702+ ubus_send_event(ctx, "wps_credentials", b.head);
4703+}
4704+#endif /* CONFIG_WPS */
4705diff --git a/wpa_supplicant/ubus.h b/wpa_supplicant/ubus.h
4706new file mode 100644
4707index 000000000..f6681cb26
4708--- /dev/null
4709+++ b/wpa_supplicant/ubus.h
4710@@ -0,0 +1,55 @@
4711+/*
4712+ * wpa_supplicant / ubus support
4713+ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
4714+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
4715+ *
4716+ * This software may be distributed under the terms of the BSD license.
4717+ * See README for more details.
4718+ */
4719+#ifndef __WPAS_UBUS_H
4720+#define __WPAS_UBUS_H
4721+
4722+struct wpa_supplicant;
4723+struct wpa_global;
4724+
4725+#include "wps_supplicant.h"
4726+
4727+#ifdef UBUS_SUPPORT
4728+#include <libubus.h>
4729+
4730+struct wpas_ubus_bss {
4731+ struct ubus_object obj;
4732+};
4733+
4734+void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
4735+void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
4736+
4737+#ifdef CONFIG_WPS
4738+void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
4739+#endif
4740+
4741+#else
4742+struct wpas_ubus_bss {};
4743+
4744+static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
4745+{
4746+}
4747+
4748+static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
4749+{
4750+}
4751+
4752+static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
4753+{
4754+}
4755+
4756+static inline void wpas_ubus_add(struct wpa_global *global)
4757+{
4758+}
4759+
4760+static inline void wpas_ubus_free(struct wpa_global *global)
4761+{
4762+}
4763+#endif
4764+
4765+#endif
4766diff --git a/wpa_supplicant/ucode.c b/wpa_supplicant/ucode.c
4767new file mode 100644
4768index 000000000..397f85bde
4769--- /dev/null
4770+++ b/wpa_supplicant/ucode.c
4771@@ -0,0 +1,299 @@
4772+#include "utils/includes.h"
4773+#include "utils/common.h"
4774+#include "utils/ucode.h"
4775+#include "drivers/driver.h"
4776+#include "ap/hostapd.h"
4777+#include "wpa_supplicant_i.h"
4778+#include "wps_supplicant.h"
4779+#include "bss.h"
4780+#include "ucode.h"
4781+
4782+static struct wpa_global *wpa_global;
4783+static uc_resource_type_t *global_type, *iface_type;
4784+static uc_value_t *global, *iface_registry;
4785+static uc_vm_t *vm;
4786+
4787+static uc_value_t *
4788+wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
4789+{
4790+ uc_value_t *val;
4791+
4792+ if (wpa_s->ucode.idx)
4793+ return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
4794+
4795+ val = uc_resource_new(iface_type, wpa_s);
4796+ wpa_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
4797+
4798+ return val;
4799+}
4800+
4801+static void
4802+wpas_ucode_update_interfaces(void)
4803+{
4804+ uc_value_t *ifs = ucv_object_new(vm);
4805+ struct wpa_supplicant *wpa_s;
4806+ int i;
4807+
4808+ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
4809+ ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
4810+
4811+ ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
4812+ ucv_gc(vm);
4813+}
4814+
4815+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
4816+{
4817+ uc_value_t *val;
4818+
4819+ if (wpa_ucode_call_prepare("iface_add"))
4820+ return;
4821+
4822+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
4823+ uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
4824+ ucv_put(wpa_ucode_call(2));
4825+ ucv_gc(vm);
4826+}
4827+
4828+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
4829+{
4830+ uc_value_t *val;
4831+
4832+ val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
4833+ if (!val)
4834+ return;
4835+
4836+ wpa_s->ucode.idx = 0;
4837+ if (wpa_ucode_call_prepare("iface_remove"))
4838+ return;
4839+
4840+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
4841+ uc_value_push(ucv_get(val));
4842+ ucv_put(wpa_ucode_call(2));
4843+ ucv_gc(vm);
4844+}
4845+
4846+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
4847+{
4848+ const char *state;
4849+ uc_value_t *val;
4850+
4851+ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
4852+ if (!val)
4853+ return;
4854+
4855+ if (wpa_ucode_call_prepare("state"))
4856+ return;
4857+
4858+ state = wpa_supplicant_state_txt(wpa_s->wpa_state);
4859+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
4860+ uc_value_push(ucv_get(val));
4861+ uc_value_push(ucv_get(ucv_string_new(state)));
4862+ ucv_put(wpa_ucode_call(3));
4863+ ucv_gc(vm);
4864+}
4865+
4866+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
4867+{
4868+ const char *state;
4869+ uc_value_t *val;
4870+
4871+ if (event != EVENT_CH_SWITCH_STARTED)
4872+ return;
4873+
4874+ val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
4875+ if (!val)
4876+ return;
4877+
4878+ if (wpa_ucode_call_prepare("event"))
4879+ return;
4880+
4881+ uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
4882+ uc_value_push(ucv_get(val));
4883+ uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
4884+ val = ucv_object_new(vm);
4885+ uc_value_push(ucv_get(val));
4886+
4887+ if (event == EVENT_CH_SWITCH_STARTED) {
4888+ ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
4889+ ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
4890+ ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
4891+ ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
4892+ ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
4893+ }
4894+
4895+ ucv_put(wpa_ucode_call(4));
4896+ ucv_gc(vm);
4897+}
4898+
4899+static const char *obj_stringval(uc_value_t *obj, const char *name)
4900+{
4901+ uc_value_t *val = ucv_object_get(obj, name, NULL);
4902+
4903+ return ucv_string_get(val);
4904+}
4905+
4906+static uc_value_t *
4907+uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
4908+{
4909+ uc_value_t *info = uc_fn_arg(0);
4910+ uc_value_t *driver = ucv_object_get(info, "driver", NULL);
4911+ uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
4912+ uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
4913+ uc_value_t *config = ucv_object_get(info, "config", NULL);
4914+ uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
4915+ struct wpa_interface iface;
4916+ int ret = -1;
4917+
4918+ if (ucv_type(info) != UC_OBJECT)
4919+ goto out;
4920+
4921+ iface = (struct wpa_interface){
4922+ .driver = "nl80211",
4923+ .ifname = ucv_string_get(ifname),
4924+ .bridge_ifname = ucv_string_get(bridge),
4925+ .confname = ucv_string_get(config),
4926+ .ctrl_interface = ucv_string_get(ctrl),
4927+ };
4928+
4929+ if (driver) {
4930+ const char *drvname;
4931+ if (ucv_type(driver) != UC_STRING)
4932+ goto out;
4933+
4934+ iface.driver = NULL;
4935+ drvname = ucv_string_get(driver);
4936+ for (int i = 0; wpa_drivers[i]; i++) {
4937+ if (!strcmp(drvname, wpa_drivers[i]->name))
4938+ iface.driver = wpa_drivers[i]->name;
4939+ }
4940+
4941+ if (!iface.driver)
4942+ goto out;
4943+ }
4944+
4945+ if (!iface.ifname || !iface.confname)
4946+ goto out;
4947+
4948+ ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
4949+ wpas_ucode_update_interfaces();
4950+
4951+out:
4952+ return ucv_int64_new(ret);
4953+}
4954+
4955+static uc_value_t *
4956+uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
4957+{
4958+ struct wpa_supplicant *wpa_s = NULL;
4959+ uc_value_t *ifname_arg = uc_fn_arg(0);
4960+ const char *ifname = ucv_string_get(ifname_arg);
4961+ int ret = -1;
4962+
4963+ if (!ifname)
4964+ goto out;
4965+
4966+ for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
4967+ if (!strcmp(wpa_s->ifname, ifname))
4968+ break;
4969+
4970+ if (!wpa_s)
4971+ goto out;
4972+
4973+ ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
4974+ wpas_ucode_update_interfaces();
4975+
4976+out:
4977+ return ucv_int64_new(ret);
4978+}
4979+
4980+static uc_value_t *
4981+uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
4982+{
4983+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
4984+ struct wpa_bss *bss;
4985+ uc_value_t *ret, *val;
4986+
4987+ if (!wpa_s)
4988+ return NULL;
4989+
4990+ ret = ucv_object_new(vm);
4991+
4992+ val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
4993+ ucv_object_add(ret, "state", ucv_get(val));
4994+
4995+ bss = wpa_s->current_bss;
4996+ if (bss) {
4997+ int sec_chan = 0;
4998+ const u8 *ie;
4999+
5000+ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
5001+ if (ie && ie[1] >= 2) {
5002+ const struct ieee80211_ht_operation *ht_oper;
5003+ int sec;
5004+
5005+ ht_oper = (const void *) (ie + 2);
5006+ sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
5007+ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
5008+ sec_chan = 1;
5009+ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
5010+ sec_chan = -1;
5011+ }
5012+
5013+ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
5014+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
5015+ }
5016+
5017+#ifdef CONFIG_MESH
5018+ if (wpa_s->ifmsh) {
5019+ struct hostapd_iface *ifmsh = wpa_s->ifmsh;
5020+
5021+ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel));
5022+ ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq));
5023+ }
5024+#endif
5025+
5026+ return ret;
5027+}
5028+
5029+int wpas_ucode_init(struct wpa_global *gl)
5030+{
5031+ static const uc_function_list_t global_fns[] = {
5032+ { "printf", uc_wpa_printf },
5033+ { "getpid", uc_wpa_getpid },
5034+ { "add_iface", uc_wpas_add_iface },
5035+ { "remove_iface", uc_wpas_remove_iface },
5036+ { "udebug_set", uc_wpa_udebug_set },
5037+ };
5038+ static const uc_function_list_t iface_fns[] = {
5039+ { "status", uc_wpas_iface_status },
5040+ };
5041+ uc_value_t *data, *proto;
5042+
5043+ wpa_global = gl;
5044+ vm = wpa_ucode_create_vm();
5045+
5046+ global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
5047+ iface_type = uc_type_declare(vm, "wpas.iface", iface_fns, NULL);
5048+
5049+ iface_registry = ucv_array_new(vm);
5050+ uc_vm_registry_set(vm, "wpas.iface_registry", iface_registry);
5051+
5052+ global = wpa_ucode_global_init("wpas", global_type);
5053+
5054+ if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
5055+ goto free_vm;
5056+
5057+ ucv_gc(vm);
5058+ return 0;
5059+
5060+free_vm:
5061+ wpa_ucode_free_vm();
5062+ return -1;
5063+}
5064+
5065+void wpas_ucode_free(void)
5066+{
5067+ if (wpa_ucode_call_prepare("shutdown") == 0)
5068+ ucv_put(wpa_ucode_call(0));
5069+ wpa_ucode_free_vm();
5070+}
5071diff --git a/wpa_supplicant/ucode.h b/wpa_supplicant/ucode.h
5072new file mode 100644
5073index 000000000..a429a0ed8
5074--- /dev/null
5075+++ b/wpa_supplicant/ucode.h
5076@@ -0,0 +1,49 @@
5077+#ifndef __WPAS_UCODE_H
5078+#define __WPAS_UCODE_H
5079+
5080+#include "utils/ucode.h"
5081+
5082+struct wpa_global;
5083+union wpa_event_data;
5084+struct wpa_supplicant;
5085+
5086+struct wpas_ucode_bss {
5087+#ifdef UCODE_SUPPORT
5088+ unsigned int idx;
5089+#endif
5090+};
5091+
5092+#ifdef UCODE_SUPPORT
5093+int wpas_ucode_init(struct wpa_global *gl);
5094+void wpas_ucode_free(void);
5095+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
5096+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
5097+void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
5098+void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
5099+#else
5100+static inline int wpas_ucode_init(struct wpa_global *gl)
5101+{
5102+ return -EINVAL;
5103+}
5104+static inline void wpas_ucode_free(void)
5105+{
5106+}
5107+static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
5108+{
5109+}
5110+
5111+static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
5112+{
5113+}
5114+
5115+static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
5116+{
5117+}
5118+
5119+static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
5120+{
5121+}
5122+
5123+#endif
5124+
5125+#endif
5126--
51272.39.2
5128