diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..36cbc970afdbd8ed92aba87f5fb92d7fcf800bdc --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.built +.built_check +.configured_ +.dep_files +.git_update +.prepared_* +ipkg-* +**/*.o +**/*.so +src/tags +src/mapcontroller +.configured_* +.pkgdir diff --git a/src/Makefile b/src/Makefile index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5cb37d52ce6aa9143c5a9b18baf5dfac471d1ef6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -0,0 +1,64 @@ +EXECS = mapcontroller +CFLAGS+=-I. -Iinclude -Iutils -Icore -Iipc -D_GNU_SOURCE +CFLAGS+= -g -Wall + +OBJS = \ + utils/debug.o \ + utils/liblist.o \ + utils/utils.o \ + utils/alloctrace.o + + +IPC_OBJS = \ + ipc/comm.o \ + ipc/worker.o \ + ipc/msgqueue.o \ + ipc/comm_ubus.o + +CNTLR_OBJS = \ + core/allsta.o \ + core/cntlr_ubus.o \ + core/cntlr.o \ + core/cntlr_map.o \ + core/main.o + #core/config.o \ + +LIBS = -lubus -lubox -ljson-c -lblobmsg_json -luci -pthread +LIBS += -rdynamic -ldl + +plugin_subdirs ?= $(wildcard plugins/*) +plugin_sofile = $(wildcard $(d)/*.so) +plugin_files = $(foreach d, $(plugin_subdirs), $(plugin_sofile)) + + +.PHONY: all check clean plugins FORCE + +all: $(EXECS) plugins + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +mapcontroller: $(OBJS) $(IPC_OBJS) $(CNTLR_OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +plugins: + @echo "$(plugin_subdirs)" + for i in $(plugin_subdirs); do [ -d $$i ] && $(MAKE) -C $$i all; done + + +check: FORCE + @cppcheck --quiet --enable=all --inconclusive --std=c99 \ + --suppress=variableScope \ + --suppress=unusedVariable \ + --suppress=unreadVariable \ + --suppress=funcArgNamesDifferent \ + --suppress=unusedFunction \ + -I. -Iutils -Icore -Iipc -Iinclude \ + . 2> cppcheck.out + +clean: + rm -f *.o *.so utils/*.o utils/*.so ipc/*.o core/*.o $(EXECS) + for i in $(plugin_subdirs); do [ -d $$i ] && $(MAKE) -C $$i clean; done + +FORCE: diff --git a/src/controller.conf b/src/controller.conf index 4b6bb7357cbc5ef8e00c42c6b1715317778b6b2b..f46ae102d9d97600b8f6a6152c29c36be181f931 100644 --- a/src/controller.conf +++ b/src/controller.conf @@ -68,6 +68,7 @@ config agent-policy option disallow_bsta_p2 '0' # 0 or 1 profile-2 bSTA +### do not parse following now ### config steer-param 'rssi' option rssi_threshold '-68' option hysteresis '5' @@ -78,7 +79,6 @@ config steer-param 'bssload' option priority '0' option bssload_threshold '80' -### custom rules follows ### config rule-custom option action steer option sta 'd8:32:e3:4d:35:d2' diff --git a/src/core/cntlr.c b/src/core/cntlr.c index 5d1723442f24b2bce19943523783723673a3efa8..69940929b706cf03197076a10f50b147d191c501 100644 --- a/src/core/cntlr.c +++ b/src/core/cntlr.c @@ -2,7 +2,7 @@ * cntlr.c * wifi controller core * - * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. * * Author: anjan.chanda@iopsys.eu * @@ -27,7 +27,7 @@ #include <libubus.h> #include <easy/easy.h> -#include <wifi.h> +#include <wifi.h> // FIXME: should not be included #include "debug.h" #include "utils.h" @@ -36,6 +36,7 @@ #include "hlist.h" #include "allsta.h" #include "cntlr_ubus.h" +#include "cntlr_map.h" static int update_fronthaul_bsslist(struct node *n, struct netif_fhbss *fh); @@ -129,7 +130,7 @@ static int forall_node_update_neighbors(struct controller *c) btostr((unsigned char *)nbrmac, sizeof(nbrmac), nbrstr); cntlr_dbg("Sending DEL_NEIGHBOR Async to node (%p:%p)---->\n", n, n->uobj[4]); - CMD(c->comm, c, (void *)n->uobj[4], + CMD(c->comm, c, (void *)(uintptr_t)n->uobj[4], CMD_DEL_NEIGHBOR, nbrstr, sizeof(nbrstr), NULL, NULL); @@ -1722,7 +1723,7 @@ static void cntlr_event_handler(struct ubus_context *ctx, } } -static int register_wifiagent_events(struct controller *c) +static int register_bus_events(struct controller *c) { #if 0 if (c->topology != UBUS_OBJECT_INVALID) { @@ -1730,13 +1731,123 @@ static int register_wifiagent_events(struct controller *c) ubus_register_event_handler(c->ubus_ctx, &c->ubus_ev, "topology.*"); } #endif - cntlr_dbg("Setting up event handlers for 'wifi.agent' nodes...\n"); + cntlr_dbg("Setting up bus event handlers...\n"); ubus_register_event_handler(c->ubus_ctx, &c->ubusx_ev, "ubus.object.add"); ubus_register_event_handler(c->ubus_ctx, &c->ubusx_ev, "ubus.object.remove"); + ubus_register_event_handler(c->ubus_ctx, &c->ieee1905_evh, "ieee1905.*"); return 0; } +static void ieee1905_cmdu_event_handler(void *e, struct blob_attr *msg) +{ +#define MAX_TLV_BUF 1500 /* FIXME */ + + struct controller *c = (struct controller *)e; + uint8_t tlvlist[MAX_TLV_BUF] = { 0 }; + char in_ifname[16] = {0}; + struct blob_attr *attr; + char src[18] = { 0 }; + uint8_t srcmac[6]; + int pos = 0; + uint16_t type; + int mid = 0; + struct blob_attr *tb[5]; + static const struct blobmsg_policy cmdu_attrs[5] = { + [0] = { .name = "cmdu_type", .type = BLOBMSG_TYPE_INT32 }, + [1] = { .name = "mid", .type = BLOBMSG_TYPE_INT32 }, + [2] = { .name = "recv_intf", .type = BLOBMSG_TYPE_STRING }, + [3] = { .name = "origin", .type = BLOBMSG_TYPE_STRING }, + [4] = { .name = "streams", .type = BLOBMSG_TYPE_ARRAY }, + }; + + + trace("%s: ------------>\n", __func__); + + blobmsg_parse(cmdu_attrs, 5, tb, blob_data(msg), blob_len(msg)); + + if (!tb[0] || !tb[1]) + return; + + if (tb[0]) { + type = (uint16_t)blobmsg_get_u32(tb[0]); + if (type < 0 || !is_cmdu_for_us(c, type)) + return; + } + + if (tb[1]) + mid = blobmsg_get_u32(tb[1]); + + + if (tb[2]) { + strncpy(in_ifname, blobmsg_data(tb[2]), 15); + } + + if (tb[3]) { + strncpy(src, blobmsg_data(tb[3]), 17); + hwaddr_aton(src, srcmac); + + } + + + if (tb[4]) { + int rem; + int len; + + blobmsg_for_each_attr(attr, tb[4], rem) { + char tlvstr[513] = { 0 }; + uint8_t tlv[256] = { 0 }; + + if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING) + continue; + + len = blobmsg_data_len(attr); + memcpy(tlvstr, blobmsg_data(attr), len); + + len = (len - 1) / 2; + if (pos + len > MAX_TLV_BUF) + break; + + strtob(tlvstr, len, tlv); + memcpy(&tlvlist[pos], tlv, len); + pos += len; + } + } + + cntlr_handle_map_event(c, type, mid, tlvlist, pos); + +} + +static void ubus_1905_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev, + const char *type, + struct blob_attr *msg) +{ + int i; + char *str; + struct controller *c = container_of(ev, struct controller, ieee1905_evh); + struct wifi_ev_handler { + const char *ev_type; + void (*handler)(void *ctx, struct blob_attr *ev_data); + } evs[] = { {"ieee1905.data", ieee1905_cmdu_event_handler} }; + + str = blobmsg_format_json(msg, true); + if (!str) + return; + + info("[ &agent = %p ] Received [event = %s] [val = %s]\n", + c, type, str); + + for (i = 0; i < sizeof(evs)/sizeof(evs[0]); i++) { + if (!strcmp(type, evs[i].ev_type)) { + evs[i].handler(c, msg); + break; + } + } + + free(str); +} + /* This function will also be called from topology change event */ static int get_topology(struct controller *c) { @@ -1843,6 +1954,7 @@ int start_controller(void) /* ev = &c->ubus_ev; */ /* ev->cb = cntlr_event_handler_local; */ c->ubusx_ev.cb = cntlr_event_handler; + c->ieee1905_evh.cb = ubus_1905_event_handler; c->topology = WIFI_OBJECT_INVALID; INIT_LIST_HEAD(&c->nodelist); INIT_LIST_HEAD(&c->watchlist); @@ -1862,13 +1974,15 @@ int start_controller(void) enumerate_topology_indirect(c); } perform_tasks_topology_init(c); - register_wifiagent_events(c); + register_bus_events(c); c->heartbeat.cb = cntlr_periodic_run; c->radar_timer.cb = cntlr_radar_exit; uloop_timeout_set(&c->heartbeat, 2 * 1000); cntlr_publish_object(c); + cntlr_register_module(c); + uloop_run(); /* out_and_exit: */ diff --git a/src/core/cntlr.h b/src/core/cntlr.h index ae2c0c22c52bbb1aa7abbc24d1089dfcd174b9bc..c4b2015a242a1dd6124b7439cce85a42e39efc22 100644 --- a/src/core/cntlr.h +++ b/src/core/cntlr.h @@ -1,5 +1,5 @@ /* - * cntlr.h - wificontroller header + * cntlr.h - MAP controller header file * * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. * @@ -10,8 +10,6 @@ #ifndef CNTLR_H #define CNTLR_H -#include "data.h" - #define cntlr_dbg(...) dbg(green "CNTLR: " nocl __VA_ARGS__) #define cntlr_warn(...) warn(red "CNTLR: " __VA_ARGS__) #define cntlr_info(...) info(blue "CNTLR: " nocl __VA_ARGS__) @@ -85,6 +83,7 @@ struct watchnode { struct list_head list; }; +/* struct node - maps to a 1905 device */ struct node { unsigned char hwaddr[6]; struct in_addr ipaddr; @@ -116,6 +115,7 @@ struct controller { struct ubus_context *ubus_ctx; /* struct ubus_event_handler ubus_ev; */ /** for local events */ struct ubus_event_handler ubusx_ev; /** for events from remote objs */ + struct ubus_event_handler ieee1905_evh; struct uloop_timeout heartbeat; ubus_object_t topology; int num_nodes; diff --git a/src/core/cntlr_map.c b/src/core/cntlr_map.c new file mode 100644 index 0000000000000000000000000000000000000000..f90bb5a1adec29f6a51f91d45726cd1b83c4afad --- /dev/null +++ b/src/core/cntlr_map.c @@ -0,0 +1,268 @@ +/* + * cntlr_map.c - implements MAP2 CMDUs handling + * + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <net/if_arp.h> +#include <pthread.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 "map_module.h" +#include "utils.h" +#include "debug.h" +#include "liblist.h" +#include "config.h" +#include "comm.h" +#include "msgqueue.h" +#include "worker.h" +#include "cntlr.h" + + +#define for_each_tlv(e, _buf, _len) \ +for ((e) = (struct tlv *)(_buf); \ + (e)->len && (_buf) + (_len) - (uint8_t *)(e) - 3 - (e)->len >= 0; \ + (e) = (struct tlv *)(_buf + 3 + (e)->len)) + +struct tlv { + uint8_t type; + uint16_t len; + uint8_t value[]; +} __attribute__ ((packed)); + + +typedef int (*map_cmdu_handler_t)(void *cntlr, uint16_t mid, uint8_t *tlvs, + int len); + + +int handle_topology_notification(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_topology_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_ap_autoconfig_search(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_ap_autoconfig_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_ap_autoconfig_wsc(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + + +int handle_1905_ack(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + + return 0; +} + +int handle_ap_caps_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + + return 0; +} + + +int handle_channel_pref_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + + return 0; +} + +int handle_channel_sel_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + + return 0; +} + +int handle_oper_channel_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + + return 0; +} + +int handle_sta_caps_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_ap_metrics_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_sta_link_metrics_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_unassoc_sta_link_metrics_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_beacon_metrics_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_sta_steer_btm_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_sta_steer_complete(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_backhaul_sta_steer_response(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_channel_scan_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_sta_disassoc_stats(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_assoc_status_notification(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_tunneled_message(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + +int handle_backhaul_sta_caps_report(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + + +int handle_failed_connection_msg(void *cntlr, uint16_t mid, uint8_t *tlvs, int len) +{ + return 0; +} + + +#define CMDU_TYPE_1905_START 0x0001 +#define CMDU_TYPE_1905_END 0x0009 + + +#define CMDU_TYPE_MAP_START 0x8000 +#define CMDU_TYPE_MAP_END 0x8033 + +/* mind the holes in the following two tables */ +static const map_cmdu_handler_t i1905ftable[] = { + [0x01] = handle_topology_notification, + [0x03] = handle_topology_response, + [0x07] = handle_ap_autoconfig_search, + [0x08] = handle_ap_autoconfig_response, + [0x09] = handle_ap_autoconfig_wsc, +}; + +static const map_cmdu_handler_t cntlr_mapftable[] = { + [0x00] = handle_1905_ack, + [0x02] = handle_ap_caps_report, + [0x05] = handle_channel_pref_report, + [0x07] = handle_channel_sel_response, + [0x08] = handle_oper_channel_report, + [0x0a] = handle_sta_caps_report, + [0x0c] = handle_ap_metrics_response, + [0x0e] = handle_sta_link_metrics_response, + [0x10] = handle_unassoc_sta_link_metrics_response, + [0x12] = handle_beacon_metrics_response, + [0x15] = handle_sta_steer_btm_report, + [0x17] = handle_sta_steer_complete, + [0x1a] = handle_backhaul_sta_steer_response, + [0x1c] = handle_channel_scan_report, + [0x22] = handle_sta_disassoc_stats, + [0x25] = handle_assoc_status_notification, + [0x26] = handle_tunneled_message, + [0x28] = handle_backhaul_sta_caps_report, + [0x33] = handle_failed_connection_msg, +}; + + +bool is_cmdu_for_us(void *module, uint16_t type) +{ + struct controller *c = (struct controller *)module; + + /* TODO: handle cmdu types relevant for operating profile. */ + + if (type >= CMDU_TYPE_1905_START && type <= CMDU_TYPE_MAP_START) { + if (i1905ftable[type]) + return true; + } else if (type >= CMDU_TYPE_MAP_START && type <= CMDU_TYPE_MAP_END) { + if (cntlr_mapftable[type - CMDU_TYPE_MAP_START]) + return true; + } + + return false; +} + +int cntlr_handle_map_event(void *module, uint16_t cmdutype, uint16_t mid, + uint8_t *tlvs, int len) +{ + struct controller *c = (struct controller *)module; + const map_cmdu_handler_t *f; + int idx; + int ret; + + trace("%s: ---> cmdu = %d\n", __func__, cmdutype); + + if (cmdutype >= CMDU_TYPE_MAP_START) { + idx = cmdutype - CMDU_TYPE_MAP_START; + f = cntlr_mapftable; + } else { + idx = cmdutype; + f = i1905ftable; + } + + if (f[idx]) { + ret = f[idx](c, mid, tlvs, len); + } + + //TODO: check ret + + return ret; +} diff --git a/src/core/cntlr_map.h b/src/core/cntlr_map.h new file mode 100644 index 0000000000000000000000000000000000000000..49a889d1d167af2606857ed9148aae629fae1a9a --- /dev/null +++ b/src/core/cntlr_map.h @@ -0,0 +1,13 @@ + +#ifndef CNTLR_MAP_H +#define CNTLR_MAP_H + + +extern bool is_cmdu_for_us(void *module, uint16_t type); + +extern int cntlr_handle_map_event(void *module, uint16_t cmdutype, uint16_t mid, + uint8_t *tlvs, int len); + + + +#endif /* CNTLR_MAP_H */ diff --git a/src/core/cntlr_ubus.c b/src/core/cntlr_ubus.c index 8ff05cf41316115a3e5ae1bb3d0918ebd71f5108..c42064bc7ddfbed005823be365d53067cdeb2438 100644 --- a/src/core/cntlr_ubus.c +++ b/src/core/cntlr_ubus.c @@ -1,5 +1,5 @@ /* - * cntlr_ubus.c - provides cntlr's object + * cntlr_ubus.c - provides map controller management object * * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. * @@ -20,7 +20,7 @@ #include <uci.h> #include <easy/easy.h> -#include <wifi.h> +//#include <wifi.h> #include "utils.h" #include "debug.h" #include "config.h" @@ -29,6 +29,8 @@ #include "allsta.h" #include "cntlr_ubus.h" +#include "map_module.h" + static struct controller *this_cntlr; @@ -123,10 +125,10 @@ struct ubus_method wificntlr_methods[] = { }; struct ubus_object_type wificntlr_type = - UBUS_OBJECT_TYPE("wifi.cntlr", wificntlr_methods); + UBUS_OBJECT_TYPE("map.controller", wificntlr_methods); struct ubus_object wificntlr_object = { - .name = "wifi.cntlr", + .name = "map.controller", .type = &wificntlr_type, .methods = wificntlr_methods, .n_methods = ARRAY_SIZE(wificntlr_methods), @@ -140,7 +142,7 @@ void cntlr_publish_object(struct controller *c) this_cntlr = c; ret = ubus_add_object(ctx, &wificntlr_object); if (ret) - err("Failed to add 'wifi.cntlr' ubus object: %s\n", + err("Failed to add 'map.controller' ubus object: %s\n", ubus_strerror(ret)); } @@ -154,3 +156,67 @@ void cntlr_remove_object(struct controller *c) this_cntlr = NULL; } +int cntlr_register_module(struct controller *c) +{ +#define cntlr_sign "f6dfa346957f45cb8d82dc6b77e2df61" + uint8_t cntlr_sig[16]; + char data[2 * sizeof(struct map_module) + 1] = {0}; + int ret; + uint32_t map_id; + struct blob_buf bb = {}; + struct map_module m = { + .role = MAP_ROLE_CONTROLLER, + .profile = MAP_PROFILE_2, + }; + const char *map_plugin = "map.1905"; + struct ubus_context *ctx = c->ubus_ctx; + + strtob(cntlr_sign, strlen(cntlr_sign) / 2, cntlr_sig); + memcpy(&m.sign, cntlr_sig, 16); + + /* register with map.1905 plugin */ + ret = ubus_lookup_id(ctx, map_plugin, &map_id); + if (ret) { + /* TODO: if map plugin is not installed, wait for it to appear. + * Retry after some time. + */ + dbg("plugin '%s' lookup failed. %s\n", map_plugin, + ubus_strerror(ret)); + return -1; + } + + blob_buf_init(&bb, 0); + blobmsg_add_u32(&bb, "module", wificntlr_object.id); + btostr((unsigned char *)&m, sizeof(struct map_module), data); + blobmsg_add_string(&bb, "data", data); + ret = ubus_invoke(ctx, map_id, "register", bb.head, NULL, 0, 3000); + if (ret) { + warn("Failed to 'register' with %s (err = %s)\n", + map_plugin, ubus_strerror(ret)); + } + + blob_buf_free(&bb); + +#if 1 // testing notify event + blob_buf_init(&bb, 0); + blobmsg_add_u32(&bb, "module", wificntlr_object.id); + blobmsg_add_string(&bb, "data", "Hello World"); + ret = ubus_notify(ctx, &wificntlr_object, "1905-NOTIFICATION", bb.head, -1); + if (ret) + warn("Failed to notify 1905-NOTIFICATION\n"); + + blob_buf_free(&bb); + + // testing send + blob_buf_init(&bb, 0); + blobmsg_add_u32(&bb, "type", 0x800a); + blobmsg_add_string(&bb, "data", "aabbccddeeff00112233445566778899"); + ret = ubus_invoke(ctx, map_id, "send", bb.head, NULL, 0, 1000); + if (ret) + warn("map.1905 plugin 'send' ret (%s)\n", ubus_strerror(ret)); + + blob_buf_free(&bb); +#endif + + return 0; +} diff --git a/src/core/cntlr_ubus.h b/src/core/cntlr_ubus.h index 3750855355e8c4c609445e9d50b08ce1e9ccf20a..b7eb46dc6f0c36ee9c242dbb0dac14f9ff9fd981 100644 --- a/src/core/cntlr_ubus.h +++ b/src/core/cntlr_ubus.h @@ -1,7 +1,7 @@ /* * cntlr_ubus.h - cntlr's ubus object header * - * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. * * Author: anjan.chanda@iopsys.eu * @@ -10,7 +10,11 @@ #ifndef CNTLR_UBUS_H #define CNTLR_UBUS_H -void cntlr_publish_object(struct controller *c); -void cntlr_remove_object(struct controller *c); +extern void cntlr_publish_object(struct controller *c); +extern void cntlr_remove_object(struct controller *c); + + +extern int cntlr_register_module(struct controller *c); + #endif /* CNTLR_UBUS_H */ diff --git a/src/core/config.c b/src/core/config.c new file mode 100644 index 0000000000000000000000000000000000000000..bbcbf746141a57a48b28b79d71b7f6cd19123380 --- /dev/null +++ b/src/core/config.c @@ -0,0 +1,817 @@ +/* + * config.c - controller configuration handling + * + * Copyright (C) 2020 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" +#include "cntlr.h" + +//TODO: remove +struct stax{ + char macstring[32]; /* ':' separated mac address string */ + struct list_head list; +}; +//////////// + +static struct netif_fhcfg *get_netif_fhcfg_by_name(struct cntlr_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; + } + } +} + +void cntlr_config_dump(struct cntlr_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); + dbg(" enabled : %s\n", pol->enabled ? "true" : "false"); + /* if (pol->dump_config) + pol->dump_config(pol, pol->policy); */ + } + + 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 cntlr_config_defaults(struct cntlr *a, struct cntlr_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, ®d_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; + dbg("%s: %s netif_fh->cfg = %p\n", __func__, ifptr->name, ifptr->cfg); + 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 cntlr_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; + INIT_LIST_HEAD(&new->steer_policylist); + /* nrules = get_registered_steer_rules(&pollist); */ /* TODO */ + list_for_each_entry(r, ®d_steer_rules, list) { + struct steer_policy *pol; + + pol = calloc(1, sizeof(struct steer_policy)); + if (!pol) { + 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; +} + +int cntlr_config_reload(const char *confname, struct cntlr_config *cfg) +{ + 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) { + err("config file '%s' not found!\n", confname); + uci_free_context(ctx); + return -1; + } + + uci_foreach_element(&pkg->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "wificntlr")) { + struct uci_element *x; + struct uci_option *op; + bool enabled = false; + + uci_foreach_element(&s->options, x) { + op = uci_to_option(x); + if (!strncmp(x->name, "enabled", 7) && + op->type == UCI_TYPE_STRING) { + dbg("wificntlr: enabled = %d\n", atoi(op->v.string)); + enabled = atoi(op->v.string) == 1 ? + true : false; + cfg->enabled = enabled; + } else if (!strncmp(x->name, "runfreq", 7) && + op->type == UCI_TYPE_STRING) { + dbg("wificntlr: speed = %s\n", op->v.string); + if (!strncasecmp(op->v.string, "auto", 4)) + cfg->runfreq = AGENT_RUN_AUTO; + else if (!strncasecmp(op->v.string, "low", 3)) + cfg->runfreq = AGENT_RUN_LOW; + else if (!strncasecmp(op->v.string, "high", 4)) + cfg->runfreq = AGENT_RUN_HIGH; + else if (!strncasecmp(op->v.string, "off", 3)) + cfg->runfreq = AGENT_RUN_OFF; + } /* else if (!strncmp(x->name, "ifname", 6) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("wificntlr: ifname: "); + uci_foreach_element(&op->v.list, xi) { + dbg("%s ", xi->name); + } + dbg("\n"); + } */ + } + } else if (!strcmp(s->type, "fh-iface")) { + struct uci_element *x; + struct uci_option *op; + bool disabled = false; + unsigned int steer_btm_retry = STEER_BTM_RETRY; + unsigned int steer_btm_retry_secs = STEER_BTM_RETRY_INT; + unsigned int legacy = STEER_LEGACY_FALLBACK_INT; + unsigned int steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT; + unsigned int steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT; + unsigned int assoc_ctrl_secs = ASSOC_CONTROL_INT; + struct netif_fhcfg *ifcfg = NULL; + + uci_foreach_element(&s->options, x) { + op = uci_to_option(x); + if (!strncmp(x->name, "disabled", 8) && + op->type == UCI_TYPE_STRING) { + dbg("wificntlr: disabled = %d\n", atoi(op->v.string)); + disabled = atoi(op->v.string) == 1 ? + true : false; + } else if (!strncmp(x->name, "ifname", 6) && + op->type == UCI_TYPE_STRING) { + /* struct uci_element *xi; */ + + ifcfg = get_netif_fhcfg_by_name(cfg, op->v.string); + if (!ifcfg) { + ifcfg = create_fronthaul_iface_config(cfg, op->v.string); + if (!ifcfg) { + warn("%s: OOM!\n", __func__); + break; + } + } else { + warn("Duplicate 'fh-iface %s' config!! ignore\n", + op->v.string); + } + + list_flush(&ifcfg->steer_excludelist, struct stax, list); + list_flush(&ifcfg->steer_btm_excludelist, struct stax, list); + list_flush(&ifcfg->assoc_ctrllist, struct stax, list); + + } else if (!strncmp(x->name, "btm_retry", 9) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: btm_retry = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + steer_btm_retry = atoi(op->v.string); + } else if (!strncmp(x->name, "btm_retry_secs", 14) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: btm_retry_secs = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + steer_btm_retry_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "fallback_legacy", 15) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: legacy = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + legacy = atoi(op->v.string); + } else if (!strncmp(x->name, "steer_legacy_reassoc_secs", 25) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: steer_legacy_rassoc_secs = %d\n", + atoi(op->v.string)); + steer_legacy_reassoc_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "steer_legacy_retry_secs", 23) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: steer_legacy_retry_secs = %d\n", + atoi(op->v.string)); + steer_legacy_retry_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "assoc_ctrl", 10) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: assoc_ctlr = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + assoc_ctrl_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "steer", 5) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("Steer: param: "); + uci_foreach_element(&op->v.list, xi) { + struct steer_policy *p = NULL; + struct steer_rule *r; + + dbg("%s ", xi->name); + p = get_steer_policy_by_name(ifcfg, xi->name); + if (!p) { + /* TODO? */ + dbg("TODO!! steer before ifname\n"); + continue; + } + p->enabled = true; + r = get_steer_rule_by_name(xi->name); + if (r) + r->enabled = true; + } + dbg("\n"); + } else if (!strncmp(x->name, "exclude_btm", 11) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("Steer: exclude_btm: "); + uci_foreach_element(&op->v.list, xi) { + dbg("%s ", xi->name); + stax_add_entry(&ifcfg->steer_btm_excludelist, xi->name); + } + dbg("\n"); + } else if (!strncmp(x->name, "exclude", 7) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("Steer: exclude: "); + uci_foreach_element(&op->v.list, xi) { + dbg("%s ", xi->name); + stax_add_entry(&ifcfg->steer_excludelist, xi->name); + } + dbg("\n"); + } + } + if (ifcfg) { + ifcfg->enabled = !disabled; + ifcfg->steer_btm_retry = steer_btm_retry; + ifcfg->steer_btm_retry_secs = steer_btm_retry_secs; + if (legacy) { + /* wait atleast these many secs before + * falling to legacy steering. + */ + ifcfg->fallback_legacy = 1 + + steer_btm_retry * steer_btm_retry_secs + + legacy; + } + ifcfg->steer_legacy_reassoc_secs = steer_legacy_reassoc_secs; + ifcfg->steer_legacy_retry_secs = steer_legacy_retry_secs; + ifcfg->assoc_control_time = assoc_ctrl_secs; + } + } +#if 0 + else if (!strcmp(s->type, "steer")) { + struct uci_element *x, *tmp; + struct uci_option *op; + bool enabled = false; + unsigned int legacy = STEER_LEGACY_FALLBACK_INT; + unsigned int steer_legacy_reassoc_secs = STEER_LEGACY_REASSOC_INT; + unsigned int steer_legacy_retry_secs = STEER_LEGACY_RETRY_INT; + unsigned int steer_btm_retry = STEER_BTM_RETRY; + unsigned int steer_btm_retry_secs = STEER_BTM_RETRY_INT; + struct netif_fhcfg *ifcfg = NULL; + + uci_foreach_element_safe(&s->options, tmp, x) { + op = uci_to_option(x); + if (!strncmp(x->name, "enabled", 7) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: enabled = %d\n", atoi(op->v.string)); + enabled = atoi(op->v.string) == 1 ? + true : false; + } else if (!strncmp(x->name, "btm_retry", 9) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: btm_retry = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + steer_btm_retry = atoi(op->v.string); + } else if (!strncmp(x->name, "btm_retry_secs", 14) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: btm_retry_secs = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + steer_btm_retry_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "fallback_legacy", 15) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: legacy = %d\n", + atoi(op->v.string)); + if (atoi(op->v.string) >= 0) + legacy = atoi(op->v.string); + } else if (!strncmp(x->name, "steer_legacy_reassoc_secs", 25) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: steer_legacy_rassoc_secs = %d\n", + atoi(op->v.string)); + steer_legacy_reassoc_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "steer_legacy_retry_secs", 23) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: steer_legacy_retry_secs = %d\n", + atoi(op->v.string)); + steer_legacy_retry_secs = atoi(op->v.string); + } else if (!strncmp(x->name, "ifname", 6) && + op->type == UCI_TYPE_STRING) { + dbg("Steer: ifname = %s\n", op->v.string); + ifcfg = get_netif_fhcfg_by_name(cfg, op->v.string); + if (!ifcfg) { + warn("stale config for '%s'\n", op->v.string); + break; + } + + list_flush(&ifcfg->steer_excludelist, struct stax, list); + list_flush(&ifcfg->steer_btm_excludelist, struct stax, list); + list_flush(&ifcfg->assoc_ctrllist, struct stax, list); + } else if (!strncmp(x->name, "param", 5) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("Steer: param: "); + uci_foreach_element(&op->v.list, xi) { + struct steer_policy *p = NULL; + struct steer_rule *r; + + dbg("%s ", xi->name); + p = get_steer_policy_by_name(ifcfg, xi->name); + if (!p) { + dbg("TODO ?"); + continue; + } + p->enabled = enabled; + r = get_steer_rule_by_name(xi->name); + if (r) + r->enabled = enabled; + } + dbg("\n"); + } else if (!strncmp(x->name, "exclude_btm", 11) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("Steer: exclude_btm: "); + uci_foreach_element(&op->v.list, xi) { + dbg("%s ", xi->name); + stax_add_entry(&ifcfg->steer_btm_excludelist, xi->name); + } + dbg("\n"); + } else if (!strncmp(x->name, "exclude", 7) && + op->type == UCI_TYPE_LIST) { + struct uci_element *xi; + + dbg("Steer: exclude: "); + uci_foreach_element(&op->v.list, xi) { + dbg("%s ", xi->name); + stax_add_entry(&ifcfg->steer_excludelist, xi->name); + } + dbg("\n"); + } + } + + if (ifcfg) { + ifcfg->enabled = enabled; + ifcfg->steer_btm_retry = steer_btm_retry; + ifcfg->steer_btm_retry_secs = steer_btm_retry_secs; + if (legacy) { + /* wait atleast these many secs before + * falling to legacy steering. + */ + ifcfg->fallback_legacy = 1 + + steer_btm_retry * steer_btm_retry_secs + + legacy; + } + ifcfg->steer_legacy_reassoc_secs = steer_legacy_reassoc_secs; + ifcfg->steer_legacy_retry_secs = steer_legacy_retry_secs; + } + } +#endif + else if (!strcmp(s->type, "steer-param")) { + /* For every steer-param name, find matching rule + * module. If one is available, then the rule module + * takes care of handling the get/set config for this + * parameter specific optins. + */ + struct steer_rule *r; + + dbg("Steer-param: %s\n", s->e.name); + r = get_steer_rule_by_name(s->e.name); + if (r) { + dbg("Rule to handle steer-param '%s' available\n", s->e.name); + r->config(r, cfg, s); + } + } else if (!strncmp(s->type, "wifi-iface", 10)) { + struct uci_element *x; + int mob_id = 500; + + uci_foreach_element(&s->options, x) { + struct uci_option *op; + + op = uci_to_option(x); + if (!strncmp(x->name, "mobility_domain", 7) && + op->type == UCI_TYPE_STRING) + mob_id = atoi(op->v.string); + } + + dbg("wireless: mobility_domain = %d\n", mob_id); + memcpy(cfg->mobility_domain, &mob_id, 2); + dbg("wireless: mobility_domain hex = %02x:%02x\n", + cfg->mobility_domain[0], + cfg->mobility_domain[1]); + + break; + } + } + + uci_unload(ctx, pkg); + uci_free_context(ctx); + return 0; +} + +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 cntlr_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, + pkg, s, option, 0, value, len); + + goto out_exit; + } + } + /* add new exclude at end of list */ + if (add) + config_update_entry(ctx, pkg, s, option, 1, value, len); + + 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; +} + +int config_update2(const char *confname, struct cntlr_config *cfg, + const char *section_type, + const char *match_option, + const char *match_option_value, + 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; + const char *optstring; + + if (strcmp(s->type, section_type)) + continue; + + if (match_option && match_option_value) { + optstring = uci_lookup_option_string(ctx, s, match_option); + if (!optstring || strcmp(optstring, match_option_value)) + 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, + pkg, s, option, + 0, value, len); + } + + goto out_exit; + } + } + /* add new 'option' at end of list */ + if (add) { + config_update_entry(ctx, pkg, s, option, + 1, value, len); + } + + 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; +} + + +#if 0 +struct config uci_config = { + .name = "uci", + .priv = uci_ctx; + .get = uci_get_config, + .set = uci_set_config, + .init = uci_setup, + .exit = uci_exit, +}; + +#define priv_get_config(priv) container_of(priv, struct config, priv) + +void register_config(struct config *c) +{ + static struct uci_context *ctx; + static struct uci_package *pkg; + struct uci_element *e; + int ret = 0; + + if (uci_ctx) + return priv_get_config(uci_ctx); + + ctx = uci_alloc_context(); + if (ctx) { + uci_ctx = ctx; + memcpy(c, &uci_config, sizeof(*cfg)); + } + if (uci_load(ctx, "wificntlr", &pkg)) + return -1; + + uci_foreach_element(&pkg->sections, e) { + struct uci_section *s = uci_to_section(e); + const char *option_val; + + if (strcmp(s->type, "wificntlr")) + continue; + + option_val = uci_lookup_option_string(ctx, s, name); + if (option_val) + sprintf(val, "%s", option_val); + else + ret = -1; + } + uci_free_context(ctx); + return ret; + +} +#endif diff --git a/src/core/config.h b/src/core/config.h index 049702d9598615e29d3e1a9c895039da9a8649b3..e9c6a9b93c330adc88143f3bc51c2dc0944a9af9 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -1,7 +1,7 @@ /* - * config.h - controller configurations + * config.h - MAP Controller configuration header file * - * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. * * Author: anjan.chanda@iopsys.eu * @@ -10,14 +10,77 @@ #ifndef CONFIG_H #define CONFIG_H -/* TODO: placeholder for now */ + +enum wifi_band { + WIFI_BAND_NONE, + WIFI_BAND_2 = 1 << 0, /**< 0x1 for 2.4Ghz band. */ + WIFI_BAND_5 = 1 << 1, /**< 0x2 for 5Ghz band. */ + WIFI_BAND_60 = 1 << 2, /**< 0x4 for 60Ghz band. */ + WIFI_BAND_6 = 1 << 3, /**< 0x8 for 6Ghz */ +}; + +struct iface_credential { + uint8_t band; + uint8_t bssid[6]; + uint8_t ssid[33]; + char encryption[64]; + uint8_t key[64]; + uint16_t vlan_id; +}; + +enum agent_steer_policy { + AGENT_STEER_DISALLOW, /* agent shall not steer based on rcpi */ + AGENT_STEER_RCPI_MANDATE, /* agent shall steer based on rcpi */ + AGENT_STEER_RCPI_ALLOW, /* agent may steer based on rcpi */ +}; + +struct agent_policy { + uint8_t alid[6]; /* ieee1905 AL macaddress */ + enum agent_steer_policy policy; /* 0, 1, 2 - see MultiAP specs */ + uint8_t util_threshold; /* utilization as in BSS load IE */ + uint8_t rcpi_threshold; /* 0 - 220 */ + bool report_scan; /* report independent scans */ + bool report_sta_assocfails; /* report STA assoc fails */ + uint8_t report_metric_periodic; /* 0 = disable, else 1 - 255 in secs */ + uint8_t report_rcpi_threshold; /* 0, or 1 - 220 */ + uint8_t report_util_threshold; /* 0, or channel utilization value */ + bool include_sta_stats; /* sta stats in AP metric response */ + bool include_sta_metric; /* sta metric in AP metric response */ + uint16_t pvid; /* primary vlan id */ + uint8_t pcp_default; /* default vlan pcp */ + bool disallow_bsta_p1; /* disallow profile1 bSTA link */ + bool disallow_bsta_p2; /* disallow profile2 bSTA link */ + + struct list_head list; /* link to next policy */ +}; struct controller_config { bool enabled; -}; + uint8_t wps_supp_bands; /* WPS registrar supported bands */ + + struct iface_credential fh5; + struct iface_credential fh2; -struct config { - struct controller_config cntlr; + struct iface_credential bk5; + struct iface_credential bk2; + + struct list_head policylist; /* list of struct agent_policy */ }; +struct controller; + +int cntlr_config_reload(const char *confname, struct controller_config *cfg); +int cntlr_config_defaults(struct controller *c, struct controller_config *cfg); +void cntlr_config_dump(struct controller_config *cfg); + +int config_update(const char *confname, struct controller_config *cfg, + const char *section, const char *option, int add, + void *value, int len); + + +int config_update2(const char *confname, struct controller_config *cfg, + const char *section_type, + const char *match_option, const char *match_option_value, + const char *option, int add, void *value, int len); + #endif diff --git a/src/include/map_module.h b/src/include/map_module.h index 9d035472d86b1409e1cf54dae35f1341ea95bb84..50dece6133784c763a28c6f7b69447e942129c2d 100644 --- a/src/include/map_module.h +++ b/src/include/map_module.h @@ -1,5 +1,5 @@ /* - * map_module.h - multi-ap module API header. + * map_module.h - multi-ap module(s) registration API header. * * Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved. * @@ -9,11 +9,6 @@ * */ -//TODO: this file belongs to the 'map-1905' plugin. -//MAP agent/controller use the functions here to register with the map-1905 -//plugin. - - #ifndef MAP_MODULE_H #define MAP_MODULE_H @@ -34,7 +29,7 @@ extern "C" { */ typedef int (*cmdu_handler_t)(uint16_t cmdu_type, void *msg, size_t msglen); -typedef void *map_module_id_t; +typedef uint8_t map_module_id_t[32]; enum map_role { MAP_ROLE_AGENT = 0x1, @@ -56,7 +51,8 @@ enum map_profile { * module's CMDU handler with the cmdu_type and the 'msg' payload. */ struct map_module { - map_module_id_t *uid; /**< id assigned by the libmap plugin */ + map_module_id_t uid; /**< handle to module object */ + uint8_t sign[32]; uint32_t role; /**< bitmap of MAP_ROLE_* */ enum map_profile profile; /**< one of MAP_PROFILE_* */ cmdu_handler_t map_cmdu_rx; /**< multiap CMDU handler function */ diff --git a/src/ipc/comm.c b/src/ipc/comm.c index eacdbdf6e703981e78ba405dcd08e6e0b4e545d1..4bd7b596bac3eb8c5bb04e9664954b55d8d4c431 100644 --- a/src/ipc/comm.c +++ b/src/ipc/comm.c @@ -1,5 +1,6 @@ /* - * comm.c - ipc utility constructs - msg_queue, worker thread etc. + * comm_impl.c + * Communication interface between controller and agents. * * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. * @@ -7,152 +8,48 @@ * */ -#include <stdio.h> -#include <stdlib.h> +#include <string.h> +#include <alloca.h> #include <pthread.h> -#include "liblist.h" -#include "utils.h" + +#include <libubox/list.h> #include "debug.h" #include "comm.h" -//TODO: almost everything will be moved to utils - -int mq_init(struct msg_queue *q) -{ - INIT_LIST_HEAD(&q->mq); - pthread_mutex_init(&q->lock, NULL); - pthread_cond_init(&q->empty, NULL); - - return 0; -} - -int mq_destroy(struct msg_queue *q) -{ - pthread_mutex_destroy(&q->lock); - pthread_cond_destroy(&q->empty); - - return 0; -} - -void mq_flush(struct msg_queue *q) -{ - pthread_mutex_lock(&q->lock); - list_flush(&q->mq, struct msg, list); - //TODO: free msg.data - pthread_mutex_unlock(&q->lock); -} - -static int __mq_length(struct msg_queue *q) -{ - struct list_head *p; - int count = 0; - - list_for_each(p, &q->mq) - count++; - - return count; +int _comm_cmd(void *comm, void *self, void *dest, + int cmd, void *data, int len, + void (*response)(void *self, void *resp, int rlen, void *cookie), + void *cookie, int is_async) +{ +#define CMD_TIMEOUT 8 /* in secs */ + int ret = -1; + struct CMD_struct *cmdi; + + if (cmd <= CMD_UNDEFINED || cmd > CMD_MAX) { + warn("Cntlr: Unknown CMD '%d'\n", cmd); + return ret; + } + + cmdi = alloca(sizeof(struct CMD_struct)); + if (!cmdi) { + warn("oom: alloc CMD failed!\n"); + return ret; + } + memset(cmdi, 0, sizeof(*cmdi)); + cmdi->id = cmd; + cmdi->len = len; + cmdi->data = data; + cmdi->timeout = CMD_TIMEOUT; + cmdi->resp = response; + cmdi->self = self; + cmdi->cookie = cookie; + cmdi->ipc = NULL; + trace_cmd("Sending CMD %s (&cmd = %p, dest = %p) --->\n", + CMDSTRING(cmd), &cmdi, dest); + + ret = is_async ? + comm_send_msg_async(comm, dest, cmdi) : + comm_send_msg(comm, dest, cmdi); + + return ret; } - -void mq_free_msg(struct msg *m) -{ - if (m->msg.data) - free(m->msg.data); - - free(m); -} - -struct msg *mq_alloc_msg(int sizeof_ipc) -{ - struct msg *m; - - m = calloc(1, sizeof(struct msg) + sizeof_ipc); - if (!m) - return NULL; - - m->msg.ipc = sizeof_ipc ? (m + 1) : NULL; - - return m; -} - -void mq_wait_for_msg(struct msg_queue *q) -{ - pthread_mutex_lock(&q->lock); - while (list_empty(&q->mq)) - pthread_cond_wait(&q->empty, &q->lock); - - pthread_mutex_unlock(&q->lock); -} - -void mq_signal_msg(struct msg_queue *q) -{ - pthread_mutex_lock(&q->lock); - if (!list_empty(&q->mq)) - pthread_cond_signal(&q->empty); - - pthread_mutex_unlock(&q->lock); -} - -void mq_enqueue_msg(struct msg_queue *q, struct msg *m) -{ - pthread_mutex_lock(&q->lock); - list_add_tail(&m->list, &q->mq); - loud("After enqueue msg (%p), qlen =%d\n", - m, __mq_length(q)); - pthread_mutex_unlock(&q->lock); -} - -static struct msg *__mq_dequeue_msg(struct msg_queue *q) -{ - struct msg *m = NULL; - - m = list_first_entry(&q->mq, struct msg, list); - if (m) - list_del(&m->list); - - return m; -} - -struct msg *mq_dequeue_msg(struct msg_queue *q) -{ - struct msg *m; - - pthread_mutex_lock(&q->lock); - while (list_empty(&q->mq)) - pthread_cond_wait(&q->empty, &q->lock); - - m = __mq_dequeue_msg(q); - loud("After dequeue msg (%p), qlen =%d\n", - m, __mq_length(q)); - - pthread_mutex_unlock(&q->lock); - - return m; -} - -void *worker_run(void *data) -{ - struct worker *w = (struct worker *)data; - - for (;;) - w->func(w->data); - - return NULL; -} - -int worker_init(struct worker *w) -{ - int ret; - - ret = pthread_create(&w->id, NULL, worker_run, w); - if (ret) - return -1; - - return 0; -} - -void worker_exit(struct worker *w) -{ - pthread_cancel(w->id); - pthread_join(w->id, NULL); -} - diff --git a/src/ipc/comm.h b/src/ipc/comm.h index abc46087f37e41e114260758b0371627bae0e755..9c75bc34f007626fe72d1df1ec31134cf4f67da7 100644 --- a/src/ipc/comm.h +++ b/src/ipc/comm.h @@ -35,8 +35,11 @@ enum { CMD_GET_MOBID, CMD_GET_OUI, CMD_FLUSH_ARP, - CMD_DISCONNECT_STA, + CMD_DISCONNECT_STA = 20, + + CMD_MAP_CMDU, /* .. others as needed */ + CMD_MAX, }; @@ -62,6 +65,8 @@ static char *_CMDSTRING[] = { [CMD_GET_OUI] = "GET_OUI", [CMD_FLUSH_ARP] = "FLUSH_ARP", [CMD_DISCONNECT_STA] = "CMD_DISCONNECT_STA", + + [CMD_MAP_CMDU] = "CMD_MAP_CMDU", }; static inline char *CMDSTRING(int m) @@ -130,36 +135,6 @@ struct msg { struct list_head list; }; -/* shared msg queue */ -struct msg_queue { - struct list_head mq; - pthread_mutex_t lock; - pthread_cond_t empty; -}; - -int mq_init(struct msg_queue *q); -int mq_destroy(struct msg_queue *q); -void mq_flush(struct msg_queue *q); -struct msg *mq_alloc_msg(int sizeof_ipc); -void mq_free_msg(struct msg *m); - -#define msg_ipc(__m) __m->msg.ipc - -extern void mq_wait_for_msg(struct msg_queue *q); -extern void mq_signal_msg(struct msg_queue *q); -extern void mq_enqueue_msg(struct msg_queue *q, struct msg *m); -extern struct msg *mq_dequeue_msg(struct msg_queue *q); - -/* worker thread */ -struct worker { - pthread_t id; - void *data; - int (*func)(void *data); -}; - -extern int worker_init(struct worker *w); -extern void worker_exit(struct worker *w); -//extern void *worker_run(void *data); //TODO: following structs will be deprecated in favour of TLVs diff --git a/src/ipc/comm_impl.c b/src/ipc/comm_impl.c deleted file mode 100644 index 4bd7b596bac3eb8c5bb04e9664954b55d8d4c431..0000000000000000000000000000000000000000 --- a/src/ipc/comm_impl.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * comm_impl.c - * Communication interface between controller and agents. - * - * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. - * - * Author: anjan.chanda@iopsys.eu - * - */ - -#include <string.h> -#include <alloca.h> -#include <pthread.h> - -#include <libubox/list.h> -#include "debug.h" -#include "comm.h" - -int _comm_cmd(void *comm, void *self, void *dest, - int cmd, void *data, int len, - void (*response)(void *self, void *resp, int rlen, void *cookie), - void *cookie, int is_async) -{ -#define CMD_TIMEOUT 8 /* in secs */ - int ret = -1; - struct CMD_struct *cmdi; - - if (cmd <= CMD_UNDEFINED || cmd > CMD_MAX) { - warn("Cntlr: Unknown CMD '%d'\n", cmd); - return ret; - } - - cmdi = alloca(sizeof(struct CMD_struct)); - if (!cmdi) { - warn("oom: alloc CMD failed!\n"); - return ret; - } - memset(cmdi, 0, sizeof(*cmdi)); - cmdi->id = cmd; - cmdi->len = len; - cmdi->data = data; - cmdi->timeout = CMD_TIMEOUT; - cmdi->resp = response; - cmdi->self = self; - cmdi->cookie = cookie; - cmdi->ipc = NULL; - trace_cmd("Sending CMD %s (&cmd = %p, dest = %p) --->\n", - CMDSTRING(cmd), &cmdi, dest); - - ret = is_async ? - comm_send_msg_async(comm, dest, cmdi) : - comm_send_msg(comm, dest, cmdi); - - return ret; -} diff --git a/src/ipc/comm_ubus.c b/src/ipc/comm_ubus.c index 3e113774c92e34655720e1bfc5bf1b516a0db9bd..fafb1dc6fbb06e0cbaa19a1a12cf46bf59d72511 100644 --- a/src/ipc/comm_ubus.c +++ b/src/ipc/comm_ubus.c @@ -105,7 +105,7 @@ int comm_ubus_send_msg(void *handle, void *dest, struct CMD_struct *cmd) if (cmd->data) blobmsg_add_string(&bb, "data", cmd->data); - ret = ubus_invoke(priv->ctx, (ubus_object_t)dest, + ret = ubus_invoke(priv->ctx, (ubus_object_t)(uintptr_t)dest, "cmd", bb.head, comm_ubus_msg_resphandler, cmd, cmd->timeout * 1000); @@ -136,7 +136,7 @@ int comm_ubus_send_msg_async(void *handle, void *dest, struct CMD_struct *cmd) blobmsg_add_string(&bb, "data", cmd->data); ret = ubus_invoke_async(priv->ctx, - (ubus_object_t)dest, "cmdi", + (ubus_object_t)(uintptr_t)dest, "cmdi", bb.head, ureq); if (ret) { warn("CMD %s failed (ret = %s)\n", diff --git a/src/ipc/msgqueue.c b/src/ipc/msgqueue.c new file mode 100644 index 0000000000000000000000000000000000000000..52c1afbe065afaf9142fedab7cecb4f7aec85e4b --- /dev/null +++ b/src/ipc/msgqueue.c @@ -0,0 +1,128 @@ +/* + * msgqueue.c - message queue utility functions. + * + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include "liblist.h" +#include "utils.h" +#include "debug.h" +#include "comm.h" +#include "msgqueue.h" + +int mq_init(struct msg_queue *q) +{ + INIT_LIST_HEAD(&q->mq); + pthread_mutex_init(&q->lock, NULL); + pthread_cond_init(&q->empty, NULL); + + return 0; +} + +int mq_destroy(struct msg_queue *q) +{ + pthread_mutex_destroy(&q->lock); + pthread_cond_destroy(&q->empty); + + return 0; +} + +void mq_flush(struct msg_queue *q) +{ + pthread_mutex_lock(&q->lock); + list_flush(&q->mq, struct msg, list); + //TODO: free msg.data + pthread_mutex_unlock(&q->lock); +} + +static int __mq_length(struct msg_queue *q) +{ + struct list_head *p; + int count = 0; + + list_for_each(p, &q->mq) + count++; + + return count; +} + +void mq_free_msg(struct msg *m) +{ + if (m->msg.data) + free(m->msg.data); + + free(m); +} + +struct msg *mq_alloc_msg(int sizeof_ipc) +{ + struct msg *m; + + m = calloc(1, sizeof(struct msg) + sizeof_ipc); + if (!m) + return NULL; + + m->msg.ipc = sizeof_ipc ? (m + 1) : NULL; + + return m; +} + +void mq_wait_for_msg(struct msg_queue *q) +{ + pthread_mutex_lock(&q->lock); + while (list_empty(&q->mq)) + pthread_cond_wait(&q->empty, &q->lock); + + pthread_mutex_unlock(&q->lock); +} + +void mq_signal_msg(struct msg_queue *q) +{ + pthread_mutex_lock(&q->lock); + if (!list_empty(&q->mq)) + pthread_cond_signal(&q->empty); + + pthread_mutex_unlock(&q->lock); +} + +void mq_enqueue_msg(struct msg_queue *q, struct msg *m) +{ + pthread_mutex_lock(&q->lock); + list_add_tail(&m->list, &q->mq); + loud("After enqueue msg (%p), qlen =%d\n", + m, __mq_length(q)); + pthread_mutex_unlock(&q->lock); +} + +static struct msg *__mq_dequeue_msg(struct msg_queue *q) +{ + struct msg *m = NULL; + + m = list_first_entry(&q->mq, struct msg, list); + if (m) + list_del(&m->list); + + return m; +} + +struct msg *mq_dequeue_msg(struct msg_queue *q) +{ + struct msg *m; + + pthread_mutex_lock(&q->lock); + while (list_empty(&q->mq)) + pthread_cond_wait(&q->empty, &q->lock); + + m = __mq_dequeue_msg(q); + loud("After dequeue msg (%p), qlen =%d\n", + m, __mq_length(q)); + + pthread_mutex_unlock(&q->lock); + + return m; +} diff --git a/src/ipc/msgqueue.h b/src/ipc/msgqueue.h new file mode 100644 index 0000000000000000000000000000000000000000..5a6de3618b4a9743c6d1ff6175a9af55ed33246b --- /dev/null +++ b/src/ipc/msgqueue.h @@ -0,0 +1,34 @@ +/* + * msgqueue.h + * Simple message queue header file + * + * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + */ + +#ifndef MSGQUEUE_H +#define MSGQUEUE_H + +/* shared msg queue */ +struct msg_queue { + struct list_head mq; + pthread_mutex_t lock; + pthread_cond_t empty; +}; + +extern int mq_init(struct msg_queue *q); +extern int mq_destroy(struct msg_queue *q); +extern void mq_flush(struct msg_queue *q); +extern struct msg *mq_alloc_msg(int sizeof_ipc); +extern void mq_free_msg(struct msg *m); + +#define msg_ipc(__m) __m->msg.ipc + +extern void mq_wait_for_msg(struct msg_queue *q); +extern void mq_signal_msg(struct msg_queue *q); +extern void mq_enqueue_msg(struct msg_queue *q, struct msg *m); +extern struct msg *mq_dequeue_msg(struct msg_queue *q); + +#endif /* MSGQUEUE_H */ diff --git a/src/ipc/worker.c b/src/ipc/worker.c new file mode 100644 index 0000000000000000000000000000000000000000..808469ab2afcdc89b81007acb656ddf6d875eaf3 --- /dev/null +++ b/src/ipc/worker.c @@ -0,0 +1,44 @@ +/* + * worker.c - worker thread and pool utility functions + * + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include "liblist.h" +#include "utils.h" +#include "debug.h" +#include "comm.h" +#include "worker.h" + +static void *worker_run(void *data) +{ + struct worker *w = (struct worker *)data; + + for (;;) + w->func(w->data); + + return NULL; +} + +int worker_init(struct worker *w) +{ + int ret; + + ret = pthread_create(&w->id, NULL, worker_run, w); + if (ret) + return -1; + + return 0; +} + +void worker_exit(struct worker *w) +{ + pthread_cancel(w->id); + pthread_join(w->id, NULL); +} diff --git a/src/ipc/worker.h b/src/ipc/worker.h new file mode 100644 index 0000000000000000000000000000000000000000..155e467ecea238c233b748c4b63b8892694dbdcd --- /dev/null +++ b/src/ipc/worker.h @@ -0,0 +1,25 @@ +/* + * worker.h + * Defines worker thread and utility functions. + * + * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: anjan.chanda@iopsys.eu + * + */ + +#ifndef WORKER_H +#define WORKER_H + + +/* worker thread */ +struct worker { + pthread_t id; + void *data; + int (*func)(void *data); +}; + +extern int worker_init(struct worker *w); +extern void worker_exit(struct worker *w); + +#endif /* WORKER_H */ diff --git a/src/plugins/README-plugins.md b/src/plugins/README-plugins.md deleted file mode 100644 index 945513db09edee643b21ec9b551b31e8be78965e..0000000000000000000000000000000000000000 --- a/src/plugins/README-plugins.md +++ /dev/null @@ -1,2 +0,0 @@ -This file will describe how to add a new plugin (extra feature) to the -controller. diff --git a/src/utils/alloctrace.h b/src/utils/alloctrace.h index c41c3046713e15425b32fcb8d9e287d2376f89b6..2480db069df6650affbc8c2ceb332d1a861a5d08 100644 --- a/src/utils/alloctrace.h +++ b/src/utils/alloctrace.h @@ -1,7 +1,7 @@ /* * alloctrace.h - trace heap memory alloc and free * - * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved. + * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved. * * Author: anjan.chanda@iopsys.eu * @@ -30,9 +30,14 @@ static inline void init_alloctrace(const char *progname) { } -static void exit_alloctrace(void) +static inline void exit_alloctrace(void) { } + +#define dbg_malloc +#define dbg_free +#define dbg_calloc + #endif /* TRACE_ALLOC */ #endif /* ALLOCTRACE_H */