First commit

This commit is contained in:
vemax78
2013-05-01 14:52:55 +02:00
commit 7dd6d43954
148 changed files with 32283 additions and 0 deletions

655
src/ac/ac.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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__ */