First commit
This commit is contained in:
655
src/ac/ac.c
Normal file
655
src/ac/ac.c
Normal file
@ -0,0 +1,655 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dtls.h"
|
||||
|
||||
#include <libconfig.h>
|
||||
|
||||
#ifndef CAPWAP_MULTITHREADING_ENABLE
|
||||
#error "AC request multithreading\n"
|
||||
#endif
|
||||
|
||||
struct ac_t g_ac;
|
||||
|
||||
#define AC_STANDARD_NAME "Unknown AC"
|
||||
|
||||
/* Local param */
|
||||
static char g_configurationfile[260] = AC_DEFAULT_CONFIGURATION_FILE;
|
||||
|
||||
/* Alloc AC */
|
||||
static int ac_init(void) {
|
||||
/* Network */
|
||||
capwap_network_init(&g_ac.net);
|
||||
g_ac.mtu = CAPWAP_MTU_DEFAULT;
|
||||
g_ac.binding = capwap_array_create(sizeof(unsigned short), 0);
|
||||
|
||||
/* Standard name */
|
||||
strcpy(g_ac.acname.name, AC_STANDARD_NAME);
|
||||
|
||||
/* Descriptor */
|
||||
g_ac.descriptor.stationlimit = AC_DEFAULT_MAXSTATION;
|
||||
g_ac.descriptor.wtplimit = AC_DEFAULT_MAXSESSIONS;
|
||||
g_ac.descriptor.security = 0;
|
||||
g_ac.descriptor.rmacfield = CAPWAP_ACDESC_RMACFIELD_NOTSUPPORTED;
|
||||
g_ac.descriptor.dtlspolicy = CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED;
|
||||
g_ac.descriptor.descsubelement = capwap_array_create(sizeof(struct capwap_acdescriptor_desc_subelement), 0);
|
||||
|
||||
/* */
|
||||
g_ac.dfa.ecn.flag = CAPWAP_LIMITED_ECN_SUPPORT;
|
||||
g_ac.dfa.transport.type = CAPWAP_UDP_TRANSPORT;
|
||||
|
||||
/* */
|
||||
g_ac.dfa.timers.discovery = AC_DEFAULT_DISCOVERY_INTERVAL;
|
||||
g_ac.dfa.timers.echorequest = AC_DEFAULT_ECHO_INTERVAL;
|
||||
g_ac.dfa.decrypterrorreport_interval = AC_DEFAULT_DECRYPT_ERROR_PERIOD_INTERVAL;
|
||||
g_ac.dfa.idletimeout.timeout = AC_DEFAULT_IDLE_TIMEOUT_INTERVAL;
|
||||
g_ac.dfa.wtpfallback.mode = AC_DEFAULT_WTP_FALLBACK_MODE;
|
||||
|
||||
/* */
|
||||
g_ac.dfa.acipv4list = capwap_array_create(sizeof(struct capwap_acipv4list_element), 0);
|
||||
g_ac.dfa.acipv6list = capwap_array_create(sizeof(struct capwap_acipv6list_element), 0);
|
||||
|
||||
/* */
|
||||
g_ac.dfa.rfcWaitJoin = AC_DEFAULT_WAITJOIN_INTERVAL;
|
||||
g_ac.dfa.rfcWaitDTLS = AC_DEFAULT_WAITDTLS_INTERVAL;
|
||||
g_ac.dfa.rfcChangeStatePendingTimer = AC_DEFAULT_CHANGE_STATE_PENDING_TIMER;
|
||||
g_ac.dfa.rfcDataCheckTimer = AC_DEFAULT_DATA_CHECK_TIMER;
|
||||
|
||||
/* Sessions */
|
||||
capwap_event_init(&g_ac.changesessionlist);
|
||||
g_ac.sessions = capwap_list_create();
|
||||
capwap_lock_init(&g_ac.sessionslock);
|
||||
g_ac.datasessionshandshake = capwap_list_create();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Destroy AC */
|
||||
static void ac_destroy(void) {
|
||||
/* Dtls */
|
||||
capwap_crypt_freecontext(&g_ac.dtlscontext);
|
||||
|
||||
/* */
|
||||
capwap_array_free(g_ac.descriptor.descsubelement);
|
||||
capwap_array_free(g_ac.binding);
|
||||
|
||||
/* */
|
||||
capwap_array_free(g_ac.dfa.acipv4list);
|
||||
capwap_array_free(g_ac.dfa.acipv6list);
|
||||
|
||||
/* Sessions */
|
||||
capwap_list_free(g_ac.sessions);
|
||||
capwap_lock_destroy(&g_ac.sessionslock);
|
||||
capwap_event_destroy(&g_ac.changesessionlist);
|
||||
capwap_list_free(g_ac.datasessionshandshake);
|
||||
}
|
||||
|
||||
/* Help */
|
||||
static void ac_print_usage(void) {
|
||||
}
|
||||
|
||||
/* Parsing configuration */
|
||||
static int ac_parsing_configuration_1_0(config_t* config) {
|
||||
int i;
|
||||
int configInt;
|
||||
int configIPv4;
|
||||
int configIPv6;
|
||||
long int configLongInt;
|
||||
const char* configString;
|
||||
config_setting_t* configSetting;
|
||||
|
||||
/* Logging configuration */
|
||||
if (config_lookup_bool(config, "logging.enable", &configInt) == CONFIG_TRUE) {
|
||||
if (!configInt) {
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_NONE);
|
||||
capwap_logging_disable_allinterface();
|
||||
} else {
|
||||
if (config_lookup_string(config, "logging.level", &configString) == CONFIG_TRUE) {
|
||||
if (!strcmp(configString, "fatal")) {
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_FATAL);
|
||||
} else if (!strcmp(configString, "error")) {
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_ERROR);
|
||||
} else if (!strcmp(configString, "warning")) {
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_WARNING);
|
||||
} else if (!strcmp(configString, "info")) {
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_INFO);
|
||||
} else if (!strcmp(configString, "debug")) {
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_DEBUG);
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown logging.level value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Logging output interface */
|
||||
configSetting = config_lookup(config, "logging.output");
|
||||
if (configSetting != NULL) {
|
||||
int count = config_setting_length(configSetting);
|
||||
|
||||
/* Disable output interface */
|
||||
capwap_logging_disable_allinterface();
|
||||
|
||||
/* Enable selected interface */
|
||||
for (i = 0; i < count; i++) {
|
||||
config_setting_t* configElement = config_setting_get_elem(configSetting, i);
|
||||
if ((configElement != NULL) && (config_setting_lookup_string(configElement, "mode", &configString) == CONFIG_TRUE)) {
|
||||
if (!strcmp(configString, "stdout")) {
|
||||
capwap_logging_enable_console(0);
|
||||
} else if (!strcmp(configString, "stderr")) {
|
||||
capwap_logging_enable_console(1);
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown logging.output value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set name of AC */
|
||||
if (config_lookup_string(config, "application.name", &configString) == CONFIG_TRUE) {
|
||||
if (strlen(configString) > CAPWAP_ACNAME_MAXLENGTH) {
|
||||
capwap_logging_error("Invalid configuration file, application.name string length exceeded");
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(g_ac.acname.name, configString);
|
||||
}
|
||||
|
||||
/* Set binding of AC */
|
||||
configSetting = config_lookup(config, "application.binding");
|
||||
if (configSetting != NULL) {
|
||||
int count = config_setting_length(configSetting);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
const char* bindingName = config_setting_get_string_elem(configSetting, i);
|
||||
if (bindingName != NULL) {
|
||||
unsigned short* binding = (unsigned short*)capwap_array_get_item_pointer(g_ac.binding, g_ac.binding->count);
|
||||
|
||||
if (!strcmp(bindingName, "802.11")) {
|
||||
*binding = CAPWAP_WIRELESS_BINDING_IEEE80211;
|
||||
} else if (!strcmp(bindingName, "EPCGlobal")) {
|
||||
*binding = CAPWAP_WIRELESS_BINDING_EPCGLOBAL;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.binding value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set max stations of AC */
|
||||
if (config_lookup_int(config, "application.descriptor.maxstations", &configLongInt) == CONFIG_TRUE) {
|
||||
if ((configLongInt > 0) && (configLongInt < 65536)) {
|
||||
g_ac.descriptor.stationlimit = (unsigned short)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.descriptor.maxstations value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set max wtp of AC */
|
||||
if (config_lookup_int(config, "application.descriptor.maxwtp", &configLongInt) == CONFIG_TRUE) {
|
||||
if ((configLongInt > 0) && (configLongInt < 65536)) {
|
||||
g_ac.descriptor.wtplimit = (unsigned short)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.descriptor.maxwtp value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set security of AC */
|
||||
if (config_lookup(config, "application.descriptor.security") != NULL) {
|
||||
g_ac.descriptor.security = 0;
|
||||
if (config_lookup_bool(config, "application.descriptor.security.presharedkey", &configInt) == CONFIG_TRUE) {
|
||||
if (configInt != 0) {
|
||||
g_ac.descriptor.security |= CAPWAP_ACDESC_SECURITY_PRESHARED_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_bool(config, "application.descriptor.security.x509", &configInt) == CONFIG_TRUE) {
|
||||
if (configInt != 0) {
|
||||
g_ac.descriptor.security |= CAPWAP_ACDESC_SECURITY_X509_CERT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set rmacfiled of AC */
|
||||
if (config_lookup_bool(config, "application.descriptor.rmacfiled.supported", &configInt) == CONFIG_TRUE) {
|
||||
g_ac.descriptor.rmacfield = ((configInt != 0) ? CAPWAP_ACDESC_RMACFIELD_SUPPORTED : CAPWAP_ACDESC_RMACFIELD_NOTSUPPORTED);
|
||||
}
|
||||
|
||||
/* Set DTLS policy of AC */
|
||||
if (config_lookup(config, "application.descriptor.dtlspolicy") != NULL) {
|
||||
g_ac.descriptor.dtlspolicy = 0;
|
||||
if (config_lookup_bool(config, "application.descriptor.dtlspolicy.cleardatachannel", &configInt) == CONFIG_TRUE) {
|
||||
if (configInt != 0) {
|
||||
g_ac.descriptor.dtlspolicy |= CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_bool(config, "application.descriptor.dtlspolicy.dtlsdatachannel", &configInt) == CONFIG_TRUE) {
|
||||
if (configInt != 0) {
|
||||
g_ac.descriptor.dtlspolicy |= CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set info descriptor of AC */
|
||||
configSetting = config_lookup(config, "application.descriptor.info");
|
||||
if (configSetting != NULL) {
|
||||
int count = config_setting_length(configSetting);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
config_setting_t* configElement = config_setting_get_elem(configSetting, i);
|
||||
if (configElement != NULL) {
|
||||
long int configVendor;
|
||||
if (config_setting_lookup_int(configElement, "idvendor", &configVendor) == CONFIG_TRUE) {
|
||||
const char* configType;
|
||||
if (config_setting_lookup_string(configElement, "type", &configType) == CONFIG_TRUE) {
|
||||
const char* configValue;
|
||||
if (config_setting_lookup_string(configElement, "value", &configValue) == CONFIG_TRUE) {
|
||||
int lengthValue = strlen(configValue);
|
||||
if (lengthValue < CAPWAP_ACDESC_SUBELEMENT_MAXDATA) {
|
||||
unsigned short type;
|
||||
struct capwap_acdescriptor_desc_subelement* desc;
|
||||
|
||||
if (!strcmp(configType, "hardware")) {
|
||||
type = CAPWAP_ACDESC_SUBELEMENT_HARDWAREVERSION;
|
||||
} else if (!strcmp(configType, "software")) {
|
||||
type = CAPWAP_ACDESC_SUBELEMENT_SOFTWAREVERSION;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.descriptor.info.type value");
|
||||
return 0;
|
||||
}
|
||||
|
||||
desc = (struct capwap_acdescriptor_desc_subelement*)capwap_array_get_item_pointer(g_ac.descriptor.descsubelement, g_ac.descriptor.descsubelement->count);
|
||||
desc->vendor = (unsigned long)configVendor;
|
||||
desc->type = type;
|
||||
desc->length = lengthValue;
|
||||
strcpy(desc->data, configValue);
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, application.descriptor.info.value string length exceeded");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, element application.descriptor.info.value not found");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, element application.descriptor.info.type not found");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, element application.descriptor.info.idvendor not found");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set ECN of AC */
|
||||
if (config_lookup_string(config, "application.ecn", &configString) == CONFIG_TRUE) {
|
||||
if (!strcmp(configString, "full")) {
|
||||
g_ac.dfa.ecn.flag = CAPWAP_FULL_ECN_SUPPORT;
|
||||
} else if (!strcmp(configString, "limited")) {
|
||||
g_ac.dfa.ecn.flag = CAPWAP_LIMITED_ECN_SUPPORT;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.ecn value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set Timer of AC */
|
||||
if (config_lookup_int(config, "application.timer.discovery", &configLongInt) == CONFIG_TRUE) {
|
||||
if ((configLongInt >= AC_DEFAULT_DISCOVERY_INTERVAL) && (configLongInt <= AC_MAX_DISCOVERY_INTERVAL)) {
|
||||
g_ac.dfa.timers.discovery = (unsigned char)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, invalid application.timer.discovery value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_int(config, "application.timer.echorequest", &configLongInt) == CONFIG_TRUE) {
|
||||
if ((configLongInt > 0) && (configLongInt < AC_MAX_ECHO_INTERVAL)) {
|
||||
g_ac.dfa.timers.echorequest = (unsigned char)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, invalid application.timer.echorequest value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_int(config, "application.timer.decrypterrorreport", &configLongInt) == CONFIG_TRUE) {
|
||||
if ((configLongInt > 0) && (configLongInt < 65536)) {
|
||||
g_ac.dfa.decrypterrorreport_interval = (unsigned short)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, invalid application.timer.decrypterrorreport value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_int(config, "application.timer.idletimeout", &configLongInt) == CONFIG_TRUE) {
|
||||
if (configLongInt > 0) {
|
||||
g_ac.dfa.idletimeout.timeout = (unsigned long)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, invalid application.timer.idletimeout value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set wtpfallback of AC */
|
||||
if (config_lookup_bool(config, "application.wtpfallback", &configInt) == CONFIG_TRUE) {
|
||||
g_ac.dfa.wtpfallback.mode = ((configInt != 0) ? CAPWAP_WTP_FALLBACK_ENABLED : CAPWAP_WTP_FALLBACK_DISABLED);
|
||||
}
|
||||
|
||||
/* Set DTLS of WTP */
|
||||
if (config_lookup_bool(config, "application.dtls.enable", &configInt) == CONFIG_TRUE) {
|
||||
if (configInt != 0) {
|
||||
struct capwap_dtls_param dtlsparam;
|
||||
|
||||
/* Init dtls param */
|
||||
memset(&dtlsparam, 0, sizeof(struct capwap_dtls_param));
|
||||
dtlsparam.type = CAPWAP_DTLS_SERVER;
|
||||
|
||||
/* Set DTLS type of AC */
|
||||
if (config_lookup_string(config, "application.dtls.type", &configString) == CONFIG_TRUE) {
|
||||
if (!strcmp(configString, "x509")) {
|
||||
dtlsparam.mode = CAPWAP_DTLS_MODE_CERTIFICATE;
|
||||
} else if (!strcmp(configString, "presharedkey")) {
|
||||
dtlsparam.mode = CAPWAP_DTLS_MODE_PRESHAREDKEY;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.dtls.type value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set DTLS configuration of AC */
|
||||
if (dtlsparam.mode == CAPWAP_DTLS_MODE_CERTIFICATE) {
|
||||
if (config_lookup_string(config, "application.dtls.x509.calist", &configString) == CONFIG_TRUE) {
|
||||
if (strlen(configString) > 0) {
|
||||
dtlsparam.cert.fileca = capwap_duplicate_string(configString);
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_string(config, "application.dtls.x509.certificate", &configString) == CONFIG_TRUE) {
|
||||
if (strlen(configString) > 0) {
|
||||
dtlsparam.cert.filecert = capwap_duplicate_string(configString);
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_string(config, "application.dtls.x509.privatekey", &configString) == CONFIG_TRUE) {
|
||||
if (strlen(configString) > 0) {
|
||||
dtlsparam.cert.filekey = capwap_duplicate_string(configString);
|
||||
}
|
||||
}
|
||||
|
||||
if (config_lookup_string(config, "application.dtls.x509.privatekeypassword", &configString) == CONFIG_TRUE) {
|
||||
if (strlen(configString) > 0) {
|
||||
dtlsparam.cert.pwdprivatekey = capwap_duplicate_string(configString);
|
||||
}
|
||||
}
|
||||
|
||||
if (dtlsparam.cert.fileca && dtlsparam.cert.filecert && dtlsparam.cert.filekey) {
|
||||
if (capwap_crypt_createcontext(&g_ac.dtlscontext, &dtlsparam)) {
|
||||
g_ac.enabledtls = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free dtls param */
|
||||
if (dtlsparam.cert.fileca) {
|
||||
capwap_free(dtlsparam.cert.fileca);
|
||||
}
|
||||
|
||||
if (dtlsparam.cert.filecert) {
|
||||
capwap_free(dtlsparam.cert.filecert);
|
||||
}
|
||||
|
||||
if (dtlsparam.cert.filekey) {
|
||||
capwap_free(dtlsparam.cert.filekey);
|
||||
}
|
||||
|
||||
if (dtlsparam.cert.pwdprivatekey) {
|
||||
capwap_free(dtlsparam.cert.pwdprivatekey);
|
||||
}
|
||||
|
||||
if (!g_ac.enabledtls) {
|
||||
return 0;
|
||||
}
|
||||
} else if (dtlsparam.mode == CAPWAP_DTLS_MODE_PRESHAREDKEY) {
|
||||
/* TODO */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set interface binding of AC */
|
||||
if (config_lookup_string(config, "application.network.binding", &configString) == CONFIG_TRUE) {
|
||||
if (strlen(configString) > (IFNAMSIZ - 1)) {
|
||||
capwap_logging_error("Invalid configuration file, application.network.binding string length exceeded");
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(g_ac.net.bind_interface, configString);
|
||||
}
|
||||
|
||||
/* Set mtu of AC */
|
||||
if (config_lookup_int(config, "application.network.mtu", &configLongInt) == CONFIG_TRUE) {
|
||||
if ((configLongInt > 0) && (configLongInt < 65536)) {
|
||||
g_ac.mtu = (unsigned short)configLongInt;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, invalid application.network.mtu value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set transport of AC */
|
||||
if (config_lookup_string(config, "application.network.transport", &configString) == CONFIG_TRUE) {
|
||||
if (!strcmp(configString, "udp")) {
|
||||
g_ac.dfa.transport.type = CAPWAP_UDP_TRANSPORT;
|
||||
} else if (!strcmp(configString, "udplite")) {
|
||||
g_ac.dfa.transport.type = CAPWAP_UDPLITE_TRANSPORT;
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unknown application.network.transport value");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set ipv4 & ipv6 of AC */
|
||||
if (config_lookup_bool(config, "application.network.ipv4", &configIPv4) != CONFIG_TRUE) {
|
||||
configIPv4 = 1;
|
||||
}
|
||||
|
||||
if (config_lookup_bool(config, "application.network.ipv6", &configIPv6) != CONFIG_TRUE) {
|
||||
configIPv6 = 1;
|
||||
}
|
||||
|
||||
if (configIPv4 && configIPv6) {
|
||||
g_ac.net.sock_family = AF_UNSPEC;
|
||||
} else if (!configIPv4 && !configIPv6) {
|
||||
capwap_logging_error("Invalid configuration file, request enable application.network.ipv4 or application.network.ipv6");
|
||||
return 0;
|
||||
} else {
|
||||
g_ac.net.sock_family = (configIPv4 ? AF_INET : AF_INET6);
|
||||
}
|
||||
|
||||
/* Set ip dual stack of WTP */
|
||||
if (config_lookup_bool(config, "application.network.ipdualstack", &configInt) == CONFIG_TRUE) {
|
||||
if (!configInt) {
|
||||
g_ac.net.bind_ctrl_flags |= CAPWAP_IPV6ONLY_FLAG;
|
||||
g_ac.net.bind_data_flags |= CAPWAP_IPV6ONLY_FLAG;
|
||||
} else {
|
||||
g_ac.net.bind_ctrl_flags &= ~CAPWAP_IPV6ONLY_FLAG;
|
||||
g_ac.net.bind_data_flags &= ~CAPWAP_IPV6ONLY_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parsing configuration */
|
||||
static int ac_parsing_configuration(config_t* config) {
|
||||
const char* configString;
|
||||
|
||||
if (config_lookup_string(config, "version", &configString) == CONFIG_TRUE) {
|
||||
if (strcmp(configString, "1.0") == 0) {
|
||||
return ac_parsing_configuration_1_0(config);
|
||||
}
|
||||
|
||||
capwap_logging_error("Invalid configuration file, '%s' is not supported", configString);
|
||||
} else {
|
||||
capwap_logging_error("Invalid configuration file, unable to found version tag");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Load configuration */
|
||||
static int ac_load_configuration(int argc, char** argv) {
|
||||
int c;
|
||||
int result = 0;
|
||||
config_t config;
|
||||
|
||||
ASSERT(argc >= 0);
|
||||
ASSERT(argv != NULL);
|
||||
|
||||
/* Parsing command line */
|
||||
opterr = 0;
|
||||
while ((c = getopt(argc, argv, "hc:")) != -1) {
|
||||
switch (c) {
|
||||
case 'h': {
|
||||
ac_print_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
case 'c': {
|
||||
if (strlen(optarg) < sizeof(g_configurationfile)) {
|
||||
strcpy(g_configurationfile, optarg);
|
||||
} else {
|
||||
capwap_logging_error("Invalid -%c argument", optopt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '?': {
|
||||
if (optopt == 'c') {
|
||||
capwap_logging_error("Option -%c requires an argument", optopt);
|
||||
} else {
|
||||
capwap_logging_error("Unknown option character `\\x%x'", optopt);
|
||||
}
|
||||
|
||||
ac_print_usage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Init libconfig */
|
||||
config_init(&config);
|
||||
|
||||
/* Load configuration */
|
||||
if (config_read_file(&config, g_configurationfile) == CONFIG_TRUE) {
|
||||
result = ac_parsing_configuration(&config);
|
||||
} else {
|
||||
result = -1;
|
||||
capwap_logging_error("Unable load the configuration file '%s': %s (%d)", g_configurationfile, config_error_text(&config), config_error_line(&config));
|
||||
}
|
||||
|
||||
/* Free libconfig */
|
||||
config_destroy(&config);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Init AC */
|
||||
static int ac_configure(void) {
|
||||
/* Bind to any address */
|
||||
if (!capwap_bind_sockets(&g_ac.net)) {
|
||||
capwap_logging_fatal("Cannot bind address");
|
||||
return AC_ERROR_NETWORK;
|
||||
}
|
||||
|
||||
return CAPWAP_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/* Close AC */
|
||||
static void ac_close(void) {
|
||||
ASSERT(g_ac.sessions->count == 0);
|
||||
|
||||
/* Close socket */
|
||||
capwap_close_sockets(&g_ac.net);
|
||||
}
|
||||
|
||||
/* Check is valid binding */
|
||||
int ac_valid_binding(unsigned short binding) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g_ac.binding->count; i++) {
|
||||
if (binding == *(unsigned short*)capwap_array_get_item_pointer(g_ac.binding, i)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main*/
|
||||
int main(int argc, char** argv) {
|
||||
int value;
|
||||
int result = CAPWAP_SUCCESSFUL;
|
||||
|
||||
/* Init logging */
|
||||
capwap_logging_init();
|
||||
capwap_logging_verboselevel(CAPWAP_LOGGING_ERROR);
|
||||
capwap_logging_enable_console(1);
|
||||
|
||||
/* Init capwap */
|
||||
if (geteuid() != 0) {
|
||||
capwap_logging_fatal("Request root privileges");
|
||||
return CAPWAP_REQUEST_ROOT;
|
||||
}
|
||||
|
||||
/* Init random generator */
|
||||
capwap_init_rand();
|
||||
|
||||
/* Init crypt */
|
||||
if (!capwap_crypt_init()) {
|
||||
capwap_logging_fatal("Error to init crypt engine");
|
||||
return CAPWAP_CRYPT_ERROR;
|
||||
}
|
||||
|
||||
/* Alloc AC */
|
||||
if (!ac_init()) {
|
||||
return AC_ERROR_SYSTEM_FAILER;
|
||||
}
|
||||
|
||||
/* Read configuration file */
|
||||
value = ac_load_configuration(argc, argv);
|
||||
if (value < 0) {
|
||||
result = AC_ERROR_LOAD_CONFIGURATION;
|
||||
} else if (value > 0) {
|
||||
/* Complete configuration AC */
|
||||
result = ac_configure();
|
||||
if (result == CAPWAP_SUCCESSFUL) {
|
||||
/* Running AC */
|
||||
result = ac_execute();
|
||||
ac_close();
|
||||
}
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
ac_destroy();
|
||||
|
||||
/* Free crypt */
|
||||
capwap_crypt_free();
|
||||
|
||||
/* Check memory leak */
|
||||
if (capwap_check_memory_leak(1)) {
|
||||
if (result == CAPWAP_SUCCESSFUL)
|
||||
result = AC_ERROR_MEMORY_LEAK;
|
||||
}
|
||||
|
||||
/* Close logging */
|
||||
capwap_logging_close();
|
||||
|
||||
return result;
|
||||
}
|
117
src/ac/ac.h
Normal file
117
src/ac/ac.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef __CAPWAP_AC_HEADER__
|
||||
#define __CAPWAP_AC_HEADER__
|
||||
|
||||
/* standard include */
|
||||
#include "capwap.h"
|
||||
#include "capwap_network.h"
|
||||
#include "capwap_protocol.h"
|
||||
#include "capwap_lock.h"
|
||||
#include "capwap_list.h"
|
||||
#include "capwap_event.h"
|
||||
#include "capwap_element.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* AC Configuration */
|
||||
#define AC_DEFAULT_CONFIGURATION_FILE "/etc/capwap/ac.conf"
|
||||
|
||||
#define AC_DEFAULT_MAXSTATION 128
|
||||
#define AC_DEFAULT_MAXSESSIONS 128
|
||||
|
||||
/* AC runtime error return code */
|
||||
#define AC_ERROR_SYSTEM_FAILER -1000
|
||||
#define AC_ERROR_LOAD_CONFIGURATION -1001
|
||||
#define AC_ERROR_NETWORK -1002
|
||||
#define AC_ERROR_MEMORY_LEAK 1
|
||||
|
||||
/* Min and max dfa values */
|
||||
#define AC_MIN_WAITDTLS_INTERVAL 30
|
||||
#define AC_DEFAULT_WAITDTLS_INTERVAL 60
|
||||
#define AC_MIN_WAITJOIN_INTERVAL 20
|
||||
#define AC_DEFAULT_WAITJOIN_INTERVAL 60
|
||||
#define AC_DEFAULT_CHANGE_STATE_PENDING_TIMER 25
|
||||
#define AC_MIN_DISCOVERY_INTERVAL 2
|
||||
#define AC_DEFAULT_DISCOVERY_INTERVAL 20
|
||||
#define AC_MAX_DISCOVERY_INTERVAL 180
|
||||
#define AC_DEFAULT_ECHO_INTERVAL 30
|
||||
#define AC_MAX_ECHO_INTERVAL 256
|
||||
#define AC_DEFAULT_DECRYPT_ERROR_PERIOD_INTERVAL 120
|
||||
#define AC_DEFAULT_IDLE_TIMEOUT_INTERVAL 300
|
||||
#define AC_DEFAULT_WTP_FALLBACK_MODE CAPWAP_WTP_FALLBACK_ENABLED
|
||||
#define AC_DEFAULT_DATA_CHECK_TIMER 30
|
||||
#define AC_DEFAULT_RETRANSMIT_INTERVAL 3
|
||||
#define AC_MAX_RETRANSMIT 5
|
||||
#define AC_DEFAULT_DTLS_SESSION_DELETE 5
|
||||
|
||||
/* AC DFA */
|
||||
struct ac_state {
|
||||
/* */
|
||||
struct capwap_ecnsupport_element ecn;
|
||||
struct capwap_transport_element transport;
|
||||
|
||||
struct capwap_timers_element timers;
|
||||
unsigned short decrypterrorreport_interval;
|
||||
struct capwap_idletimeout_element idletimeout;
|
||||
struct capwap_wtpfallback_element wtpfallback;
|
||||
|
||||
/* */
|
||||
capwap_acipv4list_element_array* acipv4list;
|
||||
capwap_acipv6list_element_array* acipv6list;
|
||||
|
||||
/* */
|
||||
int rfcWaitJoin;
|
||||
int rfcChangeStatePendingTimer;
|
||||
int rfcDataCheckTimer;
|
||||
int rfcDTLSSessionDelete;
|
||||
|
||||
/* Request retransmit */
|
||||
int rfcRetransmitInterval;
|
||||
int rfcRetransmitCount;
|
||||
int rfcMaxRetransmit;
|
||||
|
||||
/* Dtls */
|
||||
int rfcWaitDTLS;
|
||||
};
|
||||
|
||||
/* Handshake DTLS Data Channel */
|
||||
struct ac_data_session_handshake {
|
||||
struct capwap_socket socket;
|
||||
struct sockaddr_storage acaddress;
|
||||
struct sockaddr_storage wtpaddress;
|
||||
struct capwap_dtls dtls;
|
||||
};
|
||||
|
||||
/* AC */
|
||||
struct ac_t {
|
||||
int running;
|
||||
|
||||
/* */
|
||||
struct ac_state dfa;
|
||||
struct capwap_network net;
|
||||
unsigned short mtu;
|
||||
|
||||
struct capwap_array* binding;
|
||||
|
||||
struct capwap_acname_element acname;
|
||||
struct capwap_acdescriptor_element descriptor;
|
||||
|
||||
/* Sessions */
|
||||
capwap_event_t changesessionlist;
|
||||
struct capwap_list* sessions;
|
||||
capwap_lock_t sessionslock;
|
||||
struct capwap_list* datasessionshandshake;
|
||||
|
||||
/* Dtls */
|
||||
int enabledtls;
|
||||
struct capwap_dtls_context dtlscontext;
|
||||
};
|
||||
|
||||
extern struct ac_t g_ac;
|
||||
|
||||
/* Primary thread */
|
||||
int ac_execute(void);
|
||||
|
||||
int ac_valid_binding(unsigned short binding);
|
||||
void ac_update_statistics(void);
|
||||
|
||||
#endif /* __CAPWAP_AC_HEADER__ */
|
128
src/ac/ac_dfa_configure.c
Normal file
128
src/ac/ac_dfa_configure.c
Normal file
@ -0,0 +1,128 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_configure(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
unsigned long i;
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
|
||||
if (buildpacket) {
|
||||
if (!capwap_build_packet_validate(buildpacket, NULL)) {
|
||||
unsigned short binding;
|
||||
|
||||
/* */
|
||||
binding = GET_WBID_HEADER(&buildpacket->header);
|
||||
if (ac_valid_binding(binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_CONFIGURATION_STATUS_REQUEST) && IS_SEQUENCE_SMALLER(session->remoteseqnumber, buildpacket->ctrlmsg.seq)) {
|
||||
struct capwap_element_configurationstatus_request configurationstatusrequest;
|
||||
|
||||
/* Configuration Status request info*/
|
||||
capwap_init_element_configurationstatus_request(&configurationstatusrequest, binding);
|
||||
|
||||
/* Parsing elements list */
|
||||
if (capwap_parsing_element_configurationstatus_request(&configurationstatusrequest, buildpacket->elementslist->first)) {
|
||||
struct capwap_build_packet* responsepacket;
|
||||
|
||||
/* TODO: gestione richiesta */
|
||||
|
||||
/* Create response */
|
||||
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding);
|
||||
responsepacket->isctrlmsg = 1;
|
||||
|
||||
/* Prepare configuration status response */
|
||||
capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_CONFIGURATION_STATUS_RESPONSE, buildpacket->ctrlmsg.seq);
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_TIMERS_ELEMENT(&session->dfa.timers));
|
||||
|
||||
for (i = 0; i < configurationstatusrequest.radioadmstatus->count; i++) {
|
||||
struct capwap_decrypterrorreportperiod_element report;
|
||||
struct capwap_radioadmstate_element* radioadm = (struct capwap_radioadmstate_element*)capwap_array_get_item_pointer(configurationstatusrequest.radioadmstatus, i);
|
||||
|
||||
report.radioid = radioadm->radioid;
|
||||
report.interval = session->dfa.decrypterrorreport_interval;
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_DECRYPTERRORREPORTPERIOD_ELEMENT(&report));
|
||||
}
|
||||
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_IDLETIMEOUT_ELEMENT(&session->dfa.idletimeout));
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_WTPFALLBACK_ELEMENT(&session->dfa.wtpfallback));
|
||||
|
||||
if (session->dfa.acipv4list->count > 0) {
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACIPV4LIST_ELEMENT(session->dfa.acipv4list));
|
||||
}
|
||||
|
||||
if (session->dfa.acipv6list->count > 0) {
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACIPV6LIST_ELEMENT(session->dfa.acipv6list));
|
||||
}
|
||||
|
||||
/* CAPWAP_CREATE_RADIOOPRSTATE_ELEMENT */ /* TODO */
|
||||
/* CAPWAP_CREATE_WTPSTATICIPADDRESS_ELEMENT */ /* TODO */
|
||||
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
|
||||
|
||||
if (!capwap_build_packet_validate(responsepacket, NULL)) {
|
||||
int result;
|
||||
|
||||
/* Free old reference for this request */
|
||||
ac_free_reference_last_response(session);
|
||||
|
||||
/* Send configuration status response to WTP */
|
||||
result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid);
|
||||
if (result >= 0) {
|
||||
if (result == 1) {
|
||||
session->fragmentid++;
|
||||
}
|
||||
|
||||
/* Save remote sequence number */
|
||||
session->remoteseqnumber = buildpacket->ctrlmsg.seq;
|
||||
capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash);
|
||||
|
||||
/* Send */
|
||||
for (i = 0; i < session->responsefragmentpacket->count; i++) {
|
||||
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
/* Response is already created and saved. When receive a re-request, DFA autoresponse */
|
||||
capwap_logging_debug("Warning: error to send configuration status response packet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Change status */
|
||||
ac_dfa_change_state(session, CAPWAP_DATA_CHECK_STATE);
|
||||
capwap_set_timeout(session->dfa.rfcChangeStatePendingTimer, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
}
|
||||
} else {
|
||||
capwap_logging_debug("Warning: build invalid configuration status response packet");
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
capwap_build_packet_free(responsepacket);
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_free_element_configurationstatus_request(&configurationstatusrequest, binding);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
} else {
|
||||
/* Configure timeout */
|
||||
ac_dfa_change_state(session, CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_configure_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
169
src/ac/ac_dfa_datacheck.c
Normal file
169
src/ac/ac_dfa_datacheck.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_datacheck(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
unsigned long i;
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
|
||||
if (buildpacket) {
|
||||
if (!capwap_build_packet_validate(buildpacket, NULL)) {
|
||||
unsigned short binding;
|
||||
|
||||
/* */
|
||||
binding = GET_WBID_HEADER(&buildpacket->header);
|
||||
if (ac_valid_binding(binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_CHANGE_STATE_EVENT_REQUEST) && IS_SEQUENCE_SMALLER(session->remoteseqnumber, buildpacket->ctrlmsg.seq)) {
|
||||
struct capwap_element_changestateevent_request changeeventrequest;
|
||||
|
||||
/* Change event request info */
|
||||
capwap_init_element_changestateevent_request(&changeeventrequest, binding);
|
||||
|
||||
/* Parsing elements list */
|
||||
if (capwap_parsing_element_changestateevent_request(&changeeventrequest, buildpacket->elementslist->first)) {
|
||||
struct capwap_build_packet* responsepacket;
|
||||
|
||||
/* TODO: gestione richiesta */
|
||||
|
||||
/* Create response */
|
||||
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding);
|
||||
responsepacket->isctrlmsg = 1;
|
||||
|
||||
/* Prepare change event response */
|
||||
capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_CHANGE_STATE_EVENT_RESPONSE, buildpacket->ctrlmsg.seq);
|
||||
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
|
||||
|
||||
if (!capwap_build_packet_validate(responsepacket, NULL)) {
|
||||
int result;
|
||||
|
||||
/* Free old reference for this request */
|
||||
ac_free_reference_last_response(session);
|
||||
|
||||
/* Send change event response to WTP */
|
||||
result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid);
|
||||
if (result >= 0) {
|
||||
if (result == 1) {
|
||||
session->fragmentid++;
|
||||
}
|
||||
|
||||
/* Save remote sequence number */
|
||||
session->remoteseqnumber = buildpacket->ctrlmsg.seq;
|
||||
capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash);
|
||||
|
||||
/* Send */
|
||||
for (i = 0; i < session->responsefragmentpacket->count; i++) {
|
||||
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
/* Response is already created and saved. When receive a re-request, DFA autoresponse */
|
||||
capwap_logging_debug("Warning: error to send change event response packet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Change status */
|
||||
ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_RUN_STATE);
|
||||
capwap_set_timeout(session->dfa.rfcDataCheckTimer, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
}
|
||||
} else {
|
||||
capwap_logging_debug("Warning: build invalid configuration status response packet");
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
capwap_build_packet_free(responsepacket);
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_free_element_changestateevent_request(&changeeventrequest, binding);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
} else {
|
||||
/* Configure timeout */
|
||||
ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_datacheck_to_run(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
/* Wait Data Channel Keep-Alive packet */
|
||||
if (!packet->socket.isctrlsocket) {
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
|
||||
if (buildpacket) {
|
||||
struct capwap_sessionid_element sessionid;
|
||||
|
||||
if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) {
|
||||
if (!memcmp(&sessionid, &session->sessionid, sizeof(struct capwap_sessionid_element))) {
|
||||
int result;
|
||||
capwap_fragment_packet_array* txfragpacket;
|
||||
|
||||
/* Receive data packet keepalive, response with same packet */
|
||||
txfragpacket = capwap_array_create(sizeof(struct capwap_packet), 0);
|
||||
result = capwap_fragment_build_packet(buildpacket, txfragpacket, CAPWAP_DONT_FRAGMENT, 0);
|
||||
if (!result) {
|
||||
struct capwap_packet* txpacket;
|
||||
|
||||
ASSERT(txfragpacket->count == 1);
|
||||
|
||||
txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragpacket, 0);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->datadtls, session->datasocket.socket[session->datasocket.type], txpacket->header, txpacket->packetsize, &session->acdataaddress, &session->wtpdataaddress)) {
|
||||
capwap_logging_debug("Warning: error to send data channel keepalive packet");
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
capwap_fragment_free(txfragpacket);
|
||||
capwap_array_free(txfragpacket);
|
||||
|
||||
if (!result) {
|
||||
/* Capwap handshake complete */
|
||||
ac_dfa_change_state(session, CAPWAP_RUN_STATE);
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
} else {
|
||||
ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Configure timeout */
|
||||
ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_datacheck_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
51
src/ac/ac_dfa_dtls.c
Normal file
51
src/ac/ac_dfa_dtls.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* DTLS BIO send */
|
||||
int ac_bio_send(struct capwap_dtls* dtls, char* buffer, int length, void* param) {
|
||||
struct ac_session_t* session = (struct ac_session_t*)param;
|
||||
struct capwap_socket* socket = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &session->ctrlsocket : &session->datasocket);
|
||||
struct sockaddr_storage* wtpaddress = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &session->wtpctrladdress : &session->wtpdataaddress);
|
||||
struct sockaddr_storage* acaddress = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &session->acctrladdress : &session->acdataaddress);
|
||||
|
||||
return capwap_sendto(socket->socket[socket->type], buffer, length, acaddress, wtpaddress);
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_dtlssetup(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(packet == NULL);
|
||||
|
||||
/* Create DTLS session */
|
||||
if (!capwap_crypt_createsession(&session->ctrldtls, CAPWAP_DTLS_CONTROL_SESSION, &g_ac.dtlscontext, ac_bio_send, session)) {
|
||||
ac_dfa_change_state(session, CAPWAP_DTLS_SETUP_TO_IDLE_STATE); /* TODO */
|
||||
status = AC_DFA_NO_PACKET;
|
||||
} else {
|
||||
if (capwap_crypt_open(&session->ctrldtls, &session->wtpctrladdress) == CAPWAP_HANDSHAKE_ERROR) {
|
||||
ac_dfa_change_state(session, CAPWAP_DTLS_SETUP_TO_IDLE_STATE); /* TODO */
|
||||
status = AC_DFA_NO_PACKET;
|
||||
} else {
|
||||
ac_dfa_change_state(session, CAPWAP_DTLS_CONNECT_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_dtlsconnect(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(packet == NULL);
|
||||
|
||||
ac_dfa_change_state(session, CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE); /* TODO */
|
||||
return AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_dtlsconnect_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
18
src/ac/ac_dfa_imagedata.c
Normal file
18
src/ac/ac_dfa_imagedata.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_imagedata(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
/* TODO */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_imagedata_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
272
src/ac/ac_dfa_join.c
Normal file
272
src/ac/ac_dfa_join.c
Normal file
@ -0,0 +1,272 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_join(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int i;
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
struct capwap_resultcode_element resultcode = { CAPWAP_RESULTCODE_FAILURE };
|
||||
struct capwap_build_packet* responsepacket;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
|
||||
if (buildpacket) {
|
||||
int validpacket;
|
||||
unsigned long checkpacket;
|
||||
struct capwap_array* returnedmessagearray = NULL;
|
||||
capwap_unrecognized_element_array* unrecognizedarray;
|
||||
struct capwap_element_join_request joinrequest;
|
||||
unsigned short binding = GET_WBID_HEADER(&buildpacket->header);
|
||||
|
||||
/* */
|
||||
unrecognizedarray = capwap_array_create(sizeof(struct unrecognized_info), 0);
|
||||
|
||||
/* */
|
||||
checkpacket = capwap_build_packet_validate(buildpacket, unrecognizedarray);
|
||||
if (!checkpacket) {
|
||||
if (ac_valid_binding(binding)) {
|
||||
if (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_JOIN_REQUEST) {
|
||||
resultcode.code = CAPWAP_RESULTCODE_SUCCESS;
|
||||
} else {
|
||||
resultcode.code = CAPWAP_RESULTCODE_MSG_UNEXPECTED_INVALID_CURRENT_STATE;
|
||||
}
|
||||
} else {
|
||||
resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_BINDING_NOT_SUPPORTED;
|
||||
}
|
||||
} else {
|
||||
if ((checkpacket & CAPWAP_MISSING_MANDATORY_MSG_ELEMENT) != 0) {
|
||||
resultcode.code = CAPWAP_RESULTCODE_FAILURE_MISSING_MANDATORY_MSG_ELEMENT;
|
||||
} else if ((checkpacket & CAPWAP_UNRECOGNIZED_MSG_ELEMENT) != 0) {
|
||||
struct capwap_list_item* itemelement;
|
||||
|
||||
resultcode.code = CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT;
|
||||
returnedmessagearray = capwap_array_create(sizeof(struct capwap_returnedmessage_element), unrecognizedarray->count);
|
||||
|
||||
for (i = 0; i < unrecognizedarray->count; i++) {
|
||||
struct unrecognized_info* reasoninfo = capwap_array_get_item_pointer(unrecognizedarray, i);
|
||||
|
||||
/* Search element */
|
||||
itemelement = buildpacket->elementslist->first;
|
||||
while (itemelement != NULL) {
|
||||
struct capwap_message_element* elementitem = (struct capwap_message_element*)itemelement->item;
|
||||
|
||||
if (ntohs(elementitem->type) == reasoninfo->element) {
|
||||
struct capwap_returnedmessage_element* returnedelement = capwap_array_get_item_pointer(returnedmessagearray, i);
|
||||
unsigned short length = sizeof(struct capwap_message_element) + ntohs(elementitem->length);
|
||||
|
||||
returnedelement->reason = reasoninfo->reason;
|
||||
returnedelement->length = min(length, CAPWAP_RETURNED_MESSAGE_MAX_LENGTH);
|
||||
memcpy(&returnedelement->message[0], elementitem, returnedelement->length);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Next */
|
||||
itemelement = itemelement->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
capwap_array_free(unrecognizedarray);
|
||||
|
||||
/* */
|
||||
capwap_init_element_join_request(&joinrequest, binding);
|
||||
if (resultcode.code == CAPWAP_RESULTCODE_SUCCESS) {
|
||||
/* Parsing elements list */
|
||||
if (capwap_parsing_element_join_request(&joinrequest, buildpacket->elementslist->first)) {
|
||||
/* TODO: gestione richiesta */
|
||||
|
||||
/* Get sessionid */
|
||||
memcpy(&session->sessionid, joinrequest.sessionid, sizeof(struct capwap_sessionid_element));
|
||||
|
||||
/* Get binding */
|
||||
session->binding = binding;
|
||||
|
||||
resultcode.code = CAPWAP_RESULTCODE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create response */
|
||||
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding);
|
||||
responsepacket->isctrlmsg = 1;
|
||||
|
||||
/* Prepare join response */
|
||||
capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_JOIN_RESPONSE, buildpacket->ctrlmsg.seq);
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode));
|
||||
|
||||
/* Check is valid packet after parsing request */
|
||||
validpacket = (((resultcode.code == CAPWAP_RESULTCODE_SUCCESS) || (resultcode.code == CAPWAP_RESULTCODE_SUCCESS_NAT_DETECTED)) ? 1 : 0);
|
||||
if (validpacket) {
|
||||
struct capwap_list* controllist;
|
||||
struct capwap_list_item* item;
|
||||
|
||||
/* Update statistics */
|
||||
ac_update_statistics();
|
||||
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACDESCRIPTOR_ELEMENT(&g_ac.descriptor));
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACNAME_ELEMENT(&g_ac.acname));
|
||||
|
||||
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
|
||||
for (i = 0; i < joinrequest.binding.ieee80211.wtpradioinformation->count; i++) {
|
||||
struct capwap_80211_wtpradioinformation_element* radio;
|
||||
|
||||
radio = (struct capwap_80211_wtpradioinformation_element*)capwap_array_get_item_pointer(joinrequest.binding.ieee80211.wtpradioinformation, i);
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(radio));
|
||||
}
|
||||
} else {
|
||||
capwap_logging_debug("Unknown capwap binding");
|
||||
}
|
||||
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ECNSUPPORT_ELEMENT(&session->dfa.ecn));
|
||||
|
||||
/* Get information from any local address */
|
||||
controllist = capwap_list_create();
|
||||
ac_get_control_information(controllist);
|
||||
|
||||
for (item = controllist->first; item != NULL; item = item->next) {
|
||||
struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item;
|
||||
|
||||
if (sessioncontrol->localaddress.ss_family == AF_INET) {
|
||||
struct capwap_controlipv4_element element;
|
||||
|
||||
memcpy(&element.address, &((struct sockaddr_in*)&sessioncontrol->localaddress)->sin_addr, sizeof(struct in_addr));
|
||||
element.wtpcount = sessioncontrol->count;
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV4_ELEMENT(&element));
|
||||
} else if (sessioncontrol->localaddress.ss_family == AF_INET6) {
|
||||
struct capwap_controlipv6_element element;
|
||||
|
||||
memcpy(&element.address, &((struct sockaddr_in6*)&sessioncontrol->localaddress)->sin6_addr, sizeof(struct in6_addr));
|
||||
element.wtpcount = sessioncontrol->count;
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV6_ELEMENT(&element));
|
||||
}
|
||||
}
|
||||
|
||||
capwap_list_free(controllist);
|
||||
|
||||
if (session->acctrladdress.ss_family == AF_INET) {
|
||||
struct capwap_localipv4_element addr;
|
||||
|
||||
memcpy(&addr.address, &((struct sockaddr_in*)&session->acctrladdress)->sin_addr, sizeof(struct in_addr));
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_LOCALIPV4_ELEMENT(&addr));
|
||||
} else if (session->acctrladdress.ss_family == AF_INET6) {
|
||||
struct capwap_localipv6_element addr;
|
||||
|
||||
memcpy(&addr.address, &((struct sockaddr_in6*)&session->acctrladdress)->sin6_addr, sizeof(struct in6_addr));
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_LOCALIPV6_ELEMENT(&addr));
|
||||
}
|
||||
|
||||
/* CAPWAP_CREATE_ACIPV4LIST_ELEMENT */ /* TODO */
|
||||
/* CAPWAP_CREATE_ACIPV6LIST_ELEMENT */ /* TODO */
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_TRANSPORT_ELEMENT(&session->dfa.transport));
|
||||
/* CAPWAP_CREATE_IMAGEIDENTIFIER_ELEMENT */ /* TODO */
|
||||
/* CAPWAP_CREATE_MAXIMUMMESSAGELENGTH_ELEMENT */ /* TODO */
|
||||
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
|
||||
} else if (resultcode.code == CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT) {
|
||||
ASSERT(returnedmessagearray != NULL);
|
||||
|
||||
for (i = 0; i < returnedmessagearray->count; i++) {
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RETURNEDMESSAGE_ELEMENT(capwap_array_get_item_pointer(returnedmessagearray, i)));
|
||||
}
|
||||
|
||||
capwap_array_free(returnedmessagearray);
|
||||
}
|
||||
|
||||
/* Validate packet */
|
||||
if (!validpacket || !capwap_build_packet_validate(responsepacket, NULL)) {
|
||||
int result;
|
||||
|
||||
/* Free old reference for this request */
|
||||
ac_free_reference_last_response(session);
|
||||
|
||||
/* Send join response to WTP */
|
||||
result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid);
|
||||
if (result >= 0) {
|
||||
if (result == 1) {
|
||||
session->fragmentid++;
|
||||
}
|
||||
|
||||
/* Save remote sequence number */
|
||||
session->remoteseqnumber = buildpacket->ctrlmsg.seq;
|
||||
capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash);
|
||||
|
||||
/* Send */
|
||||
for (i = 0; i < session->responsefragmentpacket->count; i++) {
|
||||
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
/* Response is already created and saved. When receive a re-request, DFA autoresponse */
|
||||
capwap_logging_debug("Warning: error to send join response packet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
capwap_logging_debug("Warning: build invalid join response packet");
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
capwap_build_packet_free(responsepacket);
|
||||
capwap_free_element_join_request(&joinrequest, binding);
|
||||
capwap_build_packet_free(buildpacket);
|
||||
|
||||
/* Change state */
|
||||
if (validpacket) {
|
||||
ac_dfa_change_state(session, CAPWAP_POSTJOIN_STATE);
|
||||
} else {
|
||||
ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Join timeout */
|
||||
ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_postjoin(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
unsigned short lengthpayload;
|
||||
|
||||
lengthpayload = packet->packetsize - GET_HLEN_HEADER(packet->header) * 4;
|
||||
if (lengthpayload >= sizeof(struct capwap_control_message)) {
|
||||
struct capwap_control_message* ctrlmsg = (struct capwap_control_message*)packet->payload;
|
||||
unsigned long type = ntohl(ctrlmsg->type);
|
||||
|
||||
if (type == CAPWAP_CONFIGURATION_STATUS_REQUEST) {
|
||||
ac_dfa_change_state(session, CAPWAP_CONFIGURE_STATE);
|
||||
status = ac_dfa_state_configure(session, packet);
|
||||
} else if (type == CAPWAP_IMAGE_DATA_REQUEST) {
|
||||
ac_dfa_change_state(session, CAPWAP_IMAGE_DATA_STATE);
|
||||
status = ac_dfa_state_imagedata(session, packet);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Join timeout */
|
||||
ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_join_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
81
src/ac/ac_dfa_reset.c
Normal file
81
src/ac/ac_dfa_reset.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_reset(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
if (!capwap_compare_ip(&session->wtpctrladdress, &packet->remoteaddr)) {
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
/* Parsing packet */
|
||||
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
|
||||
if (buildpacket) {
|
||||
if (!capwap_build_packet_validate(buildpacket, NULL)) {
|
||||
unsigned short binding;
|
||||
|
||||
/* */
|
||||
binding = GET_WBID_HEADER(&buildpacket->header);
|
||||
if ((binding == session->binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_RESET_RESPONSE) && ((session->localseqnumber - 1) == buildpacket->ctrlmsg.seq)) {
|
||||
struct capwap_element_reset_response resetresponse;
|
||||
|
||||
/* Valid packet, free request packet */
|
||||
ac_free_reference_last_request(session);
|
||||
|
||||
/* Configuration status response info */
|
||||
capwap_init_element_reset_response(&resetresponse, binding);
|
||||
|
||||
/* Parsing elements list */
|
||||
if (capwap_parsing_element_reset_response(&resetresponse, buildpacket->elementslist->first)) {
|
||||
ac_dfa_change_state(session, CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
/* Free join response */
|
||||
capwap_free_element_reset_response(&resetresponse, binding);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
|
||||
/* No Configuration status response received */
|
||||
session->dfa.rfcRetransmitCount++;
|
||||
if (session->dfa.rfcRetransmitCount >= session->dfa.rfcMaxRetransmit) {
|
||||
/* Timeout join state */
|
||||
ac_free_reference_last_request(session);
|
||||
ac_dfa_change_state(session, CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
} else {
|
||||
/* Retransmit configuration request */
|
||||
for (i = 0; i < session->requestfragmentpacket->count; i++) {
|
||||
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->requestfragmentpacket, i);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
capwap_logging_debug("Warning: error to send configuration status request packet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update timeout */
|
||||
capwap_set_timeout(session->dfa.rfcRetransmitInterval, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_reset_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
263
src/ac/ac_dfa_run.c
Normal file
263
src/ac/ac_dfa_run.c
Normal file
@ -0,0 +1,263 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
static int receive_echo_request(struct ac_session_t* session, struct capwap_build_packet* buildpacket, struct capwap_packet* packet) {
|
||||
unsigned long i;
|
||||
unsigned short binding;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(buildpacket != NULL);
|
||||
ASSERT(packet != NULL);
|
||||
|
||||
/* */
|
||||
binding = GET_WBID_HEADER(&buildpacket->header);
|
||||
if (ac_valid_binding(binding) && IS_SEQUENCE_SMALLER(session->remoteseqnumber, buildpacket->ctrlmsg.seq)) {
|
||||
struct capwap_element_echo_request echorequest;
|
||||
|
||||
/* Echo request info*/
|
||||
capwap_init_element_echo_request(&echorequest, binding);
|
||||
|
||||
/* Parsing elements list */
|
||||
if (capwap_parsing_element_echo_request(&echorequest, buildpacket->elementslist->first)) {
|
||||
struct capwap_build_packet* responsepacket;
|
||||
|
||||
/* Create response */
|
||||
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding);
|
||||
responsepacket->isctrlmsg = 1;
|
||||
|
||||
/* Prepare echo response */
|
||||
capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_ECHO_RESPONSE, buildpacket->ctrlmsg.seq);
|
||||
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
|
||||
|
||||
if (!capwap_build_packet_validate(responsepacket, NULL)) {
|
||||
int result;
|
||||
|
||||
/* Free old reference for this request */
|
||||
ac_free_reference_last_response(session);
|
||||
|
||||
/* Send echo response to WTP */
|
||||
result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid);
|
||||
if (result >= 0) {
|
||||
if (result == 1) {
|
||||
session->fragmentid++;
|
||||
}
|
||||
|
||||
/* Save remote sequence number */
|
||||
session->remoteseqnumber = buildpacket->ctrlmsg.seq;
|
||||
capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash);
|
||||
|
||||
/* Send */
|
||||
for (i = 0; i < session->responsefragmentpacket->count; i++) {
|
||||
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
/* Response is already created and saved. When receive a re-request, DFA autoresponse */
|
||||
capwap_logging_debug("Warning: error to send echo response packet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
capwap_build_packet_free(responsepacket);
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_free_element_echo_request(&echorequest, binding);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_run(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (packet) {
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket);
|
||||
if (buildpacket) {
|
||||
if (!capwap_build_packet_validate(buildpacket, NULL)) {
|
||||
if (packet->socket.isctrlsocket) {
|
||||
unsigned long typemsg = ntohl(buildpacket->ctrlmsg.type);
|
||||
|
||||
if (capwap_is_request_type(typemsg) || ((session->localseqnumber - 1) == buildpacket->ctrlmsg.seq)) {
|
||||
switch (typemsg) {
|
||||
case CAPWAP_CONFIGURATION_UPDATE_REQUEST: {
|
||||
/* TODO */
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_CHANGE_STATE_EVENT_RESPONSE: {
|
||||
/* TODO */
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_ECHO_REQUEST: {
|
||||
if (!receive_echo_request(session, buildpacket, packet)) {
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
} else {
|
||||
ac_dfa_change_state(session, CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_CLEAR_CONFIGURATION_REQUEST: {
|
||||
/* TODO */
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_WTP_EVENT_RESPONSE: {
|
||||
/* TODO */
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DATA_TRANSFER_REQUEST: {
|
||||
/* TODO */
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DATA_TRANSFER_RESPONSE: {
|
||||
/* TODO */
|
||||
capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IS_FLAG_K_HEADER(&buildpacket->header)) {
|
||||
struct capwap_sessionid_element sessionid;
|
||||
|
||||
if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) {
|
||||
if (!memcmp(&sessionid, &session->sessionid, sizeof(struct capwap_sessionid_element))) {
|
||||
int result;
|
||||
capwap_fragment_packet_array* txfragpacket;
|
||||
|
||||
/* Receive data packet keepalive, response with same packet */
|
||||
txfragpacket = capwap_array_create(sizeof(struct capwap_packet), 0);
|
||||
result = capwap_fragment_build_packet(buildpacket, txfragpacket, CAPWAP_DONT_FRAGMENT, 0);
|
||||
if (!result) {
|
||||
struct capwap_packet* txpacket;
|
||||
|
||||
ASSERT(txfragpacket->count == 1);
|
||||
|
||||
txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragpacket, 0);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->datadtls, session->datasocket.socket[session->datasocket.type], txpacket->header, txpacket->packetsize, &session->acdataaddress, &session->wtpdataaddress)) {
|
||||
capwap_logging_debug("Warning: error to send data channel keepalive packet");
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
capwap_fragment_free(txfragpacket);
|
||||
capwap_array_free(txfragpacket);
|
||||
|
||||
if (result) {
|
||||
ac_dfa_change_state(session, CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* TODO */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
} else {
|
||||
ac_dfa_change_state(session, CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE);
|
||||
status = AC_DFA_NO_PACKET;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_run_to_reset(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int status = AC_DFA_NO_PACKET;
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(packet == NULL);
|
||||
|
||||
/* Build packet */
|
||||
buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, session->binding);
|
||||
buildpacket->isctrlmsg = 1;
|
||||
|
||||
/* Prepare reset request */
|
||||
capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_RESET_REQUEST, session->localseqnumber++);
|
||||
capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_IMAGEIDENTIFIER_ELEMENT(&session->startupimage));
|
||||
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
|
||||
|
||||
if (!capwap_build_packet_validate(buildpacket, NULL)) {
|
||||
int i;
|
||||
int result;
|
||||
|
||||
/* Free old reference for this request */
|
||||
ac_free_reference_last_request(session);
|
||||
|
||||
/* Send reset request to WTP */
|
||||
result = capwap_fragment_build_packet(buildpacket, session->requestfragmentpacket, session->mtu, session->fragmentid);
|
||||
if (result >= 0) {
|
||||
if (result == 1) {
|
||||
session->fragmentid++;
|
||||
}
|
||||
|
||||
/* Send */
|
||||
for (i = 0; i < session->requestfragmentpacket->count; i++) {
|
||||
struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->requestfragmentpacket, i);
|
||||
ASSERT(txpacket != NULL);
|
||||
|
||||
if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
capwap_logging_debug("Warning: error to send reset request packet");
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == -1) {
|
||||
/* Error to send packets */
|
||||
ac_free_reference_last_request(session);
|
||||
ac_dfa_change_state(session, CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE);
|
||||
} else {
|
||||
session->dfa.rfcRetransmitCount = 0;
|
||||
|
||||
capwap_killall_timeout(&session->timeout);
|
||||
capwap_set_timeout(session->dfa.rfcRetransmitInterval, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
|
||||
ac_dfa_change_state(session, CAPWAP_RESET_STATE);
|
||||
status = AC_DFA_ACCEPT_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_run_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
return ac_session_teardown_connection(session);
|
||||
}
|
22
src/ac/ac_dfa_teardown.c
Normal file
22
src/ac/ac_dfa_teardown.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_array.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_teardown(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(packet == NULL);
|
||||
|
||||
/* Defered free resource */
|
||||
ac_dfa_change_state(session, CAPWAP_DEAD_STATE);
|
||||
return AC_DFA_DROP_PACKET;
|
||||
}
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_dead(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(packet == NULL);
|
||||
|
||||
return AC_DFA_DEAD;
|
||||
}
|
276
src/ac/ac_discovery.c
Normal file
276
src/ac/ac_discovery.c
Normal file
@ -0,0 +1,276 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_protocol.h"
|
||||
#include "ac_discovery.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
#define AC_DISCOVERY_CLEANUP_TIMEOUT 1000
|
||||
|
||||
struct ac_discovery_t {
|
||||
pthread_t threadid;
|
||||
int endthread;
|
||||
|
||||
unsigned short fragmentid;
|
||||
unsigned char txseqnumber;
|
||||
|
||||
capwap_event_t waitpacket;
|
||||
capwap_lock_t packetslock;
|
||||
struct capwap_list* packets;
|
||||
};
|
||||
|
||||
struct ac_discovery_packet {
|
||||
int sendsock;
|
||||
struct sockaddr_storage sender;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
static struct ac_discovery_t g_ac_discovery;
|
||||
|
||||
/* */
|
||||
int ac_discovery_start(void) {
|
||||
int result;
|
||||
|
||||
memset(&g_ac_discovery, 0, sizeof(struct ac_discovery_t));
|
||||
|
||||
/* Init */
|
||||
capwap_event_init(&g_ac_discovery.waitpacket);
|
||||
capwap_lock_init(&g_ac_discovery.packetslock);
|
||||
g_ac_discovery.packets = capwap_list_create();
|
||||
|
||||
/* Create thread */
|
||||
result = pthread_create(&g_ac_discovery.threadid, NULL, ac_discovery_thread, NULL);
|
||||
if (result) {
|
||||
capwap_logging_debug("Unable create discovery thread");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* */
|
||||
void ac_discovery_stop(void) {
|
||||
void* dummy;
|
||||
|
||||
g_ac_discovery.endthread = 1;
|
||||
capwap_event_signal(&g_ac_discovery.waitpacket);
|
||||
pthread_join(g_ac_discovery.threadid, &dummy);
|
||||
|
||||
/* Free memory */
|
||||
capwap_event_destroy(&g_ac_discovery.waitpacket);
|
||||
capwap_lock_exit(&g_ac_discovery.packetslock);
|
||||
capwap_list_free(g_ac_discovery.packets);
|
||||
}
|
||||
|
||||
/* */
|
||||
void ac_discovery_add_packet(void* buffer, int buffersize, int sock, struct sockaddr_storage* sender) {
|
||||
struct capwap_list_item* item;
|
||||
struct ac_discovery_packet* packet;
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(buffersize > 0);
|
||||
ASSERT(sock >= 0);
|
||||
ASSERT(sender != NULL);
|
||||
|
||||
/* TODO: mettere un history delle discovery request gi<67> processate per non eseguirle di nuovo */
|
||||
/* L'elemento deve rimanere per la durata minima di una discovery request */
|
||||
|
||||
/* Copy packet */
|
||||
item = capwap_itemlist_create(sizeof(struct ac_discovery_packet) + buffersize);
|
||||
packet = (struct ac_discovery_packet*)item->item;
|
||||
packet->sendsock = sock;
|
||||
memcpy(&packet->sender, sender, sizeof(struct sockaddr_storage));
|
||||
memcpy(packet->data, buffer, buffersize);
|
||||
|
||||
/* Append to packets list */
|
||||
capwap_lock_enter(&g_ac_discovery.packetslock);
|
||||
capwap_itemlist_insert_after(g_ac_discovery.packets, NULL, item);
|
||||
capwap_event_signal(&g_ac_discovery.waitpacket);
|
||||
capwap_lock_exit(&g_ac_discovery.packetslock);
|
||||
}
|
||||
|
||||
/* */
|
||||
static struct capwap_build_packet* ac_create_discovery_response(struct capwap_build_packet* packet, struct capwap_element_discovery_request* discoveryrequest, struct sockaddr_storage* sender) {
|
||||
int i;
|
||||
unsigned short binding;
|
||||
struct capwap_list* controllist;
|
||||
struct capwap_list_item* item;
|
||||
struct capwap_build_packet* responsepacket;
|
||||
|
||||
ASSERT(packet != NULL);
|
||||
ASSERT(discoveryrequest != NULL);
|
||||
ASSERT(sender != NULL);
|
||||
|
||||
/* Check is valid binding */
|
||||
binding = GET_WBID_HEADER(&packet->header);
|
||||
if (!ac_valid_binding(binding)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Build packet */
|
||||
responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding);
|
||||
responsepacket->isctrlmsg = 1;
|
||||
|
||||
/* Update statistics */
|
||||
ac_update_statistics();
|
||||
|
||||
/* Prepare discovery response */
|
||||
capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_DISCOVERY_RESPONSE, packet->ctrlmsg.seq);
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACDESCRIPTOR_ELEMENT(&g_ac.descriptor));
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACNAME_ELEMENT(&g_ac.acname));
|
||||
|
||||
if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
|
||||
for (i = 0; i < discoveryrequest->binding.ieee80211.wtpradioinformation->count; i++) {
|
||||
struct capwap_80211_wtpradioinformation_element* radio;
|
||||
|
||||
radio = (struct capwap_80211_wtpradioinformation_element*)capwap_array_get_item_pointer(discoveryrequest->binding.ieee80211.wtpradioinformation, i);
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(radio));
|
||||
}
|
||||
} else {
|
||||
capwap_logging_debug("Unknown capwap binding");
|
||||
}
|
||||
|
||||
/* Get information from any local address */
|
||||
controllist = capwap_list_create();
|
||||
ac_get_control_information(controllist);
|
||||
|
||||
for (item = controllist->first; item != NULL; item = item->next) {
|
||||
struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item;
|
||||
|
||||
if (sessioncontrol->localaddress.ss_family == AF_INET) {
|
||||
struct capwap_controlipv4_element element;
|
||||
|
||||
memcpy(&element.address, &((struct sockaddr_in*)&sessioncontrol->localaddress)->sin_addr, sizeof(struct in_addr));
|
||||
element.wtpcount = sessioncontrol->count;
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV4_ELEMENT(&element));
|
||||
} else if (sessioncontrol->localaddress.ss_family == AF_INET6) {
|
||||
struct capwap_controlipv6_element element;
|
||||
|
||||
memcpy(&element.address, &((struct sockaddr_in6*)&sessioncontrol->localaddress)->sin6_addr, sizeof(struct in6_addr));
|
||||
element.wtpcount = sessioncontrol->count;
|
||||
capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV6_ELEMENT(&element));
|
||||
}
|
||||
}
|
||||
|
||||
capwap_list_free(controllist);
|
||||
|
||||
/* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */
|
||||
|
||||
return responsepacket;
|
||||
}
|
||||
|
||||
/* Cleanup info discovery */
|
||||
static void ac_discovery_cleanup(void) {
|
||||
/* Clean history discovery request */
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
/* */
|
||||
static void ac_discovery_run(void) {
|
||||
int sizedata;
|
||||
struct capwap_list_item* itempacket;
|
||||
struct capwap_build_packet* buildpacket;
|
||||
struct ac_discovery_packet* packet;
|
||||
unsigned short binding;
|
||||
|
||||
while (!g_ac_discovery.endthread) {
|
||||
/* Get packet */
|
||||
capwap_lock_enter(&g_ac_discovery.packetslock);
|
||||
|
||||
itempacket = NULL;
|
||||
if (g_ac_discovery.packets->count > 0) {
|
||||
itempacket = capwap_itemlist_remove_head(g_ac_discovery.packets);
|
||||
}
|
||||
|
||||
capwap_lock_exit(&g_ac_discovery.packetslock);
|
||||
|
||||
if (!itempacket) {
|
||||
/* Wait packet with timeout*/
|
||||
if (!capwap_event_wait_timeout(&g_ac_discovery.waitpacket, AC_DISCOVERY_CLEANUP_TIMEOUT)) {
|
||||
ac_discovery_cleanup();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* */
|
||||
packet = (struct ac_discovery_packet*)itempacket->item;
|
||||
sizedata = itempacket->itemsize - sizeof(struct ac_discovery_packet);
|
||||
|
||||
/* Parsing packet */
|
||||
buildpacket = capwap_rx_packet_create(packet->data, sizedata, 1);
|
||||
if (buildpacket) {
|
||||
if (!capwap_build_packet_validate(buildpacket, NULL)) {
|
||||
struct capwap_element_discovery_request discoveryrequest;
|
||||
|
||||
/* */
|
||||
binding = GET_WBID_HEADER(&buildpacket->header);
|
||||
capwap_init_element_discovery_request(&discoveryrequest, binding);
|
||||
|
||||
/* Parsing elements list */
|
||||
if (capwap_parsing_element_discovery_request(&discoveryrequest, buildpacket->elementslist->first)) {
|
||||
struct capwap_build_packet* txpacket;
|
||||
capwap_fragment_packet_array* responsefragmentpacket = NULL;
|
||||
|
||||
/* Creare discovery response */
|
||||
txpacket = ac_create_discovery_response(buildpacket, &discoveryrequest, &packet->sender);
|
||||
if (txpacket) {
|
||||
int result = -1;
|
||||
|
||||
if (!capwap_build_packet_validate(txpacket, NULL)) {
|
||||
responsefragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0);
|
||||
result = capwap_fragment_build_packet(txpacket, responsefragmentpacket, g_ac.mtu, g_ac_discovery.fragmentid);
|
||||
if (result == 1) {
|
||||
g_ac_discovery.fragmentid++;
|
||||
}
|
||||
} else {
|
||||
capwap_logging_debug("Warning: build invalid discovery response packet");
|
||||
}
|
||||
|
||||
capwap_build_packet_free(txpacket);
|
||||
|
||||
/* Send discovery response to WTP */
|
||||
if (result >= 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < responsefragmentpacket->count; i++) {
|
||||
struct capwap_packet* sendpacket = (struct capwap_packet*)capwap_array_get_item_pointer(responsefragmentpacket, i);
|
||||
ASSERT(sendpacket != NULL);
|
||||
|
||||
if (!capwap_sendto(packet->sendsock, sendpacket->header, sendpacket->packetsize, NULL, &packet->sender)) {
|
||||
capwap_logging_debug("Warning: error to send discovery response packet");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't buffering a packets sent */
|
||||
if (responsefragmentpacket) {
|
||||
capwap_fragment_free(responsefragmentpacket);
|
||||
capwap_array_free(responsefragmentpacket);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free discovery request */
|
||||
capwap_free_element_discovery_request(&discoveryrequest, binding);
|
||||
}
|
||||
|
||||
/* */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
|
||||
/* Free packet */
|
||||
capwap_itemlist_free(itempacket);
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
void* ac_discovery_thread(void* param) {
|
||||
|
||||
capwap_logging_debug("Discovery start");
|
||||
ac_discovery_run();
|
||||
capwap_logging_debug("Discovery stop");
|
||||
|
||||
/* Thread exit */
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
10
src/ac/ac_discovery.h
Normal file
10
src/ac/ac_discovery.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __AC_DISCOVERY_HEADER__
|
||||
#define __AC_DISCOVERY_HEADER__
|
||||
|
||||
void* ac_discovery_thread(void* param);
|
||||
|
||||
int ac_discovery_start(void);
|
||||
void ac_discovery_stop(void);
|
||||
void ac_discovery_add_packet(void* buffer, int buffersize, int sock, struct sockaddr_storage* sender);
|
||||
|
||||
#endif /* __AC_DISCOVERY_HEADER__ */
|
532
src/ac/ac_execute.c
Normal file
532
src/ac/ac_execute.c
Normal file
@ -0,0 +1,532 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "capwap_event.h"
|
||||
#include "ac_session.h"
|
||||
#include "ac_discovery.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/* Add packet to session */
|
||||
static void ac_session_add_packet(struct ac_session_t* session, char* buffer, int size, int isctrlsocket, int plainbuffer) {
|
||||
struct capwap_list_item* item;
|
||||
struct ac_packet* packet;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(size > 0);
|
||||
|
||||
/* Copy packet */
|
||||
item = capwap_itemlist_create(sizeof(struct ac_packet) + size);
|
||||
packet = (struct ac_packet*)item->item;
|
||||
packet->plainbuffer = plainbuffer;
|
||||
memcpy(packet->buffer, buffer, size);
|
||||
|
||||
/* Append to packets list */
|
||||
capwap_lock_enter(&session->packetslock);
|
||||
capwap_itemlist_insert_after((isctrlsocket ? session->controlpackets : session->datapackets), NULL, item);
|
||||
capwap_event_signal(&session->waitpacket);
|
||||
capwap_lock_exit(&session->packetslock);
|
||||
}
|
||||
|
||||
/* Find AC sessions */
|
||||
static struct ac_session_t* ac_search_session_from_wtpaddress(struct sockaddr_storage* address, int isctrlsocket) {
|
||||
struct ac_session_t* result = NULL;
|
||||
struct capwap_list_item* search;
|
||||
|
||||
ASSERT(address != NULL);
|
||||
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
|
||||
search = g_ac.sessions->first;
|
||||
while (search != NULL) {
|
||||
struct ac_session_t* session = (struct ac_session_t*)search->item;
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (!capwap_compare_ip(address, (isctrlsocket ? &session->wtpctrladdress : &session->wtpdataaddress))) {
|
||||
session->count++;
|
||||
result = session;
|
||||
break;
|
||||
}
|
||||
|
||||
search = search->next;
|
||||
}
|
||||
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* */
|
||||
static struct ac_session_t* ac_get_session_from_keepalive(void* buffer, int buffersize) {
|
||||
struct ac_session_t* result = NULL;
|
||||
struct capwap_build_packet* buildpacket;
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(buffersize > 0);
|
||||
|
||||
buildpacket = capwap_rx_packet_create(buffer, buffersize, 0);
|
||||
if (buildpacket) {
|
||||
struct capwap_sessionid_element sessionid;
|
||||
|
||||
if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) {
|
||||
struct capwap_list_item* search;
|
||||
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
|
||||
search = g_ac.sessions->first;
|
||||
while (search != NULL) {
|
||||
struct ac_session_t* session = (struct ac_session_t*)search->item;
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (!memcmp(&sessionid, &session->sessionid, sizeof(struct capwap_sessionid_element))) {
|
||||
session->count++;
|
||||
result = session;
|
||||
break;
|
||||
}
|
||||
|
||||
search = search->next;
|
||||
}
|
||||
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
}
|
||||
|
||||
/* Free */
|
||||
capwap_build_packet_free(buildpacket);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/* Close session */
|
||||
static void ac_close_session(struct ac_session_t* session) {
|
||||
session->closesession = 1;
|
||||
capwap_event_signal(&session->waitpacket);
|
||||
}
|
||||
|
||||
/* Close sessions */
|
||||
static void ac_close_sessions() {
|
||||
struct capwap_list_item* search;
|
||||
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
|
||||
search = g_ac.sessions->first;
|
||||
while (search != NULL) {
|
||||
struct ac_session_t* session = (struct ac_session_t*)search->item;
|
||||
ASSERT(session != NULL);
|
||||
|
||||
ac_close_session(session);
|
||||
|
||||
search = search->next;
|
||||
}
|
||||
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
}
|
||||
|
||||
/* DTLS Handshake BIO send */
|
||||
static int ac_bio_handshake_send(struct capwap_dtls* dtls, char* buffer, int length, void* param) {
|
||||
struct ac_data_session_handshake* handshake = (struct ac_data_session_handshake*)param;
|
||||
return capwap_sendto(handshake->socket.socket[handshake->socket.type], buffer, length, &handshake->acaddress, &handshake->wtpaddress);
|
||||
}
|
||||
|
||||
/* Find AC sessions */
|
||||
static void ac_update_session_from_datapacket(struct capwap_socket* socket, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, void* buffer, int buffersize) {
|
||||
struct ac_session_t* session = NULL;
|
||||
struct capwap_preamble* preamble = (struct capwap_preamble*)buffer;
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(buffersize > sizeof(struct capwap_preamble));
|
||||
ASSERT(socket != NULL);
|
||||
ASSERT(recvfromaddr != NULL);
|
||||
ASSERT(recvtoaddr != NULL);
|
||||
|
||||
/* */
|
||||
if (preamble->type == CAPWAP_PREAMBLE_HEADER) {
|
||||
if ((g_ac.descriptor.dtlspolicy & CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED) != 0) {
|
||||
session = ac_get_session_from_keepalive(buffer, buffersize);
|
||||
if (session) {
|
||||
/* Update data session */
|
||||
memcpy(&session->datasocket, socket, sizeof(struct capwap_socket));
|
||||
memcpy(&session->acdataaddress, recvtoaddr, sizeof(struct sockaddr_storage));
|
||||
memcpy(&session->wtpdataaddress, recvfromaddr, sizeof(struct sockaddr_storage));
|
||||
|
||||
/* Add packet*/
|
||||
ac_session_add_packet(session, buffer, buffersize, 0, 1);
|
||||
ac_session_release_reference(session);
|
||||
}
|
||||
}
|
||||
} else if (preamble->type == CAPWAP_PREAMBLE_DTLS_HEADER) {
|
||||
if ((g_ac.descriptor.dtlspolicy & CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED) != 0) {
|
||||
struct capwap_list_item* itemlist;
|
||||
struct ac_data_session_handshake* handshake;
|
||||
|
||||
/* Search active data dtls handshake */
|
||||
itemlist = g_ac.datasessionshandshake->first;
|
||||
while (itemlist != NULL) {
|
||||
handshake = (struct ac_data_session_handshake*)itemlist->item;
|
||||
|
||||
if (!capwap_compare_ip(recvfromaddr, &handshake->wtpaddress) && !capwap_compare_ip(recvtoaddr, &handshake->acaddress)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Next */
|
||||
itemlist = itemlist->next;
|
||||
}
|
||||
|
||||
/* Create new DTLS handshake */
|
||||
if (!itemlist) {
|
||||
itemlist = capwap_itemlist_create(sizeof(struct ac_data_session_handshake));
|
||||
handshake = (struct ac_data_session_handshake*)itemlist->item;
|
||||
memset(handshake, 0, sizeof(struct ac_data_session_handshake));
|
||||
|
||||
/* */
|
||||
memcpy(&handshake->socket, socket, sizeof(struct capwap_socket));
|
||||
memcpy(&handshake->acaddress, recvtoaddr, sizeof(struct sockaddr_storage));
|
||||
memcpy(&handshake->wtpaddress, recvfromaddr, sizeof(struct sockaddr_storage));
|
||||
|
||||
/* Create DTLS session */
|
||||
if (!capwap_crypt_createsession(&handshake->dtls, CAPWAP_DTLS_DATA_SESSION, &g_ac.dtlscontext, ac_bio_handshake_send, handshake)) {
|
||||
capwap_itemlist_free(itemlist);
|
||||
itemlist = NULL;
|
||||
} else {
|
||||
if (capwap_crypt_open(&handshake->dtls, recvfromaddr) == CAPWAP_HANDSHAKE_ERROR) {
|
||||
capwap_crypt_freesession(&handshake->dtls);
|
||||
capwap_itemlist_free(itemlist);
|
||||
itemlist = NULL;
|
||||
} else {
|
||||
/* Add item to list */
|
||||
capwap_itemlist_insert_after(g_ac.datasessionshandshake, NULL, itemlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrypt packet */
|
||||
if (itemlist) {
|
||||
char temp[CAPWAP_MAX_PACKET_SIZE];
|
||||
|
||||
/* */
|
||||
handshake = (struct ac_data_session_handshake*)itemlist->item;
|
||||
buffersize = capwap_decrypt_packet(&handshake->dtls, buffer, buffersize, temp, CAPWAP_MAX_PACKET_SIZE);
|
||||
if (buffersize > 0) {
|
||||
session = ac_get_session_from_keepalive(temp, buffersize);
|
||||
if (!session) {
|
||||
capwap_itemlist_remove(g_ac.datasessionshandshake, itemlist);
|
||||
capwap_crypt_close(&handshake->dtls);
|
||||
capwap_crypt_freesession(&handshake->dtls);
|
||||
capwap_itemlist_free(itemlist);
|
||||
} else {
|
||||
/* Update DTLS session */
|
||||
capwap_crypt_change_dtls(&handshake->dtls, &session->datadtls);
|
||||
memcpy(&session->datasocket, &handshake->socket, sizeof(struct capwap_socket));
|
||||
memcpy(&session->acdataaddress, &handshake->acaddress, sizeof(struct sockaddr_storage));
|
||||
memcpy(&session->wtpdataaddress, &handshake->wtpaddress, sizeof(struct sockaddr_storage));
|
||||
capwap_crypt_change_bio_send(&session->datadtls, ac_bio_send, session);
|
||||
|
||||
/* Remove temp element */
|
||||
capwap_itemlist_remove(g_ac.datasessionshandshake, itemlist);
|
||||
capwap_itemlist_free(itemlist);
|
||||
|
||||
/* Add packet*/
|
||||
ac_session_add_packet(session, temp, buffersize, 0, 1); /* Packet already decrypt */
|
||||
ac_session_release_reference(session);
|
||||
}
|
||||
} else if ((buffersize == CAPWAP_ERROR_SHUTDOWN) || (buffersize == CAPWAP_ERROR_CLOSE)) {
|
||||
capwap_itemlist_remove(g_ac.datasessionshandshake, itemlist);
|
||||
capwap_crypt_close(&handshake->dtls);
|
||||
capwap_crypt_freesession(&handshake->dtls);
|
||||
capwap_itemlist_free(itemlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create new session */
|
||||
static struct ac_session_t* ac_create_session(struct sockaddr_storage* wtpaddress, struct sockaddr_storage* acaddress, struct capwap_socket* ctrlsock) {
|
||||
int result;
|
||||
struct capwap_list_item* itemlist;
|
||||
struct ac_session_t* session;
|
||||
|
||||
ASSERT(acaddress != NULL);
|
||||
ASSERT(wtpaddress != NULL);
|
||||
ASSERT(ctrlsock != NULL);
|
||||
|
||||
/* Create new session */
|
||||
itemlist = capwap_itemlist_create(sizeof(struct ac_session_t));
|
||||
session = (struct ac_session_t*)itemlist->item;
|
||||
memset(session, 0, sizeof(struct ac_session_t));
|
||||
|
||||
session->count = 2;
|
||||
memcpy(&session->acctrladdress, acaddress, sizeof(struct sockaddr_storage));
|
||||
memcpy(&session->wtpctrladdress, wtpaddress, sizeof(struct sockaddr_storage));
|
||||
memcpy(&session->ctrlsocket, ctrlsock, sizeof(struct capwap_socket));
|
||||
|
||||
/* Duplicate state for DFA */
|
||||
memcpy(&session->dfa, &g_ac.dfa, sizeof(struct ac_state));
|
||||
session->dfa.acipv4list = capwap_array_clone(g_ac.dfa.acipv4list);
|
||||
session->dfa.acipv6list = capwap_array_clone(g_ac.dfa.acipv6list);
|
||||
|
||||
session->dfa.rfcRetransmitInterval = AC_DEFAULT_RETRANSMIT_INTERVAL;
|
||||
session->dfa.rfcMaxRetransmit = AC_MAX_RETRANSMIT;
|
||||
session->dfa.rfcDTLSSessionDelete = AC_DEFAULT_DTLS_SESSION_DELETE;
|
||||
|
||||
/* Add default AC list if empty*/
|
||||
if ((session->dfa.acipv4list->count == 0) && (session->dfa.acipv6list->count == 0)) {
|
||||
if (session->acctrladdress.ss_family == AF_INET) {
|
||||
struct capwap_acipv4list_element* acip = (struct capwap_acipv4list_element*)capwap_array_get_item_pointer(session->dfa.acipv4list, 0);
|
||||
memcpy(&acip->address, &((struct sockaddr_in*)&session->acctrladdress)->sin_addr, sizeof(struct in_addr));
|
||||
} else if (session->acctrladdress.ss_family == AF_INET6) {
|
||||
struct capwap_acipv6list_element* acip = (struct capwap_acipv6list_element*)capwap_array_get_item_pointer(session->dfa.acipv6list, 0);
|
||||
memcpy(&acip->address, &((struct sockaddr_in6*)&session->acctrladdress)->sin6_addr, sizeof(struct in6_addr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Init */
|
||||
capwap_event_init(&session->waitpacket);
|
||||
capwap_lock_init(&session->packetslock);
|
||||
|
||||
session->controlpackets = capwap_list_create();
|
||||
session->datapackets = capwap_list_create();
|
||||
session->requestfragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0);
|
||||
session->responsefragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0);
|
||||
session->rxfragmentpacket = capwap_defragment_init_list();
|
||||
|
||||
session->mtu = g_ac.mtu;
|
||||
session->state = CAPWAP_IDLE_STATE;
|
||||
|
||||
/* Update session list */
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
capwap_itemlist_insert_after(g_ac.sessions, NULL, itemlist);
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
|
||||
/* Create thread */
|
||||
result = pthread_create(&session->threadid, NULL, ac_session_thread, (void*)session);
|
||||
if (!result) {
|
||||
pthread_detach(session->threadid);
|
||||
|
||||
/* Notify change session list */
|
||||
capwap_event_signal(&g_ac.changesessionlist);
|
||||
} else {
|
||||
capwap_logging_debug("Unable create session thread, error code %d", result);
|
||||
|
||||
/* Destroy element */
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
capwap_itemlist_free(capwap_itemlist_remove(g_ac.sessions, itemlist));
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
|
||||
session = NULL;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
void ac_update_statistics(void) {
|
||||
|
||||
g_ac.descriptor.station = 0; /* TODO */
|
||||
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
g_ac.descriptor.wtp = g_ac.sessions->count;
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
}
|
||||
|
||||
/* Handler signal */
|
||||
static void ac_signal_handler(int signum) {
|
||||
if ((signum == SIGINT) || (signum == SIGTERM)) {
|
||||
g_ac.running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* AC running */
|
||||
int ac_execute(void) {
|
||||
int fdscount = CAPWAP_MAX_SOCKETS * 2;
|
||||
struct pollfd* fds;
|
||||
int result = CAPWAP_SUCCESSFUL;
|
||||
|
||||
int index;
|
||||
int check;
|
||||
int isctrlsocket = 0;
|
||||
struct sockaddr_storage recvfromaddr;
|
||||
struct sockaddr_storage recvtoaddr;
|
||||
int isrecvpacket = 0;
|
||||
|
||||
struct ac_session_t* session;
|
||||
struct capwap_socket ctrlsock;
|
||||
|
||||
char buffer[CAPWAP_MAX_PACKET_SIZE];
|
||||
int buffersize;
|
||||
|
||||
/* Configure poll struct */
|
||||
fds = (struct pollfd*)capwap_alloc(sizeof(struct pollfd) * fdscount);
|
||||
if (!fds) {
|
||||
capwap_outofmemory();
|
||||
}
|
||||
|
||||
/* Retrive all socket for polling */
|
||||
fdscount = capwap_network_set_pollfd(&g_ac.net, fds, fdscount);
|
||||
ASSERT(fdscount > 0);
|
||||
|
||||
/* Handler signal */
|
||||
g_ac.running = 1;
|
||||
signal(SIGINT, ac_signal_handler);
|
||||
signal(SIGTERM, ac_signal_handler);
|
||||
|
||||
/* Start discovery thread */
|
||||
if (!ac_discovery_start()) {
|
||||
capwap_free(fds);
|
||||
capwap_logging_debug("Unable to start discovery thread");
|
||||
return AC_ERROR_SYSTEM_FAILER;
|
||||
}
|
||||
|
||||
/* */
|
||||
for (;;) {
|
||||
/* Receive packet */
|
||||
isrecvpacket = 0;
|
||||
buffersize = sizeof(buffer);
|
||||
index = capwap_recvfrom(fds, fdscount, buffer, &buffersize, &recvfromaddr, &recvtoaddr, NULL);
|
||||
if (!g_ac.running) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* */
|
||||
if (index >= 0) {
|
||||
/* Detect local address */
|
||||
if (recvtoaddr.ss_family == AF_UNSPEC) {
|
||||
if (capwap_get_localaddress_by_remoteaddress(&recvtoaddr, &recvfromaddr, g_ac.net.bind_interface, (!(g_ac.net.bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG) ? 1 : 0))) {
|
||||
struct sockaddr_storage sockinfo;
|
||||
socklen_t sockinfolen = sizeof(struct sockaddr_storage);
|
||||
|
||||
memset(&sockinfo, 0, sizeof(struct sockaddr_storage));
|
||||
if (getsockname(fds[index].fd, (struct sockaddr*)&sockinfo, &sockinfolen) < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
CAPWAP_SET_NETWORK_PORT(&recvtoaddr, CAPWAP_GET_NETWORK_PORT(&sockinfo));
|
||||
}
|
||||
}
|
||||
|
||||
/* Search the AC session */
|
||||
isctrlsocket = ((index < (fdscount / 2)) ? 1 : 0);
|
||||
session = ac_search_session_from_wtpaddress(&recvfromaddr, isctrlsocket);
|
||||
|
||||
if (session) {
|
||||
/* Add packet*/
|
||||
ac_session_add_packet(session, buffer, buffersize, isctrlsocket, 0);
|
||||
|
||||
/* Release reference */
|
||||
ac_session_release_reference(session);
|
||||
} else {
|
||||
if (isctrlsocket) {
|
||||
unsigned short sessioncount;
|
||||
|
||||
/* Get current session number */
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
sessioncount = g_ac.sessions->count;
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
|
||||
/* PreParsing packet for reduce a DoS attack */
|
||||
check = capwap_sanity_check(isctrlsocket, CAPWAP_UNDEF_STATE, buffer, buffersize, g_ac.enabledtls, 0);
|
||||
if (check == CAPWAP_PLAIN_PACKET) {
|
||||
struct capwap_header* header = (struct capwap_header*)buffer;
|
||||
|
||||
/* Accepted only packet without fragmentation */
|
||||
if (!IS_FLAG_F_HEADER(header)) {
|
||||
int headersize = GET_HLEN_HEADER(header) * 4;
|
||||
if (buffersize >= (headersize + sizeof(struct capwap_control_message))) {
|
||||
struct capwap_control_message* control = (struct capwap_control_message*)((char*)buffer + headersize);
|
||||
unsigned long type = ntohl(control->type);
|
||||
|
||||
if (type == CAPWAP_DISCOVERY_REQUEST) {
|
||||
if (sessioncount < g_ac.descriptor.wtplimit) {
|
||||
ac_discovery_add_packet(buffer, buffersize, fds[index].fd, &recvfromaddr);
|
||||
}
|
||||
} else if (!g_ac.enabledtls && (type == CAPWAP_JOIN_REQUEST)) {
|
||||
if (sessioncount < g_ac.descriptor.wtplimit) {
|
||||
/* Retrive socket info */
|
||||
capwap_get_network_socket(&g_ac.net, &ctrlsock, fds[index].fd);
|
||||
|
||||
/* Create a new session */
|
||||
session = ac_create_session(&recvfromaddr, &recvtoaddr, &ctrlsock);
|
||||
if (session) {
|
||||
ac_session_add_packet(session, buffer, buffersize, isctrlsocket, 1);
|
||||
|
||||
/* Release reference */
|
||||
ac_session_release_reference(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (check == CAPWAP_DTLS_PACKET) {
|
||||
/* Need create a new sessione for check if it is a valid DTLS handshake */
|
||||
if (sessioncount < g_ac.descriptor.wtplimit) {
|
||||
/* TODO prevent dos attack add filtering ip for multiple error */
|
||||
|
||||
/* Retrive socket info */
|
||||
capwap_get_network_socket(&g_ac.net, &ctrlsock, fds[index].fd);
|
||||
|
||||
/* Create a new session */
|
||||
session = ac_create_session(&recvfromaddr, &recvtoaddr, &ctrlsock);
|
||||
if (session) {
|
||||
ac_session_add_packet(session, buffer, buffersize, isctrlsocket, 0);
|
||||
|
||||
/* Release reference */
|
||||
ac_session_release_reference(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
struct capwap_socket datasocket;
|
||||
|
||||
/* Retrieve session by sessionid of data packet */
|
||||
capwap_get_network_socket(&g_ac.net, &datasocket, fds[index].fd);
|
||||
ac_update_session_from_datapacket(&datasocket, &recvfromaddr, &recvtoaddr, buffer, buffersize);
|
||||
}
|
||||
}
|
||||
} else if (index == CAPWAP_RECV_ERROR_INTR) {
|
||||
/* Ignore recv */
|
||||
continue;
|
||||
} else if (index == CAPWAP_RECV_ERROR_SOCKET) {
|
||||
/* Socket close */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Terminate discovery thread */
|
||||
ac_discovery_stop();
|
||||
|
||||
/* Close all sessions */
|
||||
ac_close_sessions();
|
||||
|
||||
/* Wait to terminate all sessions */
|
||||
for (;;) {
|
||||
int count;
|
||||
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
count = g_ac.sessions->count;
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
|
||||
if (!count) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait that list is changed */
|
||||
capwap_logging_debug("Waiting for %d session terminate", count);
|
||||
capwap_event_wait(&g_ac.changesessionlist);
|
||||
}
|
||||
|
||||
/* Free handshark session */
|
||||
while (g_ac.datasessionshandshake->first != NULL) {
|
||||
struct ac_data_session_handshake* handshake = (struct ac_data_session_handshake*)g_ac.datasessionshandshake->first->item;
|
||||
|
||||
if (handshake->dtls.enable) {
|
||||
capwap_crypt_freesession(&handshake->dtls);
|
||||
}
|
||||
|
||||
capwap_itemlist_free(capwap_itemlist_remove(g_ac.datasessionshandshake, g_ac.datasessionshandshake->first));
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
capwap_free(fds);
|
||||
return result;
|
||||
}
|
617
src/ac/ac_session.c
Normal file
617
src/ac/ac_session.c
Normal file
@ -0,0 +1,617 @@
|
||||
#include "ac.h"
|
||||
#include "capwap_dfa.h"
|
||||
#include "ac_session.h"
|
||||
|
||||
#define PACKET_TIMEOUT -1
|
||||
#define DTLS_SHUTDOWN -2
|
||||
|
||||
static int ac_network_read(struct ac_session_t* session, void* buffer, int length, int* isctrlpacket, struct timeout_control* timeout) {
|
||||
long indextimer;
|
||||
long waittimeout;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(length > 0);
|
||||
ASSERT(isctrlpacket != NULL);
|
||||
|
||||
for (;;) {
|
||||
if (session->closesession) {
|
||||
session->closesession = 0;
|
||||
return DTLS_SHUTDOWN;
|
||||
}
|
||||
|
||||
capwap_lock_enter(&session->packetslock);
|
||||
|
||||
if ((session->controlpackets->count > 0) || (session->datapackets->count > 0)) {
|
||||
int result = 0;
|
||||
struct capwap_list_item* itempacket;
|
||||
|
||||
*isctrlpacket = ((session->controlpackets->count > 0) ? 1 : 0);
|
||||
|
||||
/* Get packet */
|
||||
itempacket = capwap_itemlist_remove_head((*isctrlpacket ? session->controlpackets : session->datapackets));
|
||||
capwap_lock_exit(&session->packetslock);
|
||||
|
||||
if (itempacket) {
|
||||
struct ac_packet* packet = (struct ac_packet*)itempacket->item;
|
||||
long packetlength = itempacket->itemsize - sizeof(struct ac_packet);
|
||||
struct capwap_dtls* dtls = (*isctrlpacket ? &session->ctrldtls : &session->datadtls);
|
||||
|
||||
if (!packet->plainbuffer && dtls->enable) {
|
||||
int oldaction = dtls->action;
|
||||
|
||||
/* Decrypt packet */
|
||||
result = capwap_decrypt_packet(dtls, packet->buffer, packetlength, buffer, length);
|
||||
if (result == CAPWAP_ERROR_AGAIN) {
|
||||
/* Check is handshake complete */
|
||||
if ((oldaction == CAPWAP_DTLS_ACTION_HANDSHAKE) && (dtls->action == CAPWAP_DTLS_ACTION_DATA)) {
|
||||
if (*isctrlpacket) {
|
||||
if (session->state == CAPWAP_DTLS_CONNECT_STATE) {
|
||||
ac_dfa_change_state(session, CAPWAP_JOIN_STATE);
|
||||
capwap_set_timeout(session->dfa.rfcWaitJoin, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (result == CAPWAP_ERROR_SHUTDOWN) {
|
||||
if ((oldaction == CAPWAP_DTLS_ACTION_DATA) && (dtls->action == CAPWAP_DTLS_ACTION_SHUTDOWN)) {
|
||||
result = DTLS_SHUTDOWN;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (packetlength <= length) {
|
||||
memcpy(buffer, packet->buffer, packetlength);
|
||||
result = packetlength;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free packet */
|
||||
capwap_itemlist_free(itempacket);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
capwap_lock_exit(&session->packetslock);
|
||||
|
||||
/* Update timeout */
|
||||
capwap_update_timeout(timeout);
|
||||
waittimeout = capwap_get_timeout(timeout, &indextimer);
|
||||
if ((waittimeout <= 0) && (indextimer != CAPWAP_TIMER_UNDEF)) {
|
||||
return PACKET_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Wait packet */
|
||||
capwap_event_wait_timeout(&session->waitpacket, waittimeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* */
|
||||
static int ac_dfa_execute(struct ac_session_t* session, struct capwap_packet* packet) {
|
||||
int action = AC_DFA_ACCEPT_PACKET;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
/* Execute state */
|
||||
switch (session->state) {
|
||||
case CAPWAP_START_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_START_TO_IDLE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_IDLE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_IDLE_TO_DISCOVERY_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_IDLE_TO_DTLS_SETUP_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DISCOVERY_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DISCOVERY_TO_IDLE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DISCOVERY_TO_SULKING_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DISCOVERY_TO_DTLS_SETUP_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_SULKING_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_SULKING_TO_IDLE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_SETUP_STATE: {
|
||||
action = ac_dfa_state_dtlssetup(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_SETUP_TO_IDLE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_SETUP_TO_SULKING_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_SETUP_TO_AUTHORIZE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_AUTHORIZE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_AUTHORIZE_TO_DTLS_SETUP_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_AUTHORIZE_TO_DTLS_CONNECT_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_AUTHORIZE_TO_DTLS_TEARDOWN_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_CONNECT_STATE: {
|
||||
action = ac_dfa_state_dtlsconnect(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_dtlsconnect_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_CONNECT_TO_JOIN_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_teardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_TEARDOWN_TO_IDLE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_TEARDOWN_TO_SULKING_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DTLS_TEARDOWN_TO_DEAD_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_JOIN_STATE: {
|
||||
action = ac_dfa_state_join(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_POSTJOIN_STATE: {
|
||||
action = ac_dfa_state_postjoin(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_join_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_JOIN_TO_IMAGE_DATA_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_JOIN_TO_CONFIGURE_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_IMAGE_DATA_STATE: {
|
||||
action = ac_dfa_state_imagedata(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_IMAGE_DATA_TO_RESET_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_IMAGE_DATA_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_imagedata_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_CONFIGURE_STATE: {
|
||||
action = ac_dfa_state_configure(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_CONFIGURE_TO_RESET_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_configure_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_CONFIGURE_TO_DATA_CHECK_STATE: {
|
||||
/* Never called with this state */
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_RESET_STATE: {
|
||||
action = ac_dfa_state_reset(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_reset_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DATA_CHECK_STATE: {
|
||||
action = ac_dfa_state_datacheck(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_datacheck_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DATA_CHECK_TO_RUN_STATE: {
|
||||
action = ac_dfa_state_datacheck_to_run(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_RUN_STATE: {
|
||||
action = ac_dfa_state_run(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE: {
|
||||
action = ac_dfa_state_run_to_dtlsteardown(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_RUN_TO_RESET_STATE: {
|
||||
action = ac_dfa_state_run_to_reset(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPWAP_DEAD_STATE: {
|
||||
action = ac_dfa_state_dead(session, packet);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
capwap_logging_debug("Unknown action event: %lu", session->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
static void ac_session_run(struct ac_session_t* session) {
|
||||
int check;
|
||||
int length;
|
||||
int isctrlsocket;
|
||||
int action = AC_DFA_ACCEPT_PACKET;
|
||||
char buffer[CAPWAP_MAX_PACKET_SIZE];
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
/* Configure DFA */
|
||||
if (g_ac.enabledtls) {
|
||||
action = AC_DFA_NO_PACKET;
|
||||
ac_dfa_change_state(session, CAPWAP_DTLS_SETUP_STATE);
|
||||
capwap_set_timeout(session->dfa.rfcWaitDTLS, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
} else {
|
||||
/* Wait Join request */
|
||||
ac_dfa_change_state(session, CAPWAP_JOIN_STATE);
|
||||
capwap_set_timeout(session->dfa.rfcWaitJoin, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
}
|
||||
|
||||
while (action != AC_DFA_DEAD) {
|
||||
/* Get packet */
|
||||
if ((action == AC_DFA_ACCEPT_PACKET) || (action == AC_DFA_DROP_PACKET)) {
|
||||
length = ac_network_read(session, buffer, sizeof(buffer), &isctrlsocket, &session->timeout);
|
||||
if (length < 0) {
|
||||
if (length == PACKET_TIMEOUT) {
|
||||
action = ac_dfa_execute(session, NULL); /* Timeout */
|
||||
} else if (length == DTLS_SHUTDOWN) {
|
||||
action = ac_session_teardown_connection(session);
|
||||
}
|
||||
} else if (length > 0) {
|
||||
/* Accept data packet only in running state */
|
||||
if (isctrlsocket || (session->state == CAPWAP_DATA_CHECK_TO_RUN_STATE) || (session->state == CAPWAP_RUN_STATE)) {
|
||||
/* Check generic capwap packet */
|
||||
check = capwap_sanity_check(isctrlsocket, CAPWAP_UNDEF_STATE, buffer, length, 0, 0);
|
||||
if (check == CAPWAP_PLAIN_PACKET) {
|
||||
struct capwap_packet packet;
|
||||
|
||||
check = capwap_defragment_packets(&session->wtpctrladdress, buffer, length, session->rxfragmentpacket, &packet);
|
||||
if (check == CAPWAP_RECEIVE_COMPLETE_PACKET) {
|
||||
int ignorepacket = 0;
|
||||
|
||||
if (isctrlsocket) {
|
||||
/* Check for already response to packet */
|
||||
if (capwap_recv_retrasmitted_request(&session->ctrldtls, &packet, session->remoteseqnumber, session->lastrecvpackethash, &session->ctrlsocket, session->responsefragmentpacket, &session->acctrladdress, &session->wtpctrladdress)) {
|
||||
ignorepacket = 1;
|
||||
}
|
||||
|
||||
/* Check message type */
|
||||
if (!capwap_check_message_type(&session->ctrldtls, &packet, session->mtu)) {
|
||||
ignorepacket = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
if (!ignorepacket && (action == AC_DFA_ACCEPT_PACKET)) {
|
||||
memcpy(&packet.socket, (isctrlsocket ? &session->ctrlsocket : &session->datasocket), sizeof(struct capwap_socket));
|
||||
action = ac_dfa_execute(session, &packet);
|
||||
}
|
||||
|
||||
/* Free packet */
|
||||
capwap_free_packet(&packet);
|
||||
} else if (check != CAPWAP_REQUEST_MORE_FRAGMENT) {
|
||||
/* Discard fragments */
|
||||
capwap_defragment_remove_sender(session->rxfragmentpacket, &session->wtpctrladdress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
action = ac_dfa_execute(session, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release reference session */
|
||||
if (!ac_session_release_reference(session)) {
|
||||
capwap_logging_debug("Reference session is > 0 to exit thread");
|
||||
}
|
||||
}
|
||||
|
||||
/* Change WTP state machine */
|
||||
void ac_dfa_change_state(struct ac_session_t* session, int state) {
|
||||
ASSERT(session != NULL);
|
||||
|
||||
if (state != session->state) {
|
||||
#ifdef DEBUG
|
||||
char sessionname[33];
|
||||
capwap_sessionid_printf(&session->sessionid, sessionname);
|
||||
capwap_logging_debug("Session AC %s change state from %s to %s", sessionname, capwap_dfa_getname(session->state), capwap_dfa_getname(state));
|
||||
#endif
|
||||
|
||||
session->state = state;
|
||||
}
|
||||
}
|
||||
|
||||
/* Teardown connection */
|
||||
int ac_session_teardown_connection(struct ac_session_t* session) {
|
||||
ASSERT(session != NULL);
|
||||
|
||||
/* Close DTSL Control */
|
||||
if (session->ctrldtls.enable) {
|
||||
capwap_crypt_close(&session->ctrldtls);
|
||||
}
|
||||
|
||||
/* Close DTLS Data */
|
||||
if (session->datadtls.enable) {
|
||||
capwap_crypt_close(&session->datadtls);
|
||||
}
|
||||
|
||||
/* */
|
||||
capwap_killall_timeout(&session->timeout);
|
||||
capwap_set_timeout(session->dfa.rfcDTLSSessionDelete, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION);
|
||||
ac_dfa_change_state(session, CAPWAP_DTLS_TEARDOWN_STATE);
|
||||
return AC_DFA_DROP_PACKET;
|
||||
}
|
||||
|
||||
/* Release reference of session */
|
||||
int ac_session_release_reference(struct ac_session_t* session) {
|
||||
int remove = 0;
|
||||
struct capwap_list_item* search;
|
||||
|
||||
ASSERT(session != NULL);
|
||||
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
|
||||
session->count--;
|
||||
if (!session->count) {
|
||||
search = g_ac.sessions->first;
|
||||
while (search != NULL) {
|
||||
struct ac_session_t* item = (struct ac_session_t*)search->item;
|
||||
if (session == item) {
|
||||
/* Free DTSL Control */
|
||||
capwap_crypt_freesession(&session->ctrldtls);
|
||||
|
||||
/* Free DTLS Data */
|
||||
capwap_crypt_freesession(&session->datadtls);
|
||||
|
||||
/* Free resource */
|
||||
capwap_event_destroy(&session->waitpacket);
|
||||
capwap_lock_exit(&session->packetslock);
|
||||
capwap_list_free(session->controlpackets);
|
||||
capwap_list_free(session->datapackets);
|
||||
capwap_defragment_free_list(session->rxfragmentpacket);
|
||||
|
||||
/* Free fragments packet */
|
||||
capwap_fragment_free(session->requestfragmentpacket);
|
||||
capwap_fragment_free(session->responsefragmentpacket);
|
||||
capwap_array_free(session->requestfragmentpacket);
|
||||
capwap_array_free(session->responsefragmentpacket);
|
||||
|
||||
/* Free DFA resource */
|
||||
capwap_array_free(session->dfa.acipv4list);
|
||||
capwap_array_free(session->dfa.acipv6list);
|
||||
|
||||
/* Remove item from list */
|
||||
remove = 1;
|
||||
capwap_itemlist_free(capwap_itemlist_remove(g_ac.sessions, search));
|
||||
capwap_event_signal(&g_ac.changesessionlist);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
search = search->next;
|
||||
}
|
||||
}
|
||||
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
|
||||
return remove;
|
||||
}
|
||||
|
||||
/* */
|
||||
void* ac_session_thread(void* param) {
|
||||
ASSERT(param != NULL);
|
||||
|
||||
capwap_logging_debug("Session start");
|
||||
ac_session_run((struct ac_session_t*)param);
|
||||
capwap_logging_debug("Session end");
|
||||
|
||||
/* Thread exit */
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* */
|
||||
void ac_get_control_information(struct capwap_list* controllist) {
|
||||
struct capwap_list* addrlist;
|
||||
struct capwap_list_item* item;
|
||||
|
||||
ASSERT(controllist != NULL);
|
||||
|
||||
/* Detect local address */
|
||||
addrlist = capwap_list_create();
|
||||
capwap_interface_list(&g_ac.net, addrlist);
|
||||
|
||||
/* Prepare control list */
|
||||
for (item = addrlist->first; item != NULL; item = item->next) {
|
||||
struct capwap_list_item* itemcontrol;
|
||||
struct ac_session_control* sessioncontrol;
|
||||
struct sockaddr_storage* address = (struct sockaddr_storage*)item->item;
|
||||
|
||||
/* */
|
||||
itemcontrol = capwap_itemlist_create(sizeof(struct ac_session_control));
|
||||
sessioncontrol = (struct ac_session_control*)itemcontrol->item;
|
||||
memcpy(&sessioncontrol->localaddress, address, sizeof(struct sockaddr_storage));
|
||||
sessioncontrol->count = 0;
|
||||
|
||||
/* Add */
|
||||
capwap_itemlist_insert_after(controllist, NULL, itemcontrol);
|
||||
}
|
||||
|
||||
/* Free local address list */
|
||||
capwap_list_free(addrlist);
|
||||
|
||||
/* */
|
||||
capwap_lock_enter(&g_ac.sessionslock);
|
||||
|
||||
/* Get wtp count from any local address */
|
||||
for (item = controllist->first; item != NULL; item = item->next) {
|
||||
struct capwap_list_item* search;
|
||||
struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item;
|
||||
|
||||
for (search = g_ac.sessions->first; search != NULL; search = search->next) {
|
||||
struct ac_session_t* session = (struct ac_session_t*)search->item;
|
||||
|
||||
if (!capwap_compare_ip(&session->acctrladdress, &sessioncontrol->localaddress)) {
|
||||
sessioncontrol->count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
capwap_lock_exit(&g_ac.sessionslock);
|
||||
}
|
||||
|
||||
/* */
|
||||
void ac_free_reference_last_request(struct ac_session_t* session) {
|
||||
ASSERT(session);
|
||||
|
||||
capwap_fragment_free(session->requestfragmentpacket);
|
||||
}
|
||||
|
||||
/* */
|
||||
void ac_free_reference_last_response(struct ac_session_t* session) {
|
||||
ASSERT(session);
|
||||
|
||||
capwap_fragment_free(session->responsefragmentpacket);
|
||||
memset(&session->lastrecvpackethash[0], 0, sizeof(session->lastrecvpackethash));
|
||||
}
|
112
src/ac/ac_session.h
Normal file
112
src/ac/ac_session.h
Normal file
@ -0,0 +1,112 @@
|
||||
#ifndef __AC_SESSION_HEADER__
|
||||
#define __AC_SESSION_HEADER__
|
||||
|
||||
#include "capwap_dtls.h"
|
||||
|
||||
#define AC_DFA_NO_PACKET 0
|
||||
#define AC_DFA_ACCEPT_PACKET 1
|
||||
#define AC_DFA_DROP_PACKET 2
|
||||
#define AC_DFA_DEAD 3
|
||||
|
||||
/* AC packet */
|
||||
struct ac_packet {
|
||||
int plainbuffer;
|
||||
char buffer[0];
|
||||
};
|
||||
|
||||
/* */
|
||||
struct ac_session_control {
|
||||
struct sockaddr_storage localaddress;
|
||||
unsigned short count;
|
||||
};
|
||||
|
||||
/* AC sessions */
|
||||
struct ac_session_t {
|
||||
struct ac_state dfa;
|
||||
|
||||
unsigned long count;
|
||||
struct sockaddr_storage acctrladdress;
|
||||
struct sockaddr_storage acdataaddress;
|
||||
struct sockaddr_storage wtpctrladdress;
|
||||
struct sockaddr_storage wtpdataaddress;
|
||||
struct capwap_socket ctrlsocket;
|
||||
struct capwap_socket datasocket;
|
||||
struct timeout_control timeout;
|
||||
|
||||
struct capwap_sessionid_element sessionid;
|
||||
unsigned short binding;
|
||||
|
||||
struct capwap_dtls ctrldtls;
|
||||
struct capwap_dtls datadtls;
|
||||
|
||||
int closesession;
|
||||
pthread_t threadid;
|
||||
|
||||
capwap_event_t waitpacket;
|
||||
capwap_lock_t packetslock;
|
||||
struct capwap_list* controlpackets;
|
||||
struct capwap_list* datapackets;
|
||||
|
||||
unsigned char localseqnumber;
|
||||
unsigned char remoteseqnumber;
|
||||
unsigned short mtu;
|
||||
unsigned short fragmentid;
|
||||
capwap_fragment_list* rxfragmentpacket;
|
||||
capwap_fragment_packet_array* requestfragmentpacket;
|
||||
capwap_fragment_packet_array* responsefragmentpacket;
|
||||
unsigned char lastrecvpackethash[16];
|
||||
|
||||
unsigned long state;
|
||||
|
||||
struct capwap_imageidentifier_element startupimage;
|
||||
};
|
||||
|
||||
void* ac_session_thread(void* param);
|
||||
int ac_session_teardown_connection(struct ac_session_t* session);
|
||||
int ac_session_release_reference(struct ac_session_t* session);
|
||||
|
||||
void ac_dfa_change_state(struct ac_session_t* session, int state);
|
||||
|
||||
void ac_get_control_information(struct capwap_list* controllist);
|
||||
|
||||
void ac_free_reference_last_request(struct ac_session_t* session);
|
||||
void ac_free_reference_last_response(struct ac_session_t* session);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_join(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_postjoin(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_join_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_bio_send(struct capwap_dtls* dtls, char* buffer, int length, void* param);
|
||||
int ac_dfa_state_dtlssetup(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_dtlsconnect(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_dtlsconnect_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_configure(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_configure_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_datacheck(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_datacheck_to_run(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_datacheck_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_imagedata(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_imagedata_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_run(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_run_to_reset(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_run_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_reset(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_reset_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
/* */
|
||||
int ac_dfa_state_teardown(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
int ac_dfa_state_dead(struct ac_session_t* session, struct capwap_packet* packet);
|
||||
|
||||
#endif /* __AC_SESSION_HEADER__ */
|
Reference in New Issue
Block a user