Skip to content
Snippets Groups Projects
config.c 32.5 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"
    
    
    static 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;
    }
    
    
    static 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;
    }
    
    
    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;
    }
    
    
    168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
    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;
    }
    
    static 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;
    	}
    
    	uci_foreach_element(&pkg->sections, e) {
    		struct uci_section *s = uci_to_section(e);
    		struct uci_element *x, *tmp;
    		struct uci_option *op;
    
    		if (strcmp(s->type, section))
    			continue;
    
    		/* iter through matched 'section' for the 'option' */
    		uci_foreach_element_safe(&s->options, tmp, x) {
    			if (strcmp(x->name, option))
    				continue;
    
    			op = uci_to_option(x);
    			if (op->type == UCI_TYPE_LIST) {
    				uci_foreach_element(&op->v.list, x) {
    					if (!strncmp(x->name, value, len)) {
    						if (!add)
    							config_update_entry(ctx,
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    								pkg, s,
    								option, 0,
    								value, len);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    						goto out_exit;
    					}
    				}
    				/* add new exclude at end of list */
    				if (add)
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					config_update_entry(ctx, pkg, s, option,
    							1, value, len);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    				goto out_exit;
    			}
    		}
    		/* 'option' name not present in 'section'
    		 * Create a new one at end of 'section'.
    		 */
    		if (add)
    			config_update_entry(ctx, pkg, s, option, 1, value, len);
    
    		goto out_exit;
    	}
    out_exit:
    	uci_free_context(ctx);
    	return 0;