diff options
Diffstat (limited to 'package/network/services/hostapd/src')
10 files changed, 2335 insertions, 324 deletions
diff --git a/package/network/services/hostapd/src/hostapd/radius.c b/package/network/services/hostapd/src/hostapd/radius.c new file mode 100644 index 0000000000..362a22c276 --- /dev/null +++ b/package/network/services/hostapd/src/hostapd/radius.c @@ -0,0 +1,715 @@ +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "crypto/tls.h" + +#include "ap/ap_config.h" +#include "eap_server/eap.h" +#include "radius/radius.h" +#include "radius/radius_server.h" +#include "eap_register.h" + +#include <libubox/blobmsg_json.h> +#include <libubox/blobmsg.h> +#include <libubox/avl.h> +#include <libubox/avl-cmp.h> +#include <libubox/kvlist.h> + +#include <sys/stat.h> +#include <fnmatch.h> + +#define VENDOR_ID_WISPR 14122 +#define VENDOR_ATTR_SIZE 6 + +struct radius_parse_attr_data { + unsigned int vendor; + u8 type; + int size; + char format; + const char *data; +}; + +struct radius_parse_attr_state { + struct hostapd_radius_attr *prev; + struct hostapd_radius_attr *attr; + struct wpabuf *buf; + void *attrdata; +}; + +struct radius_user_state { + struct avl_node node; + struct eap_user data; +}; + +struct radius_user_data { + struct kvlist users; + struct avl_tree user_state; + struct blob_attr *wildcard; +}; + +struct radius_state { + struct radius_server_data *radius; + struct eap_config eap; + + struct radius_user_data phase1, phase2; + const char *user_file; + time_t user_file_ts; + + int n_attrs; + struct hostapd_radius_attr *attrs; +}; + +struct radius_config { + struct tls_connection_params tls; + struct radius_server_conf radius; +}; + +enum { + USER_ATTR_PASSWORD, + USER_ATTR_HASH, + USER_ATTR_SALT, + USER_ATTR_METHODS, + USER_ATTR_RADIUS, + USER_ATTR_VLAN, + USER_ATTR_MAX_RATE_UP, + USER_ATTR_MAX_RATE_DOWN, + __USER_ATTR_MAX +}; + +static void radius_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + wpa_printf(MSG_DEBUG, "radius: remote certificate verification success"); + break; + case TLS_CERT_CHAIN_FAILURE: + wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'", + data->cert_fail.reason, + data->cert_fail.depth, + data->cert_fail.subject, + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s", + data->peer_cert.depth, + data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A", + data->peer_cert.subject); + break; + case TLS_ALERT: + if (data->alert.is_local) + wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s", + data->alert.description); + else + wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s", + data->alert.description); + break; + case TLS_UNSAFE_RENEGOTIATION_DISABLED: + /* Not applicable to TLS server */ + break; + } +} + +static void radius_userdata_init(struct radius_user_data *u) +{ + kvlist_init(&u->users, kvlist_blob_len); + avl_init(&u->user_state, avl_strcmp, false, NULL); +} + +static void radius_userdata_free(struct radius_user_data *u) +{ + struct radius_user_state *s, *tmp; + + kvlist_free(&u->users); + free(u->wildcard); + u->wildcard = NULL; + avl_remove_all_elements(&u->user_state, s, node, tmp) + free(s); +} + +static void +radius_userdata_load(struct radius_user_data *u, struct blob_attr *data) +{ + enum { + USERSTATE_USERS, + USERSTATE_WILDCARD, + __USERSTATE_MAX, + }; + static const struct blobmsg_policy policy[__USERSTATE_MAX] = { + [USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE }, + [USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY }, + }; + struct blob_attr *tb[__USERSTATE_MAX], *cur; + int rem; + + if (!data) + return; + + blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data)); + + blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem) + kvlist_set(&u->users, blobmsg_name(cur), cur); + + if (tb[USERSTATE_WILDCARD]) + u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]); +} + +static void +load_userfile(struct radius_state *s) +{ + enum { + USERDATA_PHASE1, + USERDATA_PHASE2, + __USERDATA_MAX + }; + static const struct blobmsg_policy policy[__USERDATA_MAX] = { + [USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE }, + [USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE }, + }; + struct blob_attr *tb[__USERDATA_MAX], *cur; + static struct blob_buf b; + struct stat st; + int rem; + + if (stat(s->user_file, &st)) + return; + + if (s->user_file_ts == st.st_mtime) + return; + + s->user_file_ts = st.st_mtime; + radius_userdata_free(&s->phase1); + radius_userdata_free(&s->phase2); + + blob_buf_init(&b, 0); + blobmsg_add_json_from_file(&b, s->user_file); + blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head)); + radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]); + radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]); + + blob_buf_free(&b); +} + +static struct blob_attr * +radius_user_get(struct radius_user_data *s, const char *name) +{ + struct blob_attr *cur; + int rem; + + cur = kvlist_get(&s->users, name); + if (cur) + return cur; + + blobmsg_for_each_attr(cur, s->wildcard, rem) { + static const struct blobmsg_policy policy = { + "name", BLOBMSG_TYPE_STRING + }; + struct blob_attr *pattern; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) + continue; + + blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur)); + if (!name) + continue; + + if (!fnmatch(blobmsg_get_string(pattern), name, 0)) + return cur; + } + + return NULL; +} + +static struct radius_parse_attr_data * +radius_parse_attr(struct blob_attr *attr) +{ + static const struct blobmsg_policy policy[4] = { + { .type = BLOBMSG_TYPE_INT32 }, + { .type = BLOBMSG_TYPE_INT32 }, + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_STRING }, + }; + static struct radius_parse_attr_data data; + struct blob_attr *tb[4]; + const char *format; + + blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr)); + + if (!tb[0] || !tb[1] || !tb[2] || !tb[3]) + return NULL; + + format = blobmsg_get_string(tb[2]); + if (strlen(format) != 1) + return NULL; + + data.vendor = blobmsg_get_u32(tb[0]); + data.type = blobmsg_get_u32(tb[1]); + data.format = format[0]; + data.data = blobmsg_get_string(tb[3]); + data.size = strlen(data.data); + + switch (data.format) { + case 's': + break; + case 'x': + if (data.size & 1) + return NULL; + data.size /= 2; + break; + case 'd': + data.size = 4; + break; + default: + return NULL; + } + + return &data; +} + +static void +radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size) +{ + struct blob_attr *data = tb[USER_ATTR_RADIUS]; + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, data, rem) { + struct radius_parse_attr_data *data; + size_t prev = *attr_size; + + data = radius_parse_attr(cur); + if (!data) + continue; + + *attr_size += data->size; + if (data->vendor) + *attr_size += VENDOR_ATTR_SIZE; + + (*n_attr)++; + } + + *n_attr += !!tb[USER_ATTR_VLAN] * 3 + + !!tb[USER_ATTR_MAX_RATE_UP] + + !!tb[USER_ATTR_MAX_RATE_DOWN]; + *attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) + + !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) + + !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE); +} + +static void * +radius_add_attr(struct radius_parse_attr_state *state, + u32 vendor, u8 type, u8 len) +{ + struct hostapd_radius_attr *attr; + struct wpabuf *buf; + void *val; + + val = state->attrdata; + + buf = state->buf++; + buf->buf = val; + + attr = state->attr++; + attr->val = buf; + attr->type = type; + + if (state->prev) + state->prev->next = attr; + state->prev = attr; + + if (vendor) { + u8 *vendor_hdr = val + 4; + + WPA_PUT_BE32(val, vendor); + vendor_hdr[0] = type; + vendor_hdr[1] = len + 2; + + len += VENDOR_ATTR_SIZE; + val += VENDOR_ATTR_SIZE; + attr->type = RADIUS_ATTR_VENDOR_SPECIFIC; + } + + buf->size = buf->used = len; + state->attrdata += len; + + return val; +} + +static void +radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state) +{ + struct blob_attr *data = tb[USER_ATTR_RADIUS]; + struct hostapd_radius_attr *prev = NULL; + struct blob_attr *cur; + int len, rem; + void *val; + + if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) { + char buf[5]; + + val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4); + WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN); + + val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4); + WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802); + + len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur)); + val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len); + memcpy(val, buf, len); + } + + if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) { + val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4); + WPA_PUT_BE32(val, blobmsg_get_u32(cur)); + } + + if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) { + val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4); + WPA_PUT_BE32(val, blobmsg_get_u32(cur)); + } + + blobmsg_for_each_attr(cur, data, rem) { + struct radius_parse_attr_data *data; + void *val; + int size; + + data = radius_parse_attr(cur); + if (!data) + continue; + + val = radius_add_attr(state, data->vendor, data->type, data->size); + switch (data->format) { + case 's': + memcpy(val, data->data, data->size); + break; + case 'x': + hexstr2bin(data->data, val, data->size); + break; + case 'd': + WPA_PUT_BE32(val, atoi(data->data)); + break; + } + } +} + +static void +radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data) +{ + struct blob_attr *cur; + int rem, n = 0; + + if (!data) + return; + + blobmsg_for_each_attr(cur, data, rem) { + const char *method; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (n == EAP_MAX_METHODS) + break; + + method = blobmsg_get_string(cur); + eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor); + if (eap->methods[n].vendor == EAP_VENDOR_IETF && + eap->methods[n].method == EAP_TYPE_NONE) { + if (!strcmp(method, "TTLS-PAP")) { + eap->ttls_auth |= EAP_TTLS_AUTH_PAP; + continue; + } + if (!strcmp(method, "TTLS-CHAP")) { + eap->ttls_auth |= EAP_TTLS_AUTH_CHAP; + continue; + } + if (!strcmp(method, "TTLS-MSCHAP")) { + eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP; + continue; + } + if (!strcmp(method, "TTLS-MSCHAPV2")) { + eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2; + continue; + } + } + n++; + } +} + +static struct eap_user * +radius_user_get_state(struct radius_user_data *u, struct blob_attr *data, + const char *id) +{ + static const struct blobmsg_policy policy[__USER_ATTR_MAX] = { + [USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING }, + [USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING }, + [USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING }, + [USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY }, + [USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY }, + [USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 }, + [USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 }, + [USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 }, + }; + struct blob_attr *tb[__USER_ATTR_MAX], *cur; + char *password_buf, *salt_buf, *name_buf; + struct radius_parse_attr_state astate = {}; + struct hostapd_radius_attr *attr; + struct radius_user_state *state; + int pw_len = 0, salt_len = 0; + struct eap_user *eap; + struct wpabuf *val; + size_t attrsize = 0; + void *attrdata; + int n_attr = 0; + + state = avl_find_element(&u->user_state, id, state, node); + if (state) + return &state->data; + + blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data)); + + if ((cur = tb[USER_ATTR_SALT]) != NULL) + salt_len = strlen(blobmsg_get_string(cur)) / 2; + if ((cur = tb[USER_ATTR_HASH]) != NULL) + pw_len = strlen(blobmsg_get_string(cur)) / 2; + else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL) + pw_len = blobmsg_len(cur) - 1; + radius_count_attrs(tb, &n_attr, &attrsize); + + state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1, + &password_buf, pw_len, + &salt_buf, salt_len, + &astate.attr, n_attr * sizeof(*astate.attr), + &astate.buf, n_attr * sizeof(*astate.buf), + &astate.attrdata, attrsize); + eap = &state->data; + eap->salt = salt_len ? salt_buf : NULL; + eap->salt_len = salt_len; + eap->password = pw_len ? password_buf : NULL; + eap->password_len = pw_len; + eap->force_version = -1; + + if ((cur = tb[USER_ATTR_SALT]) != NULL) + hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len); + if ((cur = tb[USER_ATTR_PASSWORD]) != NULL) + memcpy(password_buf, blobmsg_get_string(cur), pw_len); + else if ((cur = tb[USER_ATTR_HASH]) != NULL) { + hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len); + eap->password_hash = 1; + } + radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]); + + if (n_attr > 0) { + cur = tb[USER_ATTR_RADIUS]; + eap->accept_attr = astate.attr; + radius_parse_attrs(tb, &astate); + } + + state->node.key = strcpy(name_buf, id); + avl_insert(&u->user_state, &state->node); + + return &state->data; + +free: + free(state); + return NULL; +} + +static int radius_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct radius_state *s = ctx; + struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1; + struct blob_attr *entry; + struct eap_user *data; + char *id; + + if (identity_len > 512) + return -1; + + load_userfile(s); + + id = alloca(identity_len + 1); + memcpy(id, identity, identity_len); + id[identity_len] = 0; + + entry = radius_user_get(u, id); + if (!entry) + return -1; + + if (!user) + return 0; + + data = radius_user_get_state(u, entry, id); + if (!data) + return -1; + + *user = *data; + if (user->password_len > 0) + user->password = os_memdup(user->password, user->password_len); + if (user->salt_len > 0) + user->salt = os_memdup(user->salt, user->salt_len); + user->phase2 = phase2; + + return 0; +} + +static int radius_setup(struct radius_state *s, struct radius_config *c) +{ + struct eap_config *eap = &s->eap; + struct tls_config conf = { + .event_cb = radius_tls_event, + .tls_flags = TLS_CONN_DISABLE_TLSv1_3, + .cb_ctx = s, + }; + + eap->eap_server = 1; + eap->max_auth_rounds = 100; + eap->max_auth_rounds_short = 50; + eap->ssl_ctx = tls_init(&conf); + if (!eap->ssl_ctx) { + wpa_printf(MSG_INFO, "TLS init failed\n"); + return 1; + } + + if (tls_global_set_params(eap->ssl_ctx, &c->tls)) { + wpa_printf(MSG_INFO, "failed to set TLS parameters\n"); + return 1; + } + + c->radius.eap_cfg = eap; + c->radius.conf_ctx = s; + c->radius.get_eap_user = radius_get_eap_user; + s->radius = radius_server_init(&c->radius); + if (!s->radius) { + wpa_printf(MSG_INFO, "failed to initialize radius server\n"); + return 1; + } + + return 0; +} + +static int radius_init(struct radius_state *s) +{ + memset(s, 0, sizeof(*s)); + radius_userdata_init(&s->phase1); + radius_userdata_init(&s->phase2); +} + +static void radius_deinit(struct radius_state *s) +{ + if (s->radius) + radius_server_deinit(s->radius); + + if (s->eap.ssl_ctx) + tls_deinit(s->eap.ssl_ctx); + + radius_userdata_free(&s->phase1); + radius_userdata_free(&s->phase2); +} + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s <options>\n", + progname); +} + +int radius_main(int argc, char **argv) +{ + static struct radius_state state = {}; + static struct radius_config config = {}; + const char *progname = argv[0]; + int ret = 0; + int ch; + + wpa_debug_setup_stdout(); + wpa_debug_level = 0; + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return 1; + } + + eap_server_register_methods(); + radius_init(&state); + + while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) { + switch (ch) { + case '6': + config.radius.ipv6 = 1; + break; + case 'C': + config.tls.ca_cert = optarg; + break; + case 'c': + if (config.tls.client_cert2) + return usage(progname); + + if (config.tls.client_cert) + config.tls.client_cert2 = optarg; + else + config.tls.client_cert = optarg; + break; + case 'd': + config.tls.dh_file = optarg; + break; + case 'i': + state.eap.server_id = optarg; + state.eap.server_id_len = strlen(optarg); + break; + case 'k': + if (config.tls.private_key2) + return usage(progname); + + if (config.tls.private_key) + config.tls.private_key2 = optarg; + else + config.tls.private_key = optarg; + break; + case 'K': + if (config.tls.private_key_passwd2) + return usage(progname); + + if (config.tls.private_key_passwd) + config.tls.private_key_passwd2 = optarg; + else + config.tls.private_key_passwd = optarg; + break; + case 'p': + config.radius.auth_port = atoi(optarg); + break; + case 'P': + config.radius.acct_port = atoi(optarg); + break; + case 's': + config.radius.client_file = optarg; + break; + case 'u': + state.user_file = optarg; + break; + default: + return usage(progname); + } + } + + if (!config.tls.client_cert || !config.tls.private_key || + !config.radius.client_file || !state.eap.server_id || + !state.user_file) { + wpa_printf(MSG_INFO, "missing options\n"); + goto out; + } + + ret = radius_setup(&state, &config); + if (ret) + goto out; + + load_userfile(&state); + eloop_run(); + +out: + radius_deinit(&state); + os_program_deinit(); + + return ret; +} diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c index ddd86447eb..6ff2257c32 100644 --- a/package/network/services/hostapd/src/src/ap/ubus.c +++ b/package/network/services/hostapd/src/src/ap/ubus.c @@ -29,11 +29,6 @@ static struct ubus_context *ctx; static struct blob_buf b; static int ctx_ref; -static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj) -{ - return container_of(obj, struct hapd_interfaces, ubus); -} - static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj) { return container_of(obj, struct hostapd_data, ubus.obj); @@ -44,12 +39,6 @@ struct ubus_banned_client { u8 addr[ETH_ALEN]; }; -static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct ubus_context *ctx = eloop_ctx; - ubus_handle_event(ctx); -} - static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx) { if (ubus_reconnect(ctx, NULL)) { @@ -57,12 +46,12 @@ static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx) return; } - eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL); + ubus_add_uloop(ctx); } static void hostapd_ubus_connection_lost(struct ubus_context *ctx) { - eloop_unregister_read_sock(ctx->sock.fd); + uloop_fd_delete(&ctx->sock); eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL); } @@ -71,12 +60,14 @@ static bool hostapd_ubus_init(void) if (ctx) return true; + eloop_add_uloop(); ctx = ubus_connect(NULL); if (!ctx) return false; ctx->connection_lost = hostapd_ubus_connection_lost; - eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL); + ubus_add_uloop(ctx); + return true; } @@ -94,7 +85,7 @@ static void hostapd_ubus_ref_dec(void) if (ctx_ref) return; - eloop_unregister_read_sock(ctx->sock.fd); + uloop_fd_delete(&ctx->sock); ubus_free(ctx); ctx = NULL; } @@ -127,38 +118,6 @@ static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *ev free(event_type); } -static void hostapd_send_procd_event(char *bssname, char *event) -{ - char *name, *s; - uint32_t id; - void *v; - - if (!ctx || ubus_lookup_id(ctx, "service", &id)) - return; - - if (asprintf(&name, "hostapd.%s.%s", bssname, event) < 0) - return; - - blob_buf_init(&b, 0); - - s = blobmsg_alloc_string_buffer(&b, "type", strlen(name) + 1); - sprintf(s, "%s", name); - blobmsg_add_string_buffer(&b); - - v = blobmsg_open_table(&b, "data"); - blobmsg_close_table(&b, v); - - ubus_invoke(ctx, id, "event", b.head, NULL, NULL, 1000); - - free(name); -} - -static void hostapd_send_shared_event(struct ubus_object *obj, char *bssname, char *event) -{ - hostapd_send_procd_event(bssname, event); - hostapd_notify_ubus(obj, bssname, event); -} - static void hostapd_bss_del_ban(void *eloop_data, void *user_ctx) { @@ -203,10 +162,8 @@ hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj, struct blob_attr *msg) { struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); - int ret = hostapd_reload_config(hapd->iface, 1); - hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "reload"); - return ret; + return hostapd_reload_config(hapd->iface); } @@ -687,68 +644,6 @@ enum { __CONFIG_MAX }; -static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = { - [CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING }, - [CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING }, -}; - -static int -hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__CONFIG_MAX]; - struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj); - char buf[128]; - - blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE]) - return UBUS_STATUS_INVALID_ARGUMENT; - - snprintf(buf, sizeof(buf), "bss_config=%s:%s", - blobmsg_get_string(tb[CONFIG_IFACE]), - blobmsg_get_string(tb[CONFIG_FILE])); - - if (hostapd_add_iface(interfaces, buf)) - return UBUS_STATUS_INVALID_ARGUMENT; - - blob_buf_init(&b, 0); - blobmsg_add_u32(&b, "pid", getpid()); - ubus_send_reply(ctx, req, b.head); - - return UBUS_STATUS_OK; -} - -enum { - CONFIG_REM_IFACE, - __CONFIG_REM_MAX -}; - -static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = { - [CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING }, -}; - -static int -hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__CONFIG_REM_MAX]; - struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj); - char buf[128]; - - blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[CONFIG_REM_IFACE]) - return UBUS_STATUS_INVALID_ARGUMENT; - - if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE]))) - return UBUS_STATUS_INVALID_ARGUMENT; - - return UBUS_STATUS_OK; -} - enum { CSA_FREQ, CSA_BCN_COUNT, @@ -1669,10 +1564,61 @@ hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj, } #endif +#ifdef CONFIG_TAXONOMY +static const struct blobmsg_policy addr_policy[] = { + { "address", BLOBMSG_TYPE_STRING } +}; + +static bool +hostapd_add_b64_data(const char *name, const struct wpabuf *buf) +{ + char *str; + + if (!buf) + return false; + + str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf))); + b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf))); + blobmsg_add_string_buffer(&b); + + return true; +} + +static int +hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj); + struct blob_attr *tb; + struct sta_info *sta; + u8 addr[ETH_ALEN]; + + blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg)); + + if (!tb || hwaddr_aton(blobmsg_data(tb), addr)) + return UBUS_STATUS_INVALID_ARGUMENT; + + sta = ap_get_sta(hapd, addr); + if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy)) + return UBUS_STATUS_NOT_FOUND; + + blob_buf_init(&b, 0); + hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy); + hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy); + ubus_send_reply(ctx, req, b.head); + + return 0; +} +#endif + static const struct ubus_method bss_methods[] = { UBUS_METHOD_NOARG("reload", hostapd_bss_reload), UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients), +#ifdef CONFIG_TAXONOMY + UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy), +#endif UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status), UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy), #ifdef CONFIG_AIRTIME_POLICY @@ -1734,8 +1680,6 @@ void hostapd_ubus_add_bss(struct hostapd_data *hapd) obj->n_methods = bss_object_type.n_methods; ret = ubus_add_object(ctx, obj); hostapd_ubus_ref_inc(); - - hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "add"); } void hostapd_ubus_free_bss(struct hostapd_data *hapd) @@ -1751,8 +1695,6 @@ void hostapd_ubus_free_bss(struct hostapd_data *hapd) if (!ctx) return; - hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "remove"); - if (obj->id) { ubus_remove_object(ctx, obj); hostapd_ubus_ref_dec(); @@ -1798,47 +1740,6 @@ void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vl hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove"); } -static const struct ubus_method daemon_methods[] = { - UBUS_METHOD("config_add", hostapd_config_add, config_add_policy), - UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy), -}; - -static struct ubus_object_type daemon_object_type = - UBUS_OBJECT_TYPE("hostapd", daemon_methods); - -void hostapd_ubus_add(struct hapd_interfaces *interfaces) -{ - struct ubus_object *obj = &interfaces->ubus; - int ret; - - if (!hostapd_ubus_init()) - return; - - obj->name = strdup("hostapd"); - - obj->type = &daemon_object_type; - obj->methods = daemon_object_type.methods; - obj->n_methods = daemon_object_type.n_methods; - ret = ubus_add_object(ctx, obj); - hostapd_ubus_ref_inc(); -} - -void hostapd_ubus_free(struct hapd_interfaces *interfaces) -{ - struct ubus_object *obj = &interfaces->ubus; - char *name = (char *) obj->name; - - if (!ctx) - return; - - if (obj->id) { - ubus_remove_object(ctx, obj); - hostapd_ubus_ref_dec(); - } - - free(name); -} - struct ubus_event_req { struct ubus_notify_request nreq; int resp; diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c new file mode 100644 index 0000000000..e79f2420c0 --- /dev/null +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -0,0 +1,808 @@ +#include <sys/un.h> + +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/ucode.h" +#include "hostapd.h" +#include "beacon.h" +#include "hw_features.h" +#include "ap_drv_ops.h" +#include "dfs.h" +#include "acs.h" +#include <libubox/uloop.h> + +static uc_resource_type_t *global_type, *bss_type, *iface_type; +static struct hapd_interfaces *interfaces; +static uc_value_t *global, *bss_registry, *iface_registry; +static uc_vm_t *vm; + +static uc_value_t * +hostapd_ucode_bss_get_uval(struct hostapd_data *hapd) +{ + uc_value_t *val; + + if (hapd->ucode.idx) + return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx); + + val = uc_resource_new(bss_type, hapd); + hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val); + + return val; +} + +static uc_value_t * +hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd) +{ + uc_value_t *val; + + if (hapd->ucode.idx) + return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx); + + val = uc_resource_new(iface_type, hapd); + hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val); + + return val; +} + +static void +hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss) +{ + uc_value_t *list; + int i; + + list = ucv_array_new(vm); + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + uc_value_t *val = hostapd_ucode_bss_get_uval(hapd); + + ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface))); + ucv_object_add(bss, hapd->conf->iface, ucv_get(val)); + } + ucv_object_add(if_bss, iface->phy, ucv_get(list)); +} + +static void +hostapd_ucode_update_interfaces(void) +{ + uc_value_t *ifs = ucv_object_new(vm); + uc_value_t *if_bss = ucv_array_new(vm); + uc_value_t *bss = ucv_object_new(vm); + int i; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface))); + hostapd_ucode_update_bss_list(iface, if_bss, bss); + } + + ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs)); + ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss)); + ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss)); + ucv_gc(vm); +} + +static uc_value_t * +uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *iface = uc_fn_arg(0); + int ret; + + if (ucv_type(iface) != UC_STRING) + return ucv_int64_new(-1); + + ret = hostapd_add_iface(interfaces, ucv_string_get(iface)); + hostapd_ucode_update_interfaces(); + + return ucv_int64_new(ret); +} + +static uc_value_t * +uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *iface = uc_fn_arg(0); + + if (ucv_type(iface) != UC_STRING) + return NULL; + + hostapd_remove_iface(interfaces, ucv_string_get(iface)); + hostapd_ucode_update_interfaces(); + + return NULL; +} + +static struct hostapd_vlan * +bss_conf_find_vlan(struct hostapd_bss_config *bss, int id) +{ + struct hostapd_vlan *vlan; + + for (vlan = bss->vlan; vlan; vlan = vlan->next) + if (vlan->vlan_id == id) + return vlan; + + return NULL; +} + +static int +bss_conf_rename_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + const char *ifname) +{ + if (!strcmp(ifname, vlan->ifname)) + return 0; + + hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, vlan->ifname, ifname); + os_strlcpy(vlan->ifname, ifname, sizeof(vlan->ifname)); + + return 0; +} + +static int +bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss) +{ + struct hostapd_bss_config *old_bss = hapd->conf; + struct hostapd_vlan *vlan, *vlan_new, *wildcard; + char ifname[IFNAMSIZ + 1], vlan_ifname[IFNAMSIZ + 1], *pos; + int ret; + + vlan = bss_conf_find_vlan(old_bss, VLAN_ID_WILDCARD); + wildcard = bss_conf_find_vlan(bss, VLAN_ID_WILDCARD); + if (!!vlan != !!wildcard) + return -1; + + if (vlan && wildcard && strcmp(vlan->ifname, wildcard->ifname) != 0) + strcpy(vlan->ifname, wildcard->ifname); + else + wildcard = NULL; + + for (vlan = bss->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == VLAN_ID_WILDCARD || + vlan->dynamic_vlan > 0) + continue; + + if (!bss_conf_find_vlan(old_bss, vlan->vlan_id)) + return -1; + } + + for (vlan = old_bss->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == VLAN_ID_WILDCARD) + continue; + + if (vlan->dynamic_vlan == 0) { + vlan_new = bss_conf_find_vlan(bss, vlan->vlan_id); + if (!vlan_new) + return -1; + + if (bss_conf_rename_vlan(hapd, vlan, vlan_new->ifname)) + return -1; + + continue; + } + + if (!wildcard) + continue; + + os_strlcpy(ifname, wildcard->ifname, sizeof(ifname)); + pos = os_strchr(ifname, '#'); + if (!pos) + return -1; + + *pos++ = '\0'; + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s%d%s", + ifname, vlan->vlan_id, pos); + if (os_snprintf_error(sizeof(vlan_ifname), ret)) + return -1; + + if (bss_conf_rename_vlan(hapd, vlan, vlan_ifname)) + return -1; + } + + return 0; +} + +static uc_value_t * +uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss"); + struct hostapd_bss_config *old_bss; + struct hostapd_iface *iface; + struct hostapd_config *conf; + uc_value_t *file = uc_fn_arg(0); + uc_value_t *index = uc_fn_arg(1); + uc_value_t *files_only = uc_fn_arg(2); + unsigned int i, idx = 0; + int ret = -1; + + if (!hapd || ucv_type(file) != UC_STRING) + goto out; + + if (ucv_type(index) == UC_INTEGER) + idx = ucv_int64_get(index); + + iface = hapd->iface; + conf = interfaces->config_read_cb(ucv_string_get(file)); + if (!conf) + goto out; + + if (idx > conf->num_bss || !conf->bss[idx]) + goto free; + + if (ucv_boolean_get(files_only)) { + struct hostapd_bss_config *bss = conf->bss[idx]; + struct hostapd_bss_config *old_bss = hapd->conf; + +#define swap_field(name) \ + do { \ + void *ptr = old_bss->name; \ + old_bss->name = bss->name; \ + bss->name = ptr; \ + } while (0) + + swap_field(ssid.wpa_psk_file); + ret = bss_reload_vlans(hapd, bss); + goto done; + } + + hostapd_bss_deinit_no_free(hapd); + hostapd_drv_stop_ap(hapd); + hostapd_free_hapd_data(hapd); + + old_bss = hapd->conf; + for (i = 0; i < iface->conf->num_bss; i++) + if (iface->conf->bss[i] == hapd->conf) + iface->conf->bss[i] = conf->bss[idx]; + hapd->conf = conf->bss[idx]; + conf->bss[idx] = old_bss; + + hostapd_setup_bss(hapd, hapd == iface->bss[0], true); + hostapd_ucode_update_interfaces(); + +done: + ret = 0; +free: + hostapd_config_free(conf); +out: + return ucv_int64_new(ret); +} + +static void +hostapd_remove_iface_bss_conf(struct hostapd_config *iconf, + struct hostapd_bss_config *conf) +{ + int i; + + for (i = 0; i < iconf->num_bss; i++) + if (iconf->bss[i] == conf) + break; + + if (i == iconf->num_bss) + return; + + for (i++; i < iconf->num_bss; i++) + iconf->bss[i - 1] = iconf->bss[i]; + iconf->num_bss--; +} + + +static uc_value_t * +uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss"); + struct hostapd_iface *iface; + int i, idx; + + if (!hapd) + return NULL; + + iface = hapd->iface; + if (iface->num_bss == 1) { + wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface); + return NULL; + } + + for (idx = 0; idx < iface->num_bss; idx++) + if (iface->bss[idx] == hapd) + break; + + if (idx == iface->num_bss) + return NULL; + + for (i = idx + 1; i < iface->num_bss; i++) + iface->bss[i - 1] = iface->bss[i]; + + iface->num_bss--; + + iface->bss[0]->interface_added = 0; + hostapd_drv_set_first_bss(iface->bss[0]); + hapd->interface_added = 1; + + hostapd_drv_stop_ap(hapd); + hostapd_bss_deinit(hapd); + hostapd_remove_iface_bss_conf(iface->conf, hapd->conf); + hostapd_config_free_bss(hapd->conf); + os_free(hapd); + + hostapd_ucode_update_interfaces(); + ucv_gc(vm); + + return NULL; +} + +static uc_value_t * +uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); + struct hostapd_bss_config *bss; + struct hostapd_config *conf; + struct hostapd_data *hapd; + uc_value_t *file = uc_fn_arg(0); + uc_value_t *index = uc_fn_arg(1); + unsigned int idx = 0; + uc_value_t *ret = NULL; + + if (!iface || ucv_type(file) != UC_STRING) + goto out; + + if (ucv_type(index) == UC_INTEGER) + idx = ucv_int64_get(index); + + conf = interfaces->config_read_cb(ucv_string_get(file)); + if (!conf || idx > conf->num_bss || !conf->bss[idx]) + goto out; + + bss = conf->bss[idx]; + hapd = hostapd_alloc_bss_data(iface, iface->conf, bss); + if (!hapd) + goto out; + + hapd->driver = iface->bss[0]->driver; + hapd->drv_priv = iface->bss[0]->drv_priv; + if (interfaces->ctrl_iface_init && + interfaces->ctrl_iface_init(hapd) < 0) + goto free_hapd; + + if (iface->state == HAPD_IFACE_ENABLED && + hostapd_setup_bss(hapd, -1, true)) + goto deinit_ctrl; + + iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1, + sizeof(*iface->bss)); + iface->bss[iface->num_bss++] = hapd; + + iface->conf->bss = os_realloc_array(iface->conf->bss, + iface->conf->num_bss + 1, + sizeof(*iface->conf->bss)); + iface->conf->bss[iface->conf->num_bss] = bss; + conf->bss[idx] = NULL; + ret = hostapd_ucode_bss_get_uval(hapd); + hostapd_ucode_update_interfaces(); + goto out; + +deinit_ctrl: + if (interfaces->ctrl_iface_deinit) + interfaces->ctrl_iface_deinit(hapd); +free_hapd: + hostapd_free_hapd_data(hapd); + os_free(hapd); +out: + hostapd_config_free(conf); + return ret; +} + +static uc_value_t * +uc_hostapd_iface_set_bss_order(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); + uc_value_t *bss_list = uc_fn_arg(0); + struct hostapd_data **new_bss; + struct hostapd_bss_config **new_conf; + + if (!iface) + return NULL; + + if (ucv_type(bss_list) != UC_ARRAY || + ucv_array_length(bss_list) != iface->num_bss) + return NULL; + + new_bss = calloc(iface->num_bss, sizeof(*new_bss)); + new_conf = calloc(iface->num_bss, sizeof(*new_conf)); + for (size_t i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss; + + bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss"); + if (bss->iface != iface) + goto free; + + for (size_t k = 0; k < i; k++) + if (new_bss[k] == bss) + goto free; + + new_bss[i] = bss; + new_conf[i] = bss->conf; + } + + new_bss[0]->interface_added = 0; + for (size_t i = 1; i < iface->num_bss; i++) + new_bss[i]->interface_added = 1; + + free(iface->bss); + iface->bss = new_bss; + + free(iface->conf->bss); + iface->conf->bss = new_conf; + iface->conf->num_bss = iface->num_bss; + hostapd_drv_set_first_bss(iface->bss[0]); + + return ucv_boolean_new(true); + +free: + free(new_bss); + free(new_conf); + return NULL; +} + +static uc_value_t * +uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss"); + uc_value_t *arg = uc_fn_arg(0); + struct sockaddr_storage from = {}; + static char reply[4096]; + int reply_len; + + if (!hapd || !interfaces->ctrl_iface_recv || + ucv_type(arg) != UC_STRING) + return NULL; + + reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg), + reply, sizeof(reply), + &from, sizeof(from)); + if (reply_len < 0) + return NULL; + + if (reply_len && reply[reply_len - 1] == '\n') + reply_len--; + + return ucv_string_new_length(reply, reply_len); +} + +static uc_value_t * +uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); + int i; + + switch (iface->state) { + case HAPD_IFACE_ENABLED: + case HAPD_IFACE_DISABLED: + break; +#ifdef CONFIG_ACS + case HAPD_IFACE_ACS: + acs_cleanup(iface); + iface->scan_cb = NULL; + /* fallthrough */ +#endif + default: + hostapd_disable_iface(iface); + break; + } + + if (iface->state != HAPD_IFACE_ENABLED) + hostapd_disable_iface(iface); + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + + hostapd_drv_stop_ap(hapd); + hapd->beacon_set_done = 0; + } + + return NULL; +} + +static uc_value_t * +uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); + uc_value_t *info = uc_fn_arg(0); + struct hostapd_config *conf; + bool changed = false; + uint64_t intval; + int i; + + if (!iface) + return NULL; + + if (!info) { + iface->freq = 0; + goto out; + } + + if (ucv_type(info) != UC_OBJECT) + return NULL; + +#define UPDATE_VAL(field, name) \ + if ((intval = ucv_int64_get(ucv_object_get(info, name, NULL))) && \ + !errno && intval != conf->field) do { \ + conf->field = intval; \ + changed = true; \ + } while(0) + + conf = iface->conf; + UPDATE_VAL(op_class, "op_class"); + UPDATE_VAL(hw_mode, "hw_mode"); + UPDATE_VAL(channel, "channel"); + UPDATE_VAL(secondary_channel, "sec_channel"); + if (!changed && + (iface->bss[0]->beacon_set_done || + iface->state == HAPD_IFACE_DFS)) + return ucv_boolean_new(true); + + intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL)); + if (!errno) + hostapd_set_oper_centr_freq_seg0_idx(conf, intval); + + intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL)); + if (!errno) + hostapd_set_oper_centr_freq_seg1_idx(conf, intval); + + intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL)); + if (!errno) + hostapd_set_oper_chwidth(conf, intval); + + intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL)); + if (!errno) + iface->freq = intval; + else + iface->freq = 0; + conf->acs = 0; + +out: + switch (iface->state) { + case HAPD_IFACE_DISABLED: + break; + case HAPD_IFACE_ENABLED: + if (!hostapd_is_dfs_required(iface) || + hostapd_is_dfs_chan_available(iface)) + break; + wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface"); + /* fallthrough */ + default: + hostapd_disable_iface(iface); + break; + } + + if (conf->channel && !iface->freq) + iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel); + + if (iface->state != HAPD_IFACE_ENABLED) { + hostapd_enable_iface(iface); + return ucv_boolean_new(true); + } + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + int ret; + + hapd->conf->start_disabled = 0; + hostapd_set_freq(hapd, conf->hw_mode, iface->freq, + conf->channel, + conf->enable_edmg, + conf->edmg_channel, + conf->ieee80211n, + conf->ieee80211ac, + conf->ieee80211ax, + conf->ieee80211be, + conf->secondary_channel, + hostapd_get_oper_chwidth(conf), + hostapd_get_oper_centr_freq_seg0_idx(conf), + hostapd_get_oper_centr_freq_seg1_idx(conf)); + + ieee802_11_set_beacon(hapd); + } + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); + uc_value_t *info = uc_fn_arg(0); + struct hostapd_config *conf; + struct csa_settings csa = {}; + uint64_t intval; + int i, ret = 0; + + if (!iface || ucv_type(info) != UC_OBJECT) + return NULL; + + conf = iface->conf; + if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno) + csa.cs_count = intval; + if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno) + csa.freq_params.sec_channel_offset = intval; + + csa.freq_params.ht_enabled = conf->ieee80211n; + csa.freq_params.vht_enabled = conf->ieee80211ac; + csa.freq_params.he_enabled = conf->ieee80211ax; +#ifdef CONFIG_IEEE80211BE + csa.freq_params.eht_enabled = conf->ieee80211be; +#endif + intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL)); + if (errno) + intval = hostapd_get_oper_chwidth(conf); + if (intval) + csa.freq_params.bandwidth = 40 << intval; + else + csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20; + + if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno) + csa.freq_params.freq = intval; + if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno) + csa.freq_params.center_freq1 = intval; + if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno) + csa.freq_params.center_freq2 = intval; + + for (i = 0; i < iface->num_bss; i++) + ret = hostapd_switch_channel(iface->bss[i], &csa); + + return ucv_boolean_new(!ret); +} + +static uc_value_t * +uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss"); + uc_value_t *ifname_arg = uc_fn_arg(0); + char prev_ifname[IFNAMSIZ + 1]; + struct sta_info *sta; + const char *ifname; + int ret; + + if (!hapd || ucv_type(ifname_arg) != UC_STRING) + return NULL; + + os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname)); + ifname = ucv_string_get(ifname_arg); + + hostapd_ubus_free_bss(hapd); + if (interfaces->ctrl_iface_deinit) + interfaces->ctrl_iface_deinit(hapd); + + ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname); + if (ret) + goto out; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1]; + + if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable) + continue; + + snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid); + snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid); + hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name); + } + + if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan))) + os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan)); + os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface)); + hostapd_ubus_add_bss(hapd); + + hostapd_ucode_update_interfaces(); +out: + if (interfaces->ctrl_iface_init) + interfaces->ctrl_iface_init(hapd); + + return ret ? NULL : ucv_boolean_new(true); +} + + +int hostapd_ucode_init(struct hapd_interfaces *ifaces) +{ + static const uc_function_list_t global_fns[] = { + { "printf", uc_wpa_printf }, + { "getpid", uc_wpa_getpid }, + { "sha1", uc_wpa_sha1 }, + { "freq_info", uc_wpa_freq_info }, + { "add_iface", uc_hostapd_add_iface }, + { "remove_iface", uc_hostapd_remove_iface }, + }; + static const uc_function_list_t bss_fns[] = { + { "ctrl", uc_hostapd_bss_ctrl }, + { "set_config", uc_hostapd_bss_set_config }, + { "rename", uc_hostapd_bss_rename }, + { "delete", uc_hostapd_bss_delete }, + }; + static const uc_function_list_t iface_fns[] = { + { "set_bss_order", uc_hostapd_iface_set_bss_order }, + { "add_bss", uc_hostapd_iface_add_bss }, + { "stop", uc_hostapd_iface_stop }, + { "start", uc_hostapd_iface_start }, + { "switch_channel", uc_hostapd_iface_switch_channel }, + }; + uc_value_t *data, *proto; + + interfaces = ifaces; + vm = wpa_ucode_create_vm(); + + global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL); + bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL); + iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL); + + bss_registry = ucv_array_new(vm); + uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry); + + iface_registry = ucv_array_new(vm); + uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry); + + global = wpa_ucode_global_init("hostapd", global_type); + + if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc")) + goto free_vm; + ucv_gc(vm); + + return 0; + +free_vm: + wpa_ucode_free_vm(); + return -1; +} + +void hostapd_ucode_free(void) +{ + if (wpa_ucode_call_prepare("shutdown") == 0) + ucv_put(wpa_ucode_call(0)); + wpa_ucode_free_vm(); +} + +void hostapd_ucode_free_iface(struct hostapd_iface *iface) +{ + wpa_ucode_registry_remove(iface_registry, iface->ucode.idx); +} + +void hostapd_ucode_add_bss(struct hostapd_data *hapd) +{ + uc_value_t *val; + + if (wpa_ucode_call_prepare("bss_add")) + return; + + val = hostapd_ucode_bss_get_uval(hapd); + uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); + uc_value_push(ucv_get(val)); + ucv_put(wpa_ucode_call(2)); + ucv_gc(vm); +} + +void hostapd_ucode_reload_bss(struct hostapd_data *hapd) +{ + uc_value_t *val; + + if (wpa_ucode_call_prepare("bss_reload")) + return; + + val = hostapd_ucode_bss_get_uval(hapd); + uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); + uc_value_push(ucv_get(val)); + ucv_put(wpa_ucode_call(2)); + ucv_gc(vm); +} + +void hostapd_ucode_free_bss(struct hostapd_data *hapd) +{ + uc_value_t *val; + + val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx); + if (!val) + return; + + hapd->ucode.idx = 0; + if (wpa_ucode_call_prepare("bss_remove")) + return; + + uc_value_push(ucv_string_new(hapd->conf->iface)); + uc_value_push(ucv_get(val)); + ucv_put(wpa_ucode_call(2)); + ucv_gc(vm); +} diff --git a/package/network/services/hostapd/src/src/ap/ucode.h b/package/network/services/hostapd/src/src/ap/ucode.h new file mode 100644 index 0000000000..d00b787169 --- /dev/null +++ b/package/network/services/hostapd/src/src/ap/ucode.h @@ -0,0 +1,54 @@ +#ifndef __HOSTAPD_AP_UCODE_H +#define __HOSTAPD_AP_UCODE_H + +#include "utils/ucode.h" + +struct hostapd_data; + +struct hostapd_ucode_bss { +#ifdef UCODE_SUPPORT + int idx; +#endif +}; + +struct hostapd_ucode_iface { +#ifdef UCODE_SUPPORT + int idx; +#endif +}; + +#ifdef UCODE_SUPPORT + +int hostapd_ucode_init(struct hapd_interfaces *ifaces); + +void hostapd_ucode_free(void); +void hostapd_ucode_free_iface(struct hostapd_iface *iface); +void hostapd_ucode_add_bss(struct hostapd_data *hapd); +void hostapd_ucode_free_bss(struct hostapd_data *hapd); +void hostapd_ucode_reload_bss(struct hostapd_data *hapd); + +#else + +static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces) +{ + return -EINVAL; +} +static inline void hostapd_ucode_free(void) +{ +} +static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface) +{ +} +static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd) +{ +} +static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd) +{ +} +static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd) +{ +} + +#endif + +#endif diff --git a/package/network/services/hostapd/src/src/utils/ucode.c b/package/network/services/hostapd/src/src/utils/ucode.c new file mode 100644 index 0000000000..2beeb9a7ff --- /dev/null +++ b/package/network/services/hostapd/src/src/utils/ucode.c @@ -0,0 +1,335 @@ +#include <unistd.h> +#include "ucode.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "common/ieee802_11_common.h" +#include <libubox/uloop.h> +#include <ucode/compiler.h> + +static uc_value_t *registry; +static uc_vm_t vm; +static struct uloop_timeout gc_timer; + +static void uc_gc_timer(struct uloop_timeout *timeout) +{ + ucv_gc(&vm); +} + +uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *level = uc_fn_arg(0); + uc_value_t *ret, **args; + uc_cfn_ptr_t _sprintf; + int l = MSG_INFO; + int i, start = 0; + + _sprintf = uc_stdlib_function("sprintf"); + if (!sprintf) + return NULL; + + if (ucv_type(level) == UC_INTEGER) { + l = ucv_int64_get(level); + start++; + } + + if (nargs <= start) + return NULL; + + ret = _sprintf(vm, nargs - start); + if (ucv_type(ret) != UC_STRING) + return NULL; + + wpa_printf(l, "%s", ucv_string_get(ret)); + ucv_put(ret); + + return NULL; +} + +uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *freq = uc_fn_arg(0); + uc_value_t *sec = uc_fn_arg(1); + int width = ucv_uint64_get(uc_fn_arg(2)); + int freq_val, center_idx, center_ofs; + enum oper_chan_width chanwidth; + enum hostapd_hw_mode hw_mode; + u8 op_class, channel, tmp_channel; + const char *modestr; + int sec_channel = 0; + uc_value_t *ret; + + if (ucv_type(freq) != UC_INTEGER) + return NULL; + + freq_val = ucv_int64_get(freq); + if (ucv_type(sec) == UC_INTEGER) + sec_channel = ucv_int64_get(sec); + else if (sec) + return NULL; + else if (freq_val > 4000) + sec_channel = (freq_val / 20) & 1 ? 1 : -1; + else + sec_channel = freq_val < 2442 ? 1 : -1; + + if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0) + return NULL; + + switch (width) { + case 0: + chanwidth = CONF_OPER_CHWIDTH_USE_HT; + break; + case 1: + chanwidth = CONF_OPER_CHWIDTH_80MHZ; + break; + case 2: + chanwidth = CONF_OPER_CHWIDTH_160MHZ; + break; + default: + return NULL; + } + + hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel, + chanwidth, &op_class, &channel); + switch (hw_mode) { + case HOSTAPD_MODE_IEEE80211B: + modestr = "b"; + break; + case HOSTAPD_MODE_IEEE80211G: + modestr = "g"; + break; + case HOSTAPD_MODE_IEEE80211A: + modestr = "a"; + break; + case HOSTAPD_MODE_IEEE80211AD: + modestr = "ad"; + break; + default: + return NULL; + } + + ret = ucv_object_new(vm); + ucv_object_add(ret, "op_class", ucv_int64_new(op_class)); + ucv_object_add(ret, "channel", ucv_int64_new(channel)); + ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode)); + ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr))); + ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel)); + ucv_object_add(ret, "frequency", ucv_int64_new(freq_val)); + + if (!sec_channel) + return ret; + + if (freq_val >= 5900) + center_ofs = 0; + else if (freq_val >= 5745) + center_ofs = 20; + else + center_ofs = 35; + tmp_channel = channel - center_ofs; + tmp_channel &= ~((8 << width) - 1); + center_idx = tmp_channel + center_ofs + (4 << width) - 1; + + if (freq_val < 3000) + ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0)); + else + ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx)); + center_idx = (center_idx - channel) * 5 + freq_val; + ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx)); + +out: + return ret; +} + +uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs) +{ + return ucv_int64_new(getpid()); +} + +uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs) +{ + u8 hash[SHA1_MAC_LEN]; + char hash_hex[2 * ARRAY_SIZE(hash) + 1]; + uc_value_t *val; + size_t *lens; + const u8 **args; + int i; + + if (!nargs) + return NULL; + + args = alloca(nargs * sizeof(*args)); + lens = alloca(nargs * sizeof(*lens)); + for (i = 0; i < nargs; i++) { + val = uc_fn_arg(i); + if (ucv_type(val) != UC_STRING) + return NULL; + + args[i] = ucv_string_get(val); + lens[i] = ucv_string_length(val); + } + + if (sha1_vector(nargs, args, lens, hash)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(hash); i++) + sprintf(hash_hex + 2 * i, "%02x", hash[i]); + + return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash)); +} + +uc_vm_t *wpa_ucode_create_vm(void) +{ + static uc_parse_config_t config = { + .strict_declarations = true, + .lstrip_blocks = true, + .trim_blocks = true, + .raw_mode = true + }; + + uc_search_path_init(&config.module_search_path); + uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so"); + uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc"); + + uc_vm_init(&vm, &config); + + uc_stdlib_load(uc_vm_scope_get(&vm)); + eloop_add_uloop(); + gc_timer.cb = uc_gc_timer; + + return &vm; +} + +int wpa_ucode_run(const char *script) +{ + uc_source_t *source; + uc_program_t *prog; + uc_value_t *ops; + char *err; + int ret; + + source = uc_source_new_file(script); + if (!source) + return -1; + + prog = uc_compile(vm.config, source, &err); + uc_source_put(source); + if (!prog) { + wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err); + return -1; + } + + ret = uc_vm_execute(&vm, prog, &ops); + uc_program_put(prog); + if (ret || !ops) + return -1; + + registry = ucv_array_new(&vm); + uc_vm_registry_set(&vm, "hostap.registry", registry); + ucv_array_set(registry, 0, ucv_get(ops)); + + return 0; +} + +int wpa_ucode_call_prepare(const char *fname) +{ + uc_value_t *obj, *func; + + if (!registry) + return -1; + + obj = ucv_array_get(registry, 0); + if (!obj) + return -1; + + func = ucv_object_get(obj, fname, NULL); + if (!ucv_is_callable(func)) + return -1; + + uc_vm_stack_push(&vm, ucv_get(obj)); + uc_vm_stack_push(&vm, ucv_get(func)); + + return 0; +} + +uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type) +{ + uc_value_t *global = uc_resource_new(global_type, NULL); + uc_value_t *proto; + + uc_vm_registry_set(&vm, "hostap.global", global); + proto = ucv_prototype_get(global); + ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm))); + +#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x)) + ADD_CONST(MSG_EXCESSIVE); + ADD_CONST(MSG_MSGDUMP); + ADD_CONST(MSG_DEBUG); + ADD_CONST(MSG_INFO); + ADD_CONST(MSG_WARNING); + ADD_CONST(MSG_ERROR); +#undef ADD_CONST + + ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global)); + + return global; +} + +int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val) +{ + uc_value_t *data; + int i = 0; + + while (ucv_array_get(reg, i)) + i++; + + ucv_array_set(reg, i, ucv_get(val)); + + return i + 1; +} + +uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx) +{ + if (!idx) + return NULL; + + return ucv_array_get(reg, idx - 1); +} + +uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx) +{ + uc_value_t *val = wpa_ucode_registry_get(reg, idx); + void **dataptr; + + if (!val) + return NULL; + + ucv_array_set(reg, idx - 1, NULL); + dataptr = ucv_resource_dataptr(val, NULL); + if (dataptr) + *dataptr = NULL; + + return val; +} + + +uc_value_t *wpa_ucode_call(size_t nargs) +{ + if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE) + return NULL; + + if (!gc_timer.pending) + uloop_timeout_set(&gc_timer, 10); + + return uc_vm_stack_pop(&vm); +} + +void wpa_ucode_free_vm(void) +{ + if (!vm.config) + return; + + uc_search_path_free(&vm.config->module_search_path); + uc_vm_free(&vm); + registry = NULL; + vm = (uc_vm_t){}; +} diff --git a/package/network/services/hostapd/src/src/utils/ucode.h b/package/network/services/hostapd/src/src/utils/ucode.h new file mode 100644 index 0000000000..2c1886976e --- /dev/null +++ b/package/network/services/hostapd/src/src/utils/ucode.h @@ -0,0 +1,29 @@ +#ifndef __HOSTAPD_UTILS_UCODE_H +#define __HOSTAPD_UTILS_UCODE_H + +#include "utils/includes.h" +#include "utils/common.h" +#include <ucode/lib.h> +#include <ucode/vm.h> + +#define HOSTAPD_UC_PATH "/usr/share/hostap/" + +extern uc_value_t *uc_registry; +uc_vm_t *wpa_ucode_create_vm(void); +int wpa_ucode_run(const char *script); +int wpa_ucode_call_prepare(const char *fname); +uc_value_t *wpa_ucode_call(size_t nargs); +void wpa_ucode_free_vm(void); + +uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type); + +int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val); +uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx); +uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx); + +uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs); +uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs); +uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs); +uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs); + +#endif diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.c b/package/network/services/hostapd/src/wpa_supplicant/ubus.c index 16a68c5073..1c477f0c0c 100644 --- a/package/network/services/hostapd/src/wpa_supplicant/ubus.c +++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.c @@ -30,12 +30,6 @@ static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *ob return container_of(obj, struct wpa_supplicant, ubus.obj); } -static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct ubus_context *ctx = eloop_ctx; - ubus_handle_event(ctx); -} - static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx) { if (ubus_reconnect(ctx, NULL)) { @@ -43,12 +37,12 @@ static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx) return; } - eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL); + ubus_add_uloop(ctx); } static void wpas_ubus_connection_lost(struct ubus_context *ctx) { - eloop_unregister_read_sock(ctx->sock.fd); + uloop_fd_delete(&ctx->sock); eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL); } @@ -57,12 +51,14 @@ static bool wpas_ubus_init(void) if (ctx) return true; + eloop_add_uloop(); ctx = ubus_connect(NULL); if (!ctx) return false; ctx->connection_lost = wpas_ubus_connection_lost; - eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL); + ubus_add_uloop(ctx); + return true; } @@ -80,7 +76,7 @@ static void wpas_ubus_ref_dec(void) if (ctx_ref) return; - eloop_unregister_read_sock(ctx->sock.fd); + uloop_fd_delete(&ctx->sock); ubus_free(ctx); ctx = NULL; } @@ -211,152 +207,6 @@ void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s) free(name); } -enum { - WPAS_CONFIG_DRIVER, - WPAS_CONFIG_IFACE, - WPAS_CONFIG_BRIDGE, - WPAS_CONFIG_HOSTAPD_CTRL, - WPAS_CONFIG_CTRL, - WPAS_CONFIG_FILE, - __WPAS_CONFIG_MAX -}; - -static const struct blobmsg_policy wpas_config_add_policy[__WPAS_CONFIG_MAX] = { - [WPAS_CONFIG_DRIVER] = { "driver", BLOBMSG_TYPE_STRING }, - [WPAS_CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING }, - [WPAS_CONFIG_BRIDGE] = { "bridge", BLOBMSG_TYPE_STRING }, - [WPAS_CONFIG_HOSTAPD_CTRL] = { "hostapd_ctrl", BLOBMSG_TYPE_STRING }, - [WPAS_CONFIG_CTRL] = { "ctrl", BLOBMSG_TYPE_STRING }, - [WPAS_CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING }, -}; - -static int -wpas_config_add(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__WPAS_CONFIG_MAX]; - struct wpa_global *global = get_wpa_global_from_object(obj); - struct wpa_interface *iface; - - blobmsg_parse(wpas_config_add_policy, __WPAS_CONFIG_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[WPAS_CONFIG_FILE] || !tb[WPAS_CONFIG_IFACE] || !tb[WPAS_CONFIG_DRIVER]) - return UBUS_STATUS_INVALID_ARGUMENT; - - iface = os_zalloc(sizeof(struct wpa_interface)); - if (iface == NULL) - return UBUS_STATUS_UNKNOWN_ERROR; - - iface->driver = blobmsg_get_string(tb[WPAS_CONFIG_DRIVER]); - iface->ifname = blobmsg_get_string(tb[WPAS_CONFIG_IFACE]); - iface->confname = blobmsg_get_string(tb[WPAS_CONFIG_FILE]); - - if (tb[WPAS_CONFIG_BRIDGE]) - iface->bridge_ifname = blobmsg_get_string(tb[WPAS_CONFIG_BRIDGE]); - - if (tb[WPAS_CONFIG_CTRL]) - iface->ctrl_interface = blobmsg_get_string(tb[WPAS_CONFIG_CTRL]); - - if (tb[WPAS_CONFIG_HOSTAPD_CTRL]) - iface->hostapd_ctrl = blobmsg_get_string(tb[WPAS_CONFIG_HOSTAPD_CTRL]); - - if (!wpa_supplicant_add_iface(global, iface, NULL)) - return UBUS_STATUS_INVALID_ARGUMENT; - - blob_buf_init(&b, 0); - blobmsg_add_u32(&b, "pid", getpid()); - ubus_send_reply(ctx, req, b.head); - - return UBUS_STATUS_OK; -} - -enum { - WPAS_CONFIG_REM_IFACE, - __WPAS_CONFIG_REM_MAX -}; - -static const struct blobmsg_policy wpas_config_remove_policy[__WPAS_CONFIG_REM_MAX] = { - [WPAS_CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING }, -}; - -static int -wpas_config_remove(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__WPAS_CONFIG_REM_MAX]; - struct wpa_global *global = get_wpa_global_from_object(obj); - struct wpa_supplicant *wpa_s = NULL; - unsigned int found = 0; - - blobmsg_parse(wpas_config_remove_policy, __WPAS_CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[WPAS_CONFIG_REM_IFACE]) - return UBUS_STATUS_INVALID_ARGUMENT; - - /* find wpa_s object for to-be-removed interface */ - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (!strncmp(wpa_s->ifname, - blobmsg_get_string(tb[WPAS_CONFIG_REM_IFACE]), - sizeof(wpa_s->ifname))) - { - found = 1; - break; - } - } - - if (!found) - return UBUS_STATUS_INVALID_ARGUMENT; - - if (wpa_supplicant_remove_iface(global, wpa_s, 0)) - return UBUS_STATUS_INVALID_ARGUMENT; - - return UBUS_STATUS_OK; -} - -static const struct ubus_method wpas_daemon_methods[] = { - UBUS_METHOD("config_add", wpas_config_add, wpas_config_add_policy), - UBUS_METHOD("config_remove", wpas_config_remove, wpas_config_remove_policy), -}; - -static struct ubus_object_type wpas_daemon_object_type = - UBUS_OBJECT_TYPE("wpa_supplicant", wpas_daemon_methods); - -void wpas_ubus_add(struct wpa_global *global) -{ - struct ubus_object *obj = &global->ubus_global; - int ret; - - if (!wpas_ubus_init()) - return; - - obj->name = strdup("wpa_supplicant"); - - obj->type = &wpas_daemon_object_type; - obj->methods = wpas_daemon_object_type.methods; - obj->n_methods = wpas_daemon_object_type.n_methods; - ret = ubus_add_object(ctx, obj); - wpas_ubus_ref_inc(); -} - -void wpas_ubus_free(struct wpa_global *global) -{ - struct ubus_object *obj = &global->ubus_global; - char *name = (char *) obj->name; - - if (!ctx) - return; - - if (obj->id) { - ubus_remove_object(ctx, obj); - wpas_ubus_ref_dec(); - } - - free(name); -} - - #ifdef CONFIG_WPS void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred) { diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.h b/package/network/services/hostapd/src/wpa_supplicant/ubus.h index bf92b98c01..f6681cb26d 100644 --- a/package/network/services/hostapd/src/wpa_supplicant/ubus.h +++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.h @@ -24,9 +24,6 @@ struct wpas_ubus_bss { void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s); void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s); -void wpas_ubus_add(struct wpa_global *global); -void wpas_ubus_free(struct wpa_global *global); - #ifdef CONFIG_WPS void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred); #endif @@ -34,14 +31,6 @@ void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential #else struct wpas_ubus_bss {}; -static inline void wpas_ubus_add_iface(struct wpa_supplicant *wpa_s) -{ -} - -static inline void wpas_ubus_free_iface(struct wpa_supplicant *wpa_s) -{ -} - static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s) { } diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.c b/package/network/services/hostapd/src/wpa_supplicant/ucode.c new file mode 100644 index 0000000000..55d22584ff --- /dev/null +++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.c @@ -0,0 +1,281 @@ +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/ucode.h" +#include "drivers/driver.h" +#include "ap/hostapd.h" +#include "wpa_supplicant_i.h" +#include "wps_supplicant.h" +#include "bss.h" +#include "ucode.h" + +static struct wpa_global *wpa_global; +static uc_resource_type_t *global_type, *iface_type; +static uc_value_t *global, *iface_registry; +static uc_vm_t *vm; + +static uc_value_t * +wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s) +{ + uc_value_t *val; + + if (wpa_s->ucode.idx) + return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx); + + val = uc_resource_new(iface_type, wpa_s); + wpa_s->ucode.idx = wpa_ucode_registry_add(iface_registry, val); + + return val; +} + +static void +wpas_ucode_update_interfaces(void) +{ + uc_value_t *ifs = ucv_object_new(vm); + struct wpa_supplicant *wpa_s; + int i; + + for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next) + ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s))); + + ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs)); + ucv_gc(vm); +} + +void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s) +{ + uc_value_t *val; + + if (wpa_ucode_call_prepare("iface_add")) + return; + + uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname))); + uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s))); + ucv_put(wpa_ucode_call(2)); + ucv_gc(vm); +} + +void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s) +{ + uc_value_t *val; + + val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx); + if (!val) + return; + + wpa_s->ucode.idx = 0; + if (wpa_ucode_call_prepare("iface_remove")) + return; + + uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname))); + uc_value_push(ucv_get(val)); + ucv_put(wpa_ucode_call(2)); + ucv_gc(vm); +} + +void wpas_ucode_update_state(struct wpa_supplicant *wpa_s) +{ + const char *state; + uc_value_t *val; + + val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx); + if (!val) + return; + + if (wpa_ucode_call_prepare("state")) + return; + + state = wpa_supplicant_state_txt(wpa_s->wpa_state); + uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname))); + uc_value_push(ucv_get(val)); + uc_value_push(ucv_get(ucv_string_new(state))); + ucv_put(wpa_ucode_call(3)); + ucv_gc(vm); +} + +void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data) +{ + const char *state; + uc_value_t *val; + + if (event != EVENT_CH_SWITCH_STARTED) + return; + + val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx); + if (!val) + return; + + if (wpa_ucode_call_prepare("event")) + return; + + uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname))); + uc_value_push(ucv_get(val)); + uc_value_push(ucv_get(ucv_string_new(event_to_string(event)))); + val = ucv_object_new(vm); + uc_value_push(ucv_get(val)); + + if (event == EVENT_CH_SWITCH_STARTED) { + ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count)); + ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq)); + ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset)); + ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1)); + ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2)); + } + + ucv_put(wpa_ucode_call(4)); + ucv_gc(vm); +} + +static const char *obj_stringval(uc_value_t *obj, const char *name) +{ + uc_value_t *val = ucv_object_get(obj, name, NULL); + + return ucv_string_get(val); +} + +static uc_value_t * +uc_wpas_add_iface(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *info = uc_fn_arg(0); + uc_value_t *ifname = ucv_object_get(info, "iface", NULL); + uc_value_t *bridge = ucv_object_get(info, "bridge", NULL); + uc_value_t *config = ucv_object_get(info, "config", NULL); + uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL); + struct wpa_interface iface; + int ret = -1; + + if (ucv_type(info) != UC_OBJECT) + goto out; + + iface = (struct wpa_interface){ + .driver = "nl80211", + .ifname = ucv_string_get(ifname), + .bridge_ifname = ucv_string_get(bridge), + .confname = ucv_string_get(config), + .ctrl_interface = ucv_string_get(ctrl), + }; + + if (!iface.ifname || !iface.confname) + goto out; + + ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1; + wpas_ucode_update_interfaces(); + +out: + return ucv_int64_new(ret); +} + +static uc_value_t * +uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs) +{ + struct wpa_supplicant *wpa_s = NULL; + uc_value_t *ifname_arg = uc_fn_arg(0); + const char *ifname = ucv_string_get(ifname_arg); + int ret = -1; + + if (!ifname) + goto out; + + for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next) + if (!strcmp(wpa_s->ifname, ifname)) + break; + + if (!wpa_s) + goto out; + + ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0); + wpas_ucode_update_interfaces(); + +out: + return ucv_int64_new(ret); +} + +static uc_value_t * +uc_wpas_iface_status(uc_vm_t *vm, size_t nargs) +{ + struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface"); + struct wpa_bss *bss; + uc_value_t *ret, *val; + + if (!wpa_s) + return NULL; + + ret = ucv_object_new(vm); + + val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state)); + ucv_object_add(ret, "state", ucv_get(val)); + + bss = wpa_s->current_bss; + if (bss) { + int sec_chan = 0; + const u8 *ie; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2) { + const struct ieee80211_ht_operation *ht_oper; + int sec; + + ht_oper = (const void *) (ie + 2); + sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan)); + ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq)); + } + +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + struct hostapd_iface *ifmsh = wpa_s->ifmsh; + + ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(ifmsh->conf->secondary_channel)); + ucv_object_add(ret, "frequency", ucv_int64_new(ifmsh->freq)); + } +#endif + + return ret; +} + +int wpas_ucode_init(struct wpa_global *gl) +{ + static const uc_function_list_t global_fns[] = { + { "printf", uc_wpa_printf }, + { "getpid", uc_wpa_getpid }, + { "add_iface", uc_wpas_add_iface }, + { "remove_iface", uc_wpas_remove_iface }, + }; + static const uc_function_list_t iface_fns[] = { + { "status", uc_wpas_iface_status }, + }; + uc_value_t *data, *proto; + + wpa_global = gl; + vm = wpa_ucode_create_vm(); + + global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL); + iface_type = uc_type_declare(vm, "wpas.iface", iface_fns, NULL); + + iface_registry = ucv_array_new(vm); + uc_vm_registry_set(vm, "wpas.iface_registry", iface_registry); + + global = wpa_ucode_global_init("wpas", global_type); + + if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc")) + goto free_vm; + + ucv_gc(vm); + return 0; + +free_vm: + wpa_ucode_free_vm(); + return -1; +} + +void wpas_ucode_free(void) +{ + if (wpa_ucode_call_prepare("shutdown") == 0) + ucv_put(wpa_ucode_call(0)); + wpa_ucode_free_vm(); +} diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.h b/package/network/services/hostapd/src/wpa_supplicant/ucode.h new file mode 100644 index 0000000000..a429a0ed87 --- /dev/null +++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.h @@ -0,0 +1,49 @@ +#ifndef __WPAS_UCODE_H +#define __WPAS_UCODE_H + +#include "utils/ucode.h" + +struct wpa_global; +union wpa_event_data; +struct wpa_supplicant; + +struct wpas_ucode_bss { +#ifdef UCODE_SUPPORT + unsigned int idx; +#endif +}; + +#ifdef UCODE_SUPPORT +int wpas_ucode_init(struct wpa_global *gl); +void wpas_ucode_free(void); +void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s); +void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s); +void wpas_ucode_update_state(struct wpa_supplicant *wpa_s); +void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data); +#else +static inline int wpas_ucode_init(struct wpa_global *gl) +{ + return -EINVAL; +} +static inline void wpas_ucode_free(void) +{ +} +static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_ucode_update_state(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data) +{ +} + +#endif + +#endif |