395 lines
9.2 KiB
C
395 lines
9.2 KiB
C
#include <sys/un.h>
|
|
|
|
#include "utils/includes.h"
|
|
#include "utils/common.h"
|
|
#include "utils/ucode.h"
|
|
#include "hostapd.h"
|
|
#include "ap_drv_ops.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);
|
|
wpa_ucode_registry_add(bss_registry, val, &hapd->ucode.idx);
|
|
|
|
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);
|
|
wpa_ucode_registry_add(iface_registry, val, &hapd->ucode.idx);
|
|
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
hostapd_ucode_update_bss_list(struct hostapd_iface *iface)
|
|
{
|
|
uc_value_t *ifval, *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);
|
|
uc_value_t *proto = ucv_prototype_get(val);
|
|
|
|
ucv_object_add(proto, "name", ucv_get(ucv_string_new(hapd->conf->iface)));
|
|
ucv_object_add(proto, "index", ucv_int64_new(i));
|
|
ucv_array_set(list, i, ucv_get(val));
|
|
}
|
|
|
|
ifval = hostapd_ucode_iface_get_uval(iface);
|
|
ucv_object_add(ucv_prototype_get(ifval), "bss", ucv_get(list));
|
|
}
|
|
|
|
static void
|
|
hostapd_ucode_update_interfaces(void)
|
|
{
|
|
uc_value_t *ifs = 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);
|
|
}
|
|
|
|
ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
|
|
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 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);
|
|
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 || idx > conf->num_bss || !conf->bss[idx])
|
|
goto out;
|
|
|
|
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_config_free(conf);
|
|
|
|
hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
|
|
|
|
ret = 0;
|
|
|
|
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 || hapd == hapd->iface->bss[0])
|
|
return NULL;
|
|
|
|
iface = hapd->iface;
|
|
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--;
|
|
|
|
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_bss_list(iface);
|
|
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_bss_list(iface);
|
|
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_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);
|
|
}
|
|
|
|
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 },
|
|
{ "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 },
|
|
{ "delete", uc_hostapd_bss_delete },
|
|
};
|
|
static const uc_function_list_t iface_fns[] = {
|
|
{ "add_bss", uc_hostapd_iface_add_bss }
|
|
};
|
|
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, int reconf)
|
|
{
|
|
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));
|
|
uc_value_push(ucv_int64_new(reconf));
|
|
ucv_put(wpa_ucode_call(3));
|
|
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);
|
|
}
|