822 lines
20 KiB
C
822 lines
20 KiB
C
#include <sys/un.h>
|
|
|
|
#include "utils/includes.h"
|
|
#include "utils/common.h"
|
|
#include "utils/ucode.h"
|
|
#include "sta_info.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; iface->bss && 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);
|
|
char *data;
|
|
int ret;
|
|
|
|
if (ucv_type(iface) != UC_STRING)
|
|
return ucv_int64_new(-1);
|
|
|
|
data = strdup(ucv_string_get(iface));
|
|
ret = hostapd_add_iface(interfaces, data);
|
|
free(data);
|
|
|
|
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 void
|
|
uc_hostapd_disable_iface(struct hostapd_iface *iface)
|
|
{
|
|
switch (iface->state) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (!iface)
|
|
return NULL;
|
|
|
|
if (iface->state != HAPD_IFACE_ENABLED)
|
|
uc_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_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:
|
|
uc_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 },
|
|
{ "udebug_set", uc_wpa_udebug_set },
|
|
};
|
|
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_bss_cb(struct hostapd_data *hapd, const char *type)
|
|
{
|
|
uc_value_t *val;
|
|
|
|
if (wpa_ucode_call_prepare(type))
|
|
return;
|
|
|
|
val = hostapd_ucode_bss_get_uval(hapd);
|
|
uc_value_push(ucv_get(ucv_string_new(hapd->iface->phy)));
|
|
uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
|
|
uc_value_push(ucv_get(val));
|
|
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);
|
|
}
|
|
|
|
#ifdef CONFIG_APUP
|
|
void hostapd_ucode_apup_newpeer(struct hostapd_data *hapd, const char *ifname)
|
|
{
|
|
uc_value_t *val;
|
|
|
|
if (wpa_ucode_call_prepare("apup_newpeer"))
|
|
return;
|
|
|
|
val = hostapd_ucode_bss_get_uval(hapd);
|
|
uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); // BSS ifname
|
|
uc_value_push(ucv_get(val));
|
|
uc_value_push(ucv_get(ucv_string_new(ifname))); // APuP peer ifname
|
|
ucv_put(wpa_ucode_call(2));
|
|
ucv_gc(vm);
|
|
}
|
|
#endif // def CONFIG_APUP
|