Skip to content
Snippets Groups Projects
config.c 33.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Anjan Chanda's avatar
    Anjan Chanda committed
    /*
     * config.c - configurations handling
     *
     * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
     *
     * Author: anjan.chanda@iopsys.eu
     *
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #ifndef _GNU_SOURCE
    #define _GNU_SOURCE
    #endif
    
    #include <json-c/json.h>
    #include <libubox/blobmsg.h>
    #include <libubox/blobmsg_json.h>
    #include <libubox/uloop.h>
    #include <libubox/ustream.h>
    #include <libubox/utils.h>
    #include <libubus.h>
    #include <uci.h>
    
    #include "debug.h"
    #include "utils.h"
    #include "config.h"
    #include "steer_rules.h"
    #include "comm.h"
    
    #include "msgqueue.h"
    #include "worker.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "agent.h"
    
    
    #define WPS_AUTH_OPEN          (0x0001)
    #define WPS_AUTH_WPAPSK        (0x0002)
    #define WPS_AUTH_SHARED        (0x0004)	/* deprecated */
    #define WPS_AUTH_WPA           (0x0008)
    #define WPS_AUTH_WPA2          (0x0010)
    #define WPS_AUTH_WPA2PSK       (0x0020)
    #define WPS_ENCR_NONE          (0x0001)
    #define WPS_ENCR_WEP           (0x0002)	/* deprecated */
    #define WPS_ENCR_TKIP          (0x0004)
    #define WPS_ENCR_AES           (0x0008)
    
    // UCI sections
    #define UCI_BK_AGENT "bk-iface"
    #define UCI_FH_AGENT "fh-iface"
    #define UCI_WLAN_IFACE "wifi-iface"
    #define UCI_WIRELESS "wireless"
    #define UCI_IEEE1905 "ieee1905"
    #define UCI_AGENT "agent"
    
    
    int set_value(struct uci_context *ctx, struct uci_package *pkg,
    
    		struct uci_section *section, const char *key,
    		const char *value, enum uci_option_type type)
    {
    	struct uci_ptr ptr = {0};
    
    	ptr.p = pkg;
    	ptr.s = section;
    	ptr.option = key;
    	ptr.value = value;
    
    	if (type == UCI_TYPE_STRING)
    		return uci_set(ctx, &ptr);
    
    	if (type == UCI_TYPE_LIST)
    		return uci_add_list(ctx, &ptr);
    
    	return -1;
    }
    
    
    struct uci_section *config_get_iface_section(struct uci_context *ctx,
    
    		struct uci_package *pkg, const char *type, const char *ifname)
    
    {
    	struct uci_element *e;
    	struct uci_section *section;
    
    	/* get the wet iface section */
    	uci_foreach_element(&pkg->sections, e) {
    		const char *c_ifname;
    
    		section = uci_to_section(e);
    
    		if (strcmp(section->type, type))
    
    			continue;
    
    		c_ifname = uci_lookup_option_string(ctx, section, "ifname");
    		if (c_ifname && !strcmp(c_ifname, ifname))
    			return section;
    	}
    
    	return NULL;
    }
    
    
    struct uci_package *uci_load_pkg(struct uci_context **ctx, const char *config)
    {
    	struct uci_package *pkg;
    
    	if (!*ctx) {
    		*ctx = uci_alloc_context();
    		if (!*ctx)
    			return NULL;
    	}
    	if (uci_load(*ctx, config, &pkg) != UCI_OK) {
    		free(*ctx);
    		return NULL;
    	}
    
    	return pkg;
    }
    
    /* TODO: can it be generalized? */
    int wifi_set_iface_bssid(const char *ifname, uint8_t *bssid)
    {
    	struct uci_context *ctx = NULL;
    	struct uci_package *pkg;
    	struct uci_section *section;
    	char bssid_str[18] = {0};
    	int ret = -1;
    
    	pkg = uci_load_pkg(&ctx, "wireless");
    	if (!pkg)
    		return ret;
    
    	section = config_get_iface_section(ctx, pkg, "wifi-iface", ifname);
    	if (!section)
    		goto out_pkg;
    
    	if (bssid && !hwaddr_ntoa(bssid, bssid_str))
    		goto out_pkg;
    
    	ret = set_value(ctx, pkg, section, "bssid", bssid_str, UCI_TYPE_STRING);
    
    	uci_commit(ctx, &pkg, false);
    
    	uci_reload_services();
    out_pkg:
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    	return ret;
    }
    
    
    int config_del_iface(const char *config, const char *type, const char *ifname)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_section *section;
    	struct uci_ptr ptr = {0};
    	int rv = -1;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		goto out;
    
    	if (uci_load(ctx, config, &pkg) != UCI_OK) {
    		dbg("config file 'wireless' not found!\n");
    		goto out_uci;
    	}
    
    	section = config_get_iface_section(ctx, pkg, type, ifname);
    	if (!section)
    		goto out_pkg;
    
    	ptr.p = pkg;
    	ptr.s = section;
    
    	uci_delete(ctx, &ptr);
    	uci_commit(ctx, &pkg, false);
    out_pkg:
    	uci_unload(ctx, pkg);
    out_uci:
    	uci_free_context(ctx);
    out:
    	return rv;
    }
    
    
    int wifi_apply_iface_cfg(const char *ifname, const char *encryption,
    		const char *ssid, const char *key)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_section *section;
    	int rv = -1;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		goto out;
    
    	if (uci_load(ctx, "wireless", &pkg) != UCI_OK) {
    		dbg("config file 'wireless' not found!\n");
    		goto out_uci;
    	}
    
    
    	section = config_get_iface_section(ctx, pkg, "wifi-iface", ifname);
    
    	if (!section)
    		goto out_pkg;
    
    	set_value(ctx, pkg, section, "encryption", encryption, UCI_TYPE_STRING);
    	set_value(ctx, pkg, section, "ssid", ssid, UCI_TYPE_STRING);
    	set_value(ctx, pkg, section, "key", key, UCI_TYPE_STRING);
    
    	uci_commit(ctx, &pkg, false);
    
    out_pkg:
    	uci_unload(ctx, pkg);
    out_uci:
    	uci_free_context(ctx);
    out:
    	return rv;
    }
    
    
    int config_add_section(const char *config, const char *type, const char *ifname)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_section *section;
    	struct uci_ptr ptr = {0};
    	int rv = -1;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		goto out;
    
    	if (uci_load(ctx, config, &pkg) != UCI_OK) {
    		dbg("config file '%s' not found!\n", config);
    		goto out_uci;
    	}
    
    	section = config_get_iface_section(ctx, pkg, type, ifname);
    	if (section)
    		goto out_pkg;
    
    	rv = uci_add_section(ctx, pkg, type, &section);
    	if (rv)
    		goto out_pkg;
    
    	rv = uci_save(ctx, pkg);
    	if (rv)
    		goto out_pkg;
    
    	ptr.value = ifname;
    	ptr.package = config;
    	ptr.section = section->e.name;
    	ptr.option = "ifname";
    	ptr.target = UCI_TYPE_OPTION;
    
    	uci_lookup_ptr(ctx, &ptr, NULL, false);
    	uci_set(ctx, &ptr);
    	uci_save(ctx, ptr.p);
    	uci_commit(ctx, &pkg, false);
    out_pkg:
    	uci_unload(ctx, pkg);
    out_uci:
    	uci_free_context(ctx);
    out:
    	return rv;
    }
    
    int config_add_default_wifi_iface(const char *config, const char *type,
    		const char *ifname, const char *device, const char *network,
    		const char *mode)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_section *section;
    	int rv = -1;
    
    	rv = config_add_section(config, type, ifname);
    	if (rv)
    		return -1;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		goto out;
    
    	if (uci_load(ctx, config, &pkg) != UCI_OK) {
    		dbg("config file 'wireless' not found!\n");
    		goto out_uci;
    	}
    
    	section = config_get_iface_section(ctx, pkg, type, ifname);
    	if (!section)
    		goto out_pkg;
    
    	set_value(ctx, pkg, section, "device", device, UCI_TYPE_STRING);
    	set_value(ctx, pkg, section, "network", network, UCI_TYPE_STRING);
    	//set_value(ctx, pkg, section, "mode", mode, UCI_TYPE_STRING);
    
    	uci_commit(ctx, &pkg, false);
    
    out_pkg:
    	uci_unload(ctx, pkg);
    out_uci:
    	uci_free_context(ctx);
    out:
    	return rv;
    }
    
    int config_add_default_agent_iface(const char *config, const char *type,
    		const char *ifname, enum wifi_band band)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_section *section;
    	int rv = -1;
    
    	rv = config_add_section(config, type, ifname);
    	if (rv)
    		return -1;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		goto out;
    
    	if (uci_load(ctx, config, &pkg) != UCI_OK) {
    		dbg("config file 'wireless' not found!\n");
    		goto out_uci;
    	}
    
    	section = config_get_iface_section(ctx, pkg, type, ifname);
    	if (!section)
    		goto out_pkg;
    
    	trace("band = %d\n", band);
    
    	if (band == BAND_5)
    		set_value(ctx, pkg, section, "band", "5", UCI_TYPE_STRING);
    	if (band == BAND_2)
    		set_value(ctx, pkg, section, "band", "2", UCI_TYPE_STRING);
    
    	uci_commit(ctx, &pkg, false);
    
    out_pkg:
    	uci_unload(ctx, pkg);
    out_uci:
    	uci_free_context(ctx);
    out:
    	return rv;
    }
    
    /* below functions are mostly taken from ieee1905d */
    static bool uci_check_wifi_iface(char *package_name, char *ifname,
    		char *section)
    {
    	bool ret;
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_element *e;
    
    	if (!package_name || !ifname)
    		return false;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		return false;
    
    	if (uci_load(ctx, package_name, &pkg)) {
    		uci_free_context(ctx);
    		return false;
    	}
    
    	ret = false;
    	uci_foreach_element(&pkg->sections, e) {
    		struct uci_section *s = uci_to_section(e);
    
    		if (!strcmp(s->type, section)) {
    			struct uci_option *opt = uci_lookup_option(ctx, s,
    					"ifname");
    
    			if (!opt || opt->type != UCI_TYPE_STRING)
    				continue;
    			if (strcmp(opt->v.string, ifname) == 0) {
    				ret = true;
    				break;
    			}
    		}
    	}
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    
    	return ret;
    }
    
    static bool uci_set_wireless_interface_option(char *package_name,
    		char *section_type, char *ifname, char *option, char *value)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_element *e;
    
    	if (!package_name || !ifname || !option || !value)
    		return false;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		return false;
    
    	if (uci_load(ctx, package_name, &pkg)) {
    		uci_free_context(ctx);
    		return false;
    	}
    
    	uci_foreach_element(&pkg->sections, e) {
    		struct uci_section *s = uci_to_section(e);
    
    		if (!strcmp(s->type, section_type)) {
    			struct uci_option *opt = uci_lookup_option(ctx, s,
    					"ifname");
    
    			if (!opt || opt->type != UCI_TYPE_STRING)
    				continue;
    			if (strcmp(opt->v.string, ifname) == 0) {
    				struct uci_ptr ptr = {0};
    
    				ptr.value = value;
    				ptr.package = package_name;
    				ptr.section = s->e.name;
    				ptr.option = option;
    				ptr.target = UCI_TYPE_OPTION;
    				if (uci_lookup_ptr(ctx, &ptr, NULL, false) ||
    						!UCI_LOOKUP_COMPLETE)
    					break;
    				if (uci_set(ctx, &ptr) == UCI_OK)
    					uci_save(ctx, ptr.p);
    				break;
    			}
    		}
    	}
    	uci_commit(ctx, &pkg, false);
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    	return false;
    }
    
    static bool get_encryption_value(uint16_t auth_type, uint16_t encryption_type,
    		char *encrypt_val, size_t elen)
    {
    	if (!encrypt_val)
    		return false;
    
    	if ((auth_type & WPS_AUTH_WPAPSK) && (auth_type & WPS_AUTH_WPA2PSK))
    		strncat(encrypt_val, "psk-mixed", elen);
    	else if ((auth_type & WPS_AUTH_WPA) && (auth_type & WPS_AUTH_WPA2))
    		strncat(encrypt_val, "wpa-mixed", elen);
    	else if (auth_type & WPS_AUTH_WPAPSK)
    		strncat(encrypt_val, "psk", elen);
    	else if (auth_type & WPS_AUTH_WPA2PSK)
    		strncat(encrypt_val, "psk2", elen);
    	else if (auth_type & WPS_AUTH_WPA)
    		strncat(encrypt_val, "wpa", elen);
    	else if (auth_type & WPS_AUTH_WPA2)
    		strncat(encrypt_val, "wpa2", elen);
    	else
    		return false;
    
    	//Check for the encryption type
    	if ((encryption_type & WPS_ENCR_TKIP) &&
    			(encryption_type & WPS_ENCR_AES))
    		strncat(encrypt_val, "+tkip+aes", elen);
    	else if (encryption_type & WPS_ENCR_TKIP)
    		strncat(encrypt_val, "+tkip", elen);
    	else if (encryption_type & WPS_ENCR_AES)
    		strncat(encrypt_val, "+aes", elen);
    
    	return true;
    }
    
    static bool uci_add_wireless_iface_sec(char *package_name, char *interface_name,
    		char *section_name)
    {
    	struct uci_context *ctx;
    	struct uci_package *pkg;
    	struct uci_section *s;
    
    	if (!interface_name || !package_name)
    		return false;
    
    	ctx = uci_alloc_context();
    	if (!ctx)
    		return false;
    
    	if (uci_load(ctx, package_name, &pkg)) {
    		uci_free_context(ctx);
    		return false;
    	}
    
    	if (uci_add_section(ctx, pkg, section_name, &s) == UCI_OK) {
    		struct uci_ptr ptr = {0};
    
    		if (uci_save(ctx, pkg) != UCI_OK)
    			return false;
    
    		ptr.value = interface_name;
    		ptr.package = package_name;
    		ptr.section = s->e.name;
    		ptr.option = "ifname";
    		ptr.target = UCI_TYPE_OPTION;
    		uci_lookup_ptr(ctx, &ptr, NULL, false);
    
    		uci_set(ctx, &ptr);
    		uci_save(ctx, ptr.p);
    		uci_commit(ctx, &pkg, false);
    	}
    
    	uci_unload(ctx, pkg);
    	uci_free_context(ctx);
    
    	return true;
    }
    
    static int ubus_call(const char *object, const char *method,
    		struct blob_buf *data, void *callback, void *cb_arg)
    {
    	uint32_t id;
    	struct ubus_context *ctx = ubus_connect(NULL);
    
    	if (!ctx) {
    		err("ubus_connect failed\n");
    		return UBUS_STATUS_UNKNOWN_ERROR;
    	}
    
    	if (ubus_lookup_id(ctx, object, &id)) {
    		err("(%s) not present\n", object);
    		ubus_free(ctx);
    		return UBUS_STATUS_UNKNOWN_ERROR;
    	}
    
    	// Invoke Ubus to get data from uspd
    	if (ubus_invoke(ctx, id, method, data->head, callback, cb_arg, 1000)) {
    		err("ubus call failed\n");
    		ubus_free(ctx);
    		return UBUS_STATUS_UNKNOWN_ERROR;
    	}
    
    	ubus_free(ctx);
    	return UBUS_STATUS_OK;
    }
    
    
    bool uci_reload_services(void)
    
    {
    	struct blob_buf bb;
    
    	memset(&bb, 0, sizeof(struct blob_buf));
    	blob_buf_init(&bb, 0);
    
    	info("## Reloading uci config\n");
    	if (!ubus_call("uci", "reload_config", &bb, NULL, NULL))
    		return true;
    
    	blob_buf_free(&bb);
    
    	return false;
    }
    
    int uci_apply_m2(char *interface_name, uint8_t *ssid, uint8_t *bssid,
    		uint16_t auth_type, uint16_t encryption_type,
    		uint8_t *network_key, uint8_t mapie, uint8_t band)
    {
    	bool ret;
    	char auth_type_str[20] = {0};
    	char multiap_str[2] = {0};
    	uint8_t multi_ap = 0;
    	bool sta_mode;
    	char band_str[2] = {0};
    
    	dbg("Applying WSC configuration (%s):\n", interface_name);
    	dbg("  - SSID            : %s\n", ssid);
    	dbg("  - BSSID           : %02x:%02x:%02x:%02x:%02x:%02x\n",
    	     bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
    	dbg("  - AUTH_TYPE       : 0x%04x\n", auth_type);
    	dbg("  - ENCRYPTION_TYPE : 0x%04x\n", encryption_type);
    	dbg("  - NETWORK_KEY     : %s\n", network_key);
    	dbg("  - MAPIE_EXTENSION : 0x%02x\n", mapie);
    
    	// if teardown bit is set, return
    	if (BIT(3, mapie))
    		return M2_PROCESS_TEARDOWN;
    
    	multi_ap |= (BIT(5, mapie) << 1);
    	multi_ap |= BIT(6, mapie);
    	sta_mode = BIT(7, mapie);
    
    	snprintf(multiap_str, sizeof(multiap_str), "%d", multi_ap);
    
    	if (!get_encryption_value(auth_type, encryption_type,
    			auth_type_str, 20)) {
    		info("Unsupported encryption or cipher received!!\n");
    		return M2_PROCESS_ERROR;
    	}
    
    	// Set uci in agent
    	ret = uci_check_wifi_iface(UCI_AGENT, interface_name,
    			(sta_mode ? UCI_BK_AGENT : UCI_FH_AGENT));
    	if (!ret) {
    		ret = uci_add_wireless_iface_sec(UCI_AGENT, interface_name,
    				(sta_mode ? UCI_BK_AGENT : UCI_FH_AGENT));
    		if (!ret)
    			return M2_PROCESS_ERROR;
    	}
    
    	printf("%s band = %d\n", __func__, band);
    
    	if (band == BAND_5)
    		strncpy(band_str, "5", 1);
    	else if (band == BAND_2)
    		strncpy(band_str, "2", 1);
    	else /* TODO: 60 */
    		return M2_PROCESS_ERROR;
    
    	uci_set_wireless_interface_option(UCI_AGENT,
    			(sta_mode ? UCI_BK_AGENT : UCI_FH_AGENT),
    			interface_name, "band",
    			band_str);
    	if (sta_mode) {
    		char disallow_str[2] = {0};
    
    		snprintf(disallow_str, sizeof(disallow_str), "%d",
    				((mapie >> 2) & 0x03));
    		uci_set_wireless_interface_option(UCI_AGENT,
    				(sta_mode ? UCI_BK_AGENT : UCI_FH_AGENT),
    				interface_name,
    				"disallow_bsta", disallow_str);
    	}
    
    	// Set uci in ieee1905
    	ret = uci_check_wifi_iface(UCI_IEEE1905, interface_name,
    			UCI_WLAN_IFACE);
    	if (!ret) {
    		ret = uci_add_wireless_iface_sec(UCI_IEEE1905, interface_name,
    				UCI_WLAN_IFACE);
    		if (!ret)
    			return M2_PROCESS_ERROR;
    	}
    
    	uci_set_wireless_interface_option(UCI_IEEE1905, UCI_WLAN_IFACE,
    			interface_name, "ssid", (char *) ssid);
    	uci_set_wireless_interface_option(UCI_IEEE1905, UCI_WLAN_IFACE,
    			interface_name, "key", (char *) network_key);
    	uci_set_wireless_interface_option(UCI_IEEE1905, UCI_WLAN_IFACE,
    			interface_name, "encryption", auth_type_str);
    	uci_set_wireless_interface_option(UCI_IEEE1905, UCI_WLAN_IFACE,
    			interface_name, "multi_ap", multiap_str);
    	uci_set_wireless_interface_option(UCI_IEEE1905, UCI_WLAN_IFACE,
    			interface_name, "mode", (sta_mode ? "sta" : "ap"));
    
    	// Set uci in wireless
    	ret = uci_check_wifi_iface(UCI_WIRELESS, interface_name,
    			UCI_WLAN_IFACE);
    	if (!ret) {
    		ret = uci_add_wireless_iface_sec(UCI_WIRELESS, interface_name,
    				UCI_WLAN_IFACE);
    		if (!ret)
    			return M2_PROCESS_ERROR;
    	}
    
    	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
    			interface_name, "ssid", (char *) ssid);
    	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
    			interface_name, "key", (char *) network_key);
    	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
    			interface_name, "encryption", auth_type_str);
    	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
    			interface_name, "multi_ap", multiap_str);
    	uci_set_wireless_interface_option(UCI_WIRELESS, UCI_WLAN_IFACE,
    			interface_name, "mode", (sta_mode ? "sta" : "ap"));
    
    	uci_reload_services();
    	return M2_PROCESS_OK;
    }
    /* end of functions taken from ieee1905d */
    
    
    static struct netif_bkcfg *get_netif_bkcfg_by_name(struct agent_config *c,
    
    		const char *name)
    
    {
    	struct netif_bkcfg *p;
    
    	list_for_each_entry(p, &c->bklist, list) {
    		if (!strcmp(name, p->name))
    			return p;
    	}
    
    	return NULL;
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static struct netif_fhcfg *get_netif_fhcfg_by_name(struct agent_config *c,
    							const char *name)
    {
    	struct netif_fhcfg *p;
    
    	list_for_each_entry(p, &c->fhlist, list) {
    		if (!strcmp(name, p->name))
    			return p;
    	}
    
    	return NULL;
    }
    
    static struct steer_policy *get_steer_policy_by_name(struct netif_fhcfg *c,
    							const char *name)
    {
    	struct steer_policy *p;
    
    	if (!c)
    		return NULL;
    
    	list_for_each_entry(p, &c->steer_policylist, list) {
    		if (!strcmp(name, p->name))
    			return p;
    	}
    
    	return NULL;
    }
    
    void stax_add_entry(struct list_head *h, char *sta_macstr)
    {
    	struct stax *n;
    
    	n = calloc(1, sizeof(struct stax));
    	if (n) {
    		snprintf(n->macstring, 18, "%s", sta_macstr);
    		list_add(&n->list, h);
    	}
    }
    
    void stax_del_entry(struct list_head *h, char *sta_macstr)
    {
    	struct stax *s, *tmp;
    
    	list_for_each_entry_safe(s, tmp, h, list) {
    		if (!strncmp(s->macstring, sta_macstr, sizeof(s->macstring))) {
    			list_del(&s->list);
    			free(s);
    			return;
    		}
    	}
    }
    
    
    static int clean_steer_btm_excl(struct netif_fhcfg *p)
    {
    	struct stax *n, *tmp;
    
    	list_for_each_entry_safe(n, tmp, &p->steer_btm_excludelist, list) {
    		list_del(&n->list);
    		free(n);
    	}
    
    	return 0;
    }
    static int clean_steer_excl(struct netif_fhcfg *p)
    {
    	struct stax *n, *tmp;
    
    	list_for_each_entry_safe(n, tmp, &p->steer_excludelist, list) {
    		list_del(&n->list);
    		free(n);
    	}
    
    	return 0;
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    void agent_config_dump(struct agent_config *cfg)
    {
    	struct netif_fhcfg *n;
    	struct steer_policy *pol;
    	struct stax *x;
    
    	if (!cfg)
    		return;
    
    	list_for_each_entry(n, &cfg->fhlist, list) {
    		dbg("name: %s\n", n->name);
    		dbg("  enabled  : %s\n", n->enabled ? "true" : "false");
    		dbg("  assocctrl: %s\n", n->assoc_control ? "true" : "false");
    
    		dbg("  Policies -------\n");
    		list_for_each_entry(pol, &n->steer_policylist, list) {
    			dbg("    name: %s\n", pol->name);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    			dbg("    enabled  : %s\n",
    					pol->enabled ? "true" : "false");
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			/* if (pol->dump_config)
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    			 *	pol->dump_config(pol, pol->policy);
    			 */
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		}
    
    		dbg("  Steer Exclude Lists -------\n");
    		list_for_each_entry(x, &n->steer_excludelist, list) {
    			dbg("    mac: %s\n", x->macstring);
    		}
    
    		dbg("  Steer BTM Exclude Lists -------\n");
    		list_for_each_entry(x, &n->steer_btm_excludelist, list) {
    			dbg("    mac: %s\n", x->macstring);
    		}
    
    		dbg("  Assoc Ctrl Lists -------\n");
    		list_for_each_entry(x, &n->assoc_ctrllist, list) {
    			dbg("    mac: %s\n", x->macstring);
    		}
    	}
    }
    
    int agent_config_defaults(struct agent *a, struct agent_config *cfg)
    {
    	struct list_head *p, *tmp;
    	struct netif_fh *ifptr;
    	struct netif_fhcfg *new;
    
    	if (!cfg)
    		return -1;
    
    	cfg->enabled = false;
    	cfg->runfreq = AGENT_RUN_AUTO;
    
    	INIT_LIST_HEAD(&cfg->fhlist);
    	INIT_LIST_HEAD(&cfg->bklist);
    	list_for_each_entry(ifptr, &a->fhlist, list) {
    		/* struct list_head pollist; */
    		struct steer_rule *r;
    
    		new = calloc(1, sizeof(struct netif_fhcfg));
    		if (!new) {
    			warn("OOM! config\n");
    			goto err_alloc;
    		}
    
    		snprintf(new->name, 16, "%s", ifptr->name);
    		new->enabled = false;
    		new->steer_btm_retry = STEER_BTM_RETRY;
    		new->steer_btm_retry_secs = STEER_BTM_RETRY_INT;
    		new->fallback_legacy = STEER_LEGACY_FALLBACK_INT;
    		new->steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
    		new->steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
    		new->assoc_control_time = ASSOC_CONTROL_INT;
    		INIT_LIST_HEAD(&new->steer_policylist);
    		/* nrules = get_registered_steer_rules(&pollist); */ /* TODO */
    		list_for_each_entry(r, &regd_steer_rules, list) {
    			struct steer_policy *pol;
    
    			pol = calloc(1, sizeof(struct steer_policy));
    			if (!pol)
    				goto err_alloc_policy;
    
    			snprintf(pol->name, 16, "%s", r->name);
    			pol->enabled = false;
    			if (r->init_config)
    				r->init_config(r, &pol->policy);
    			list_add(&pol->list, &new->steer_policylist);
    		}
    
    		INIT_LIST_HEAD(&new->steer_excludelist);
    		INIT_LIST_HEAD(&new->steer_btm_excludelist);
    		INIT_LIST_HEAD(&new->assoc_ctrllist);
    
    		ifptr->cfg = new;
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		dbg("%s: %s netif_fh->cfg = %p\n", __func__, ifptr->name,
    				ifptr->cfg);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		list_add(&new->list, &cfg->fhlist);
    	}
    
    	return 0;
    
    err_alloc_policy:
    	/* TODO */
    err_alloc:
    	list_for_each_safe(p, tmp, &cfg->fhlist) {
    		list_del(p);
    		free(p);
    	}
    
    	return -1;
    }
    
    /* create fh-iface config and initialize with default values */
    struct netif_fhcfg *create_fronthaul_iface_config(struct agent_config *cfg,
    							const char *ifname)
    {
    	struct netif_fhcfg *new;
    	struct steer_rule *r;
    
    	if (!cfg)
    		return NULL;
    
    	new = calloc(1, sizeof(struct netif_fhcfg));
    	if (!new) {
    		warn("OOM! config\n");
    		return NULL;
    	}
    
    	snprintf(new->name, 16, "%s", ifname);
    	new->enabled = true;
    	new->fallback_legacy = STEER_LEGACY_FALLBACK_INT;
    	new->steer_btm_retry_secs = STEER_BTM_RETRY_INT;
    	new->steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT;
    	new->steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT;
    	new->assoc_control_time = ASSOC_CONTROL_INT;
    
    	new->band = BAND_UNKNOWN;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	INIT_LIST_HEAD(&new->steer_policylist);
    	/* nrules = get_registered_steer_rules(&pollist); */ /* TODO */
    	list_for_each_entry(r, &regd_steer_rules, list) {
    		struct steer_policy *pol;
    
    		pol = calloc(1, sizeof(struct steer_policy));
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		if (!pol)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			goto err_oom;
    
    		snprintf(pol->name, 16, "%s", r->name);
    		pol->enabled = false;
    		if (r->init_config)
    			r->init_config(r, &pol->policy);
    		list_add(&pol->list, &new->steer_policylist);
    	}
    
    	INIT_LIST_HEAD(&new->steer_excludelist);
    	INIT_LIST_HEAD(&new->steer_btm_excludelist);
    	INIT_LIST_HEAD(&new->assoc_ctrllist);
    
    	/* f->cfg = new; */
    	dbg("%s: %s netif_fh->cfg = %p\n", __func__, new->name, new);
    
    	list_add(&new->list, &cfg->fhlist);
    
    	return new;
    
    err_oom:
    	list_flush(&new->steer_policylist, struct steer_policy, list);
    	free(new);
    	return NULL;
    }
    
    
    /* create fh-iface config and initialize with default values */
    struct netif_bkcfg *create_backhaul_iface_config(struct agent_config *cfg,
    							const char *ifname)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct netif_bkcfg *new;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	new = calloc(1, sizeof(struct netif_bkcfg));
    	if (!new) {
    		warn("OOM! config\n");
    		return NULL;
    	}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	snprintf(new->name, 16, "%s", ifname);
    	new->enabled = true;
    	new->onboarded = false;
    
    	new->band = BAND_UNKNOWN;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	/* f->cfg = new; */
    	dbg("%s: %s netif_fh->cfg = %p\n", __func__, new->name, new);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	list_add(&new->list, &cfg->bklist);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    static void config_update_entry(struct uci_context *ctx, struct uci_package *p,
    				struct uci_section *s, const char *optname,
    				int add, void *val, int len)
    {
    	struct uci_ptr ptr;
    
    	memset(&ptr, 0, sizeof(struct uci_ptr));
    	ptr.p = p;
    	ptr.s = s;
    	ptr.package = p->e.name;
    	ptr.section = s->e.name;
    	ptr.option = optname;
    	ptr.target = UCI_TYPE_OPTION;
    	ptr.flags |= UCI_LOOKUP_EXTENDED;
    	ptr.value = (char *)val;
    
    	if (add) {
    		dbg("config: add list option: %s\n", (char *)val);
    		uci_add_list(ctx, &ptr);
    	} else {
    		dbg("config: del list option: %s\n", (char *)val);
    		uci_del_list(ctx, &ptr);
    	}
    	uci_commit(ctx, &p, false);
    }
    
    int config_update(const char *confname, struct agent_config *cfg,
    			const char *section, const char *option,
    			int add,
    			void *value, int len)
    {
    	struct uci_context *ctx = NULL;
    	struct uci_package *pkg = NULL;
    	struct uci_element *e;
    
    	ctx = uci_alloc_context();
    	if (ctx && uci_load(ctx, confname, &pkg) != UCI_OK) {
    		dbg("config file '%s' not found!\n", confname);
    		free(ctx);
    		return -1;