Commit 0dcbb477 authored by Kamil Zulewski's avatar Kamil Zulewski
Browse files

Add handling of BSS config. request, response and result CMDUs

parent 25f54338
Pipeline #75305 passed with stages
in 7 minutes and 51 seconds
......@@ -18,7 +18,8 @@ OBJS = \
cntlr_map_debug.o \
cmdu_validate.o \
config.o \
main.o
main.o \
tlv_debug.o
OBJS += steer_module.o
......
......@@ -13,6 +13,7 @@
#include <libubus.h>
#include <uci.h>
#include <easy/easy.h>
#include <json-c/json.h>
#include <easymesh.h>
#include <1905_tlvs.h>
......@@ -497,6 +498,171 @@ bool validate_1905_ack(struct cmdu_buff *cmdu, struct tlv *tv[][16])
return true;
}
#if (EASYMESH_VERSION > 2)
static int check_bss_configuration_report_tlv(struct tlv *t)
{
const struct tlv_bss_configuration_report *tlv;
const uint8_t *const tlv_data = t->data;
const uint16_t tlv_len = tlv_length(t);
int i, offset = 0;
if (!tlv_len)
return -1;
if (!tlv_data)
return -1;
tlv = (struct tlv_bss_configuration_report *)t->data;
/* num_radio (1 byte) */
offset += 1;
if (offset > tlv_len)
return -1;
for (i = 0; i < tlv->num_radio; i++) {
uint8_t num_bss = 0;
int j;
/* RUID/macaddr (6 bytes) */
offset += 6;
if (offset > tlv_len)
return -1;
/* num_bss (1 byte) */
offset += 1;
if (offset > tlv_len)
return -1;
num_bss = tlv_data[offset - sizeof(num_bss)];
for (j = 0; j < num_bss; j++) {
uint8_t ssid_len = 0;
/* BSSID/macaddr (6 bytes) */
offset += 6;
if (offset > tlv_len)
return -1;
/* report flags (1 byte) */
offset += 1;
if (offset > tlv_len)
return -1;
/* reserved flags (1 bytes) */
offset += 1;
if (offset > tlv_len)
return -1;
/* ssidlen (1 byte) */
offset += 1;
if (offset > tlv_len)
return -1;
ssid_len = tlv_data[offset - sizeof(ssid_len)];
/* ssid (ssidlen bytes) */
offset += ssid_len;
if (offset > tlv_len)
return -1;
}
}
return 0;
}
static int check_akm_suite_caps_tlv(struct tlv *t)
{
const uint16_t tlv_len = tlv_length(t);
uint16_t offset;
const struct bbss_akm_suite *bbss;
const struct fbss_akm_suite *fbss;
if (!tlv_len)
return -1;
if (!t->data)
return -1;
bbss = (const struct bbss_akm_suite *)t->data;
offset = sizeof(bbss->num);
if (offset > tlv_len)
return -1;
offset += bbss->num * sizeof(bbss->suite[0]);
if (offset > tlv_len)
return -1;
fbss = (const struct fbss_akm_suite *)(t->data + offset);
offset += sizeof(fbss->num);
if (offset > tlv_len)
return -1;
offset += fbss->num * sizeof(fbss->suite[0]);
if (offset > tlv_len)
return -1;
return 0;
}
static int check_backhaul_sta_radio_caps_tlv(struct tlv *t)
{
const uint16_t tlv_len = tlv_length(t);
uint16_t offset;
const struct tlv_bsta_radio_cap *bsta_radio_cap =
(const struct tlv_bsta_radio_cap *)t->data;
if (!tlv_len)
return -1;
if (!t->data)
return -1;
offset = sizeof(*bsta_radio_cap);
if (offset > tlv_len)
return -1;
if (bsta_radio_cap->macaddr_included & BSTA_MACADDRESS_INCLUDED) {
offset += sizeof(macaddr_t);
if (offset > tlv_len)
return -1;
}
return 0;
}
static int check_bss_config_request_tlv(struct tlv *t)
{
const uint16_t tlv_len = tlv_length(t);
json_tokener *tok;
json_object *jsobj;
int result;
if (!tlv_len)
return -1;
if (!t->data)
return -1;
/* Check whether TLV is valid JSON object */
tok = json_tokener_new();
if (!tok)
return -1;
result = 0;
jsobj = json_tokener_parse_ex(tok, (const char *)t->data, tlv_len);
if (!jsobj || !json_object_is_type(jsobj, json_type_object))
result = -1;
json_tokener_free(tok);
return result;
}
#endif /* EASYMESH_VERSION > 2 */
bool validate_ap_caps_report(struct cmdu_buff *cmdu, struct tlv *tv[][16])
{
int ret = 0;
......@@ -1003,84 +1169,10 @@ bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv[][16])
#if (EASYMESH_VERSION > 2)
/* MAP_TLV_BSS_CONFIGURATION_REPORT */
dbg("Inside %s MAP_TLV_BSS_CONFIGURATION_REPORT\n", __func__);
if (tv[11][0]) {
struct tlv_bss_configuration_report *tlv;
uint8_t *tv_data;
uint16_t tlv_len = 0;
int i, offset = 0;
tlv_len = tlv_length(tv[11][0]);
if (!tlv_len)
return false;
tlv = (struct tlv_bss_configuration_report *)tv[11][0]->data;
if (!tlv)
return false;
tv_data = (uint8_t *)tlv;
/* num_radio (1 byte) */
if (offset + 1 > tlv_len)
return false;
offset += 1;
for (i = 0; i < tlv->num_radio; i++) {
uint8_t num_bss = 0;
int j;
/* macaddr (6 bytes) */
if (offset + 6 > tlv_len)
return false;
offset += 6;
/* num_bss (1 byte) */
if (offset + 1 > tlv_len)
return false;
memcpy(&num_bss, &tv_data[offset], 1);
offset += 1;
for (j = 0; j < num_bss; j++) {
uint8_t ssidlen = 0;
/* macaddr (6 bytes) */
if (offset + 6 > tlv_len)
return false;
offset += 6;
/* report (1 bytes) */
if (offset + 1 > tlv_len)
return false;
offset += 1;
/* reserved (1 bytes) */
if (offset + 1 > tlv_len)
return false;
offset += 1;
/* ssidlen (1 byte) */
if (offset + 1 > tlv_len)
return false;
memcpy(&ssidlen, &tv_data[offset], 1);
offset += 1;
/* ssid (ssidlen bytes) */
if (offset + ssidlen > tlv_len)
return false;
offset += ssidlen;
}
}
}
if (check_bss_configuration_report_tlv(tv[11][0]))
return false;
#endif
return true;
}
......@@ -1290,3 +1382,95 @@ bool validate_ap_autoconfig_response(struct cmdu_buff *cmdu, struct tlv *tv[][16
return true;
}
#if (EASYMESH_VERSION > 2)
bool validate_bss_configuration_request(struct cmdu_buff *cmdu, struct tlv *tlvs[][16])
{
const int easymesh_rev = 4;
const int max_num_of_tlvs = 16;
int i;
if (map_cmdu_parse_tlvs(cmdu, tlvs, BSS_CFG_REQ_MAX_NUMBER_OF_TLV_TYPES, easymesh_rev)) {
dbg("%s: cmdu_parse_tlvs failed, err = (%d) '%s'\n", __func__,
ieee1905_error, ieee1905_strerror(ieee1905_error));
return false;
}
if (check_map_profile_tlv(tlvs[BSS_CFG_REQ_MULTIAP_PROFILE_IDX][0])) {
dbg("%s: check_map_profile_tlv failed.\n", __func__);
return false;
}
if (check_supported_service_tlv(tlvs[BSS_CFG_REQ_SUPPORTED_SERVICE_IDX][0])) {
dbg("%s: check_supported_service_tlv failed.\n", __func__);
return false;
}
if (check_akm_suite_caps_tlv(tlvs[BSS_CFG_REQ_AKM_SUITE_CAPS_IDX][0])) {
dbg("%s: check_akm_suite_caps_tlv failed.\n", __func__);
return false;
}
i = 0;
while ((i < max_num_of_tlvs) && tlvs[BSS_CFG_REQ_AP_RADIO_BASIC_CAPS_IDX][i]) {
if (check_ap_radio_basic_cap_tlv(tlvs[BSS_CFG_REQ_AP_RADIO_BASIC_CAPS_IDX][i])) {
dbg("%s: check_ap_radio_basic_cap_tlv failed.\n", __func__);
return false;
}
++i;
}
i = 0;
while ((i < max_num_of_tlvs) && tlvs[BSS_CFG_REQ_BACKHAUL_STA_RADIO_CAPS_IDX][i]) {
if (check_backhaul_sta_radio_caps_tlv(tlvs[BSS_CFG_REQ_BACKHAUL_STA_RADIO_CAPS_IDX][i])) {
dbg("%s: check_backhaul_sta_radio_caps_tlv failed.\n", __func__);
return false;
}
++i;
}
if (check_profile2_ap_cap_tlv(tlvs[BSS_CFG_REQ_PROFILE2_AP_CAP_IDX][0])) {
dbg("%s: check_profile2_ap_cap_tlv failed.\n", __func__);
return false;
}
i = 0;
while ((i < max_num_of_tlvs) && tlvs[BSS_CFG_REQ_AP_RADIO_ADVANCED_CAPS_IDX][i]) {
if (check_ap_radio_adv_cap_tlv(tlvs[BSS_CFG_REQ_AP_RADIO_ADVANCED_CAPS_IDX][i])) {
dbg("%s: check_ap_radio_adv_cap_tlv failed.\n", __func__);
return false;
}
++i;
}
if (check_bss_config_request_tlv(tlvs[BSS_CFG_REQ_CONFIG_REQUEST_IDX][0])) {
dbg("%s: check_bss_config_request_tlv failed.\n", __func__);
return false;
}
return true;
}
bool validate_bss_configuration_result(struct cmdu_buff *cmdu,
struct tlv *tlvs[][16])
{
const int easymesh_rev = 4;
if (map_cmdu_parse_tlvs(cmdu, tlvs, BSS_CFG_RESULT_MAX_NUMBER_OF_TLV_TYPES, easymesh_rev)) {
dbg("%s: cmdu_parse_tlvs failed, err = (%d) '%s'\n", __func__,
ieee1905_error, ieee1905_strerror(ieee1905_error));
return false;
}
if (check_bss_configuration_report_tlv(tlvs[BSS_CFG_RESULT_BSS_CONFIG_REPORT_IDX][0])) {
dbg("%s: check_bss_configuration_report_tlv failed.\n", __func__);
return false;
}
return true;
}
#endif /* EASYMESH_VERSION > 2 */
......@@ -7,10 +7,48 @@ bool validate_1905_ack(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
bool validate_ap_caps_report(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
bool validate_ap_metrics_response(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
bool validate_channel_scan_report(struct cmdu_buff *cmdu, struct tlv *tv_tsp[][16],
struct tlv *tv_scan[], int *num);
struct tlv *tv_scan[], int *num);
bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv_tsp[][16]);
bool validate_ap_autoconfig_wsc(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
bool validate_ap_autoconfig_search(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
bool validate_ap_autoconfig_response(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
#endif // CMDU_VALIDATE
#if (EASYMESH_VERSION > 2)
/**
* @enum enum bss_configuration_request_tlvs_order
* @brief specifies order of output TLVs and max. number of different TLVs
* for validate_bss_configuration_request function.
*/
enum bss_configuration_request_tlvs_order {
BSS_CFG_REQ_MULTIAP_PROFILE_IDX, // 0
BSS_CFG_REQ_SUPPORTED_SERVICE_IDX,
BSS_CFG_REQ_AKM_SUITE_CAPS_IDX,
BSS_CFG_REQ_AP_RADIO_BASIC_CAPS_IDX,
BSS_CFG_REQ_BACKHAUL_STA_RADIO_CAPS_IDX,
BSS_CFG_REQ_PROFILE2_AP_CAP_IDX,
BSS_CFG_REQ_AP_RADIO_ADVANCED_CAPS_IDX,
BSS_CFG_REQ_CONFIG_REQUEST_IDX,
BSS_CFG_REQ_MAX_NUMBER_OF_TLV_TYPES
};
bool validate_bss_configuration_request(struct cmdu_buff *cmdu,
struct tlv *tlvs[][16]);
/**
* @enum enum bss_configuration_result_tlvs_order
* @brief specifies order of output TLVs and max. number of different TLVs
* for validate_bss_configuration_result function.
*/
enum bss_configuration_result_tlvs_order {
BSS_CFG_RESULT_BSS_CONFIG_REPORT_IDX, // 0
BSS_CFG_RESULT_MAX_NUMBER_OF_TLV_TYPES
};
bool validate_bss_configuration_result(struct cmdu_buff *cmdu,
struct tlv *tlvs[][16]);
#endif /* EASYMESH_VERSION> 2 */
#endif /* CMDU_VALIDATE */
......@@ -1805,6 +1805,10 @@ static int controller_subscribe_for_cmdus(struct controller *c)
CMDU_AP_CAPABILITY_REPORT,
CMDU_CLIENT_CAPABILITY_REPORT,
CMDU_HIGHER_LAYER_DATA,
#if (EASYMESH_VERSION > 2)
CMDU_BSS_CONFIG_REQUEST,
CMDU_BSS_CONFIG_RESULT,
#endif
-1);
memcpy(c->cmdu_mask, cmdu_mask, sizeof(c->cmdu_mask));
......
......@@ -1017,3 +1017,58 @@ error:
cmdu_free(frm);
return NULL;
}
#if (EASYMESH_VERSION > 2)
struct cmdu_buff *cntrl_gen_bss_configuration_response(struct controller *c, struct cmdu_buff *request_cmdu)
{
struct cmdu_buff *resp_cmdu;
struct node *node;
uint16_t mid = 0;
resp_cmdu = cmdu_alloc_simple(CMDU_BSS_CONFIG_RESPONSE, &mid);
if (!resp_cmdu) {
dbg("%s: -ENOMEM\n", __func__);
return NULL;
}
/* One or more BSS Configuration Response TLV */
if (cntlr_gen_bss_config_response_tlv(c, resp_cmdu)) {
dbg("%s: cntlr_gen_bss_config_response_tlv failed.\n", __func__);
goto out;
}
node = find_node_by_mac(c, request_cmdu->origin);
if (!node) {
dbg("|%s:%d| agent:"MACFMT" was not found\n",
__func__, __LINE__, MAC2STR(request_cmdu->origin));
}
if (node && !node->np) {
warn("|%s:%d| agent:"MACFMT" was found "\
"but it does not contain a node policy\n",
__func__, __LINE__, MAC2STR(request_cmdu->origin));
}
if (node && node->np && c->cfg.enable_ts) {
/* Zero or one Default 802.1Q Settings TLV */
if (cntlr_gen_8021q_settings(c, resp_cmdu, node->np)) {
dbg("%s: cntlr_gen_8021q_settings failed.\n", __func__);
goto out;
}
/* Zero or one Traffic Separation Policy TLV */
if (cntlr_gen_traffic_sep_policy(c, resp_cmdu, BAND_5 /* todo: */)) {
dbg("%s: cntlr_gen_traffic_sep_policy failed.\n", __func__);
goto out;
}
}
cmdu_put_eom(resp_cmdu);
memcpy(resp_cmdu->origin, request_cmdu->origin, 6);
return resp_cmdu;
out:
cmdu_free(resp_cmdu);
return NULL;
}
#endif /* EASYMESH_VERSION > 2 */
......@@ -91,4 +91,9 @@ int cntlr_send_client_steer_request(struct controller *c,
uint8_t *agent_mac, uint8_t *bssid,
uint32_t steer_timeout, uint32_t sta_nr, uint8_t stas[][6],
uint32_t bssid_nr, uint8_t target_bssid[][6], uint32_t request_mode);
#if (EASYMESH_VERSION > 2)
struct cmdu_buff *cntrl_gen_bss_configuration_response(struct controller *c, struct cmdu_buff *request_cmdu);
#endif
#endif /* CNTLR_CMDU_GEN_H */
......@@ -491,6 +491,10 @@ static inline uint16_t c_cmdu_expect_response(uint16_t req_type)
return CMDU_BACKHAUL_STA_CAPABILITY_REPORT;
case CMDU_FAILED_CONNECTION:
return CMDU_TYPE_NONE;
#if (EASYMESH_VERSION > 2)
case CMDU_BSS_CONFIG_RESPONSE:
return CMDU_BSS_CONFIG_RESULT;
#endif
default:
break;
}
......@@ -809,7 +813,6 @@ int handle_ap_autoconfig_search(void *cntlr, struct cmdu_buff *rx_cmdu)
} else {
char data[128] = {0};
/* cppcheck-suppress wrongPrintfScanfArgNum */
snprintf(data, sizeof(data), "{\"type\":\"error\", \"reason\":\"multiple controllers\", \"data\": {\"remote_almac\":\""MACFMT"\"}}", MAC2STR(rx_cmdu->origin));
cntlr_notify_event(c, "map.controller", data);
}
......@@ -914,7 +917,6 @@ int handle_ap_autoconfig_response(void *cntlr, struct cmdu_buff *rx_cmdu)
} else {
char data[128] = {0};
/* cppcheck-suppress wrongPrintfScanfArgNum */
snprintf(data, sizeof(data), "{\"type\":\"error\", \"reason\":\"multiple controllers\", \"data\": {\"remote_almac\":\""MACFMT"\"}}", MAC2STR(rx_cmdu->origin));
cntlr_notify_event(c, "map.controller", data);
}
......@@ -2862,6 +2864,79 @@ int handle_backhaul_sta_caps_report(void *cntlr, struct cmdu_buff *cmdu)
return 0;
}
#if (EASYMESH_VERSION > 2)
int handle_bss_configuration_request(void *cntlr, struct cmdu_buff *request_cmdu)
{
trace("%s: --->\n", __func__);
int res;
struct cmdu_buff *response_cmdu;
struct controller *controller = (struct controller *)cntlr;
struct tlv *tlv;
struct tlv *tlvs[BSS_CFG_REQ_MAX_NUMBER_OF_TLV_TYPES][16] = { 0 };
if (!validate_bss_configuration_request(request_cmdu, tlvs)) {
dbg("cmdu validation: [BSS_CONFIGURATION_REQUEST] failed\n");
return -1;
}
// todo: process tlvs and send response
/* One Multi-AP Profile TLV */
tlv = tlvs[BSS_CFG_REQ_MULTIAP_PROFILE_IDX][0];
(void) tlv;
(void) controller;
/* One SupportedService TLV */
/* One AKM Suite Capabilities TLV */
/* One or more AP Radio Basic Capabilities TLV */
/* Zero or more Backhaul STA Radio Capabilities TLV */
/* One Profile-2 AP Capability TLV */
/* One or more AP Radio Advanced Capabilities TLV */
/* One BSS Configuration Request TLV */
response_cmdu = cntrl_gen_bss_configuration_response(controller, request_cmdu);
if (!response_cmdu)
return -1;
res = send_cmdu(controller, response_cmdu);
if (res == 0xffff) {
res = -1;
dbg("%s: agent_send_cmdu failed.\n", __func__);
} else {
res = 0;
dbg("%s: bss configuration response sent.\n", __func__);
}
cmdu_free(response_cmdu);
return res;
}
int handle_bss_configuration_result(void *cntlr, struct cmdu_buff *cmdu)
{
trace("%s: --->\n", __func__);
struct controller *controller = (struct controller *)cntlr;
struct tlv *tlv;
struct tlv *tlvs[BSS_CFG_RESULT_MAX_NUMBER_OF_TLV_TYPES][16] = { 0 };
if (!validate_bss_configuration_result(cmdu, tlvs)) {
dbg("cmdu validation: [BSS_CONFIGURATION_RESULT] failed\n");
return -1;
}
// todo: process tlvs
/* One BSS Configuration Report TLV */
tlv = tlvs[BSS_CFG_RESULT_BSS_CONFIG_REPORT_IDX][0];
(void) tlv;
(void) controller;
return 0;
}
#endif /* EASYMESH_VERSION > 2 */
int handle_failed_connection_msg(void *cntlr, struct cmdu_buff *cmdu)
{
......@@ -2983,6 +3058,16 @@ static const struct map_cmdu_calltable_t cntlr_mapftable[] = {
.handle = handle_backhaul_sta_caps_report,
.debug = debug_backhaul_sta_caps_report
},
#if (EASYMESH_VERSION > 2)
[0x2c] = {
.handle = handle_bss_configuration_request,
.debug = debug_bss_configuration_request