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