freewtp/src/dfa_discovery.c

332 lines
11 KiB
C

#include "wtp.h"
#include "capwap_dfa.h"
#include "element.h"
#include "array.h"
#include "list.h"
#include "dfa.h"
/* */
void wtp_free_discovery_response_array(void)
{
int i;
/* Free items */
for (i = 0; i < g_wtp.acdiscoveryresponse->count; i++) {
struct wtp_discovery_response* response =
(struct wtp_discovery_response*)capwap_array_get_item_pointer(g_wtp.acdiscoveryresponse, i);
capwap_array_free(response->controlipv4);
capwap_array_free(response->controlipv6);
}
/* Remove all items */
capwap_array_resize(g_wtp.acdiscoveryresponse, 0);
}
static void wtp_send_discovery_request()
{
int i;
struct capwap_header_data capwapheader;
struct capwap_packet_txmng* txmngpacket;
if (g_wtp.net.socket < 0)
if (capwap_bind_sockets(&g_wtp.net) < 0) {
log_printf(LOG_EMERG, "Cannot bind control address");
exit(-1);
}
/* Update status radio */
g_wtp.descriptor.radiosinuse = wtp_update_radio_in_use();
/* Build packet */
capwap_header_init(&capwapheader, CAPWAP_RADIOID_NONE, g_wtp.binding);
txmngpacket = capwap_packet_txmng_create_ctrl_message(&capwapheader, CAPWAP_DISCOVERY_REQUEST, g_wtp.localseqnumber, g_wtp.mtu);
/* Add message element */
capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_DISCOVERYTYPE, &g_wtp.discoverytype);
capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_WTPBOARDDATA, &g_wtp.boarddata);
capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_WTPDESCRIPTOR, &g_wtp.descriptor);
capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_WTPFRAMETUNNELMODE, &g_wtp.mactunnel);
capwap_packet_txmng_add_message_element(txmngpacket, CAPWAP_ELEMENT_WTPMACTYPE, &g_wtp.mactype);
if (g_wtp.binding == CAPWAP_WIRELESS_BINDING_IEEE80211) {
wtp_create_80211_wtpradioinformation_element(txmngpacket);
wtp_create_80211_encryption_capability_elements(txmngpacket);
wtp_create_80211_supported_mac_profiles_elements(txmngpacket);
}
/* CAPWAP_ELEMENT_MTUDISCOVERY */ /* TODO */
/* CAPWAP_ELEMENT_VENDORPAYLOAD */ /* TODO */
/* Discovery request complete, get fragment packets */
wtp_free_reference_last_request();
capwap_packet_txmng_get_fragment_packets(txmngpacket, g_wtp.requestfragmentpacket, g_wtp.fragmentid);
if (g_wtp.requestfragmentpacket->count > 1)
g_wtp.fragmentid++;
/* Free packets manager */
capwap_packet_txmng_free(txmngpacket);
/* Send discovery request to AC */
for (i = 0; i < g_wtp.acdiscoveryarray->count; i++) {
struct addr_capwap* addr = capwap_array_get_item_pointer(g_wtp.acdiscoveryarray, i);
if (!addr->resolved) {
if (capwap_address_from_string(addr->fqdn, &addr->sockaddr)) {
if (!CAPWAP_GET_NETWORK_PORT(&addr->sockaddr)) {
CAPWAP_SET_NETWORK_PORT(&addr->sockaddr, CAPWAP_CONTROL_PORT);
}
addr->resolved = 1;
g_wtp.discoverytype.type = CAPWAP_DISCOVERYTYPE_TYPE_STATIC;
} else {
log_printf(LOG_INFO, "%s:%d Could not resolve application.acdiscovery.host %s",
__FILE__, __LINE__, addr->fqdn);
}
}
if (!capwap_sendto_fragmentpacket(g_wtp.net.socket,
g_wtp.requestfragmentpacket,
&addr->sockaddr)) {
log_printf(LOG_DEBUG, "Warning: error to send discovery request packet");
}
}
/* Don't buffering a packets sent */
wtp_free_reference_last_request();
}
/* */
static void wtp_dfa_state_discovery_timeout(EV_P_ ev_timer *w, int revents)
{
long discoveryinterval;
if (g_wtp.acdiscoveryresponse->count > 0) {
int i, j, w;
int countwtp = -1;
int indexpreferred = -1;
union sockaddr_capwap checkaddr;
union sockaddr_capwap peeraddr;
/* */
peeraddr.ss.ss_family = AF_UNSPEC;
/* Selected by preferred or less WTP by AC */
for (i = 0; i < g_wtp.acdiscoveryresponse->count; i++) {
struct wtp_discovery_response* response = (struct wtp_discovery_response*)capwap_array_get_item_pointer(g_wtp.acdiscoveryresponse, i);
/* AC with IPv4 */
for (w = 0; w < response->controlipv4->count; w++) {
struct capwap_controlipv4_element* controlipv4 = (struct capwap_controlipv4_element*)capwap_array_get_item_pointer(response->controlipv4, w);
/* Create IPv4 address */
checkaddr.sin.sin_family = AF_INET;
memcpy(&checkaddr.sin.sin_addr, &controlipv4->address, sizeof(struct in_addr));
checkaddr.sin.sin_port = htons(CAPWAP_CONTROL_PORT);
/* Check for preferred AC */
for (j = 0; j < ((indexpreferred != -1) ? indexpreferred : g_wtp.acpreferedarray->count); j++) {
struct addr_capwap* acpreferredaddr = (struct addr_capwap*)capwap_array_get_item_pointer(g_wtp.acpreferedarray, j);
if (!acpreferredaddr->resolved) {
if (capwap_address_from_string(acpreferredaddr->fqdn, &acpreferredaddr->sockaddr)) {
if (!CAPWAP_GET_NETWORK_PORT(&acpreferredaddr->sockaddr)) {
CAPWAP_SET_NETWORK_PORT(&acpreferredaddr->sockaddr, CAPWAP_CONTROL_PORT);
}
acpreferredaddr->resolved = 1;
} else {
log_printf(LOG_INFO, "%s:%d Could not resolve application.acprefered.host %s", __FILE__, __LINE__, acpreferredaddr->fqdn);
}
}
if (!capwap_compare_ip(&acpreferredaddr->sockaddr, &checkaddr)) {
indexpreferred = j;
memcpy(&peeraddr, &checkaddr, sizeof(union sockaddr_capwap));
break;
}
}
/* Check by number of WTP */
if (indexpreferred == -1) {
if ((countwtp == -1) || (countwtp > controlipv4->wtpcount)) {
countwtp = controlipv4->wtpcount;
memcpy(&peeraddr, &checkaddr, sizeof(union sockaddr_capwap));
}
}
}
/* AC with IPv6 */
if (g_wtp.net.localaddr.ss.ss_family == AF_INET6) {
for (w = 0; w < response->controlipv6->count; w++) {
struct capwap_controlipv6_element* controlipv6 = (struct capwap_controlipv6_element*)capwap_array_get_item_pointer(response->controlipv6, w);
/* Create IPv6 address */
checkaddr.sin6.sin6_family = AF_INET6;
memcpy(&checkaddr.sin6.sin6_addr, &controlipv6->address, sizeof(struct in6_addr));
checkaddr.sin6.sin6_port = htons(CAPWAP_CONTROL_PORT);
/* Check for preferred AC */
for (j = 0; j < ((indexpreferred != -1) ? indexpreferred : g_wtp.acpreferedarray->count); j++) {
struct addr_capwap* acpreferredaddr = (struct addr_capwap*)capwap_array_get_item_pointer(g_wtp.acpreferedarray, j);
if (!acpreferredaddr->resolved) {
if (capwap_address_from_string(acpreferredaddr->fqdn, &acpreferredaddr->sockaddr)) {
if (!CAPWAP_GET_NETWORK_PORT(&acpreferredaddr->sockaddr)) {
CAPWAP_SET_NETWORK_PORT(&acpreferredaddr->sockaddr, CAPWAP_CONTROL_PORT);
}
acpreferredaddr->resolved = 1;
} else {
log_printf(LOG_INFO, "Could not resolve application.acprefered.host %s", acpreferredaddr->fqdn);
}
}
if (!capwap_compare_ip(&acpreferredaddr->sockaddr, &checkaddr)) {
indexpreferred = j;
memcpy(&peeraddr, &checkaddr, sizeof(union sockaddr_capwap));
break;
}
}
/* Check by number of WTP */
if (indexpreferred == -1) {
if ((countwtp == -1) || (countwtp > controlipv6->wtpcount)) {
countwtp = controlipv6->wtpcount;
memcpy(&peeraddr, &checkaddr, sizeof(union sockaddr_capwap));
}
}
}
}
}
/* Free memory */
wtp_free_discovery_response_array();
/* Change state if found AC */
if (peeraddr.ss.ss_family != AF_UNSPEC) {
union sockaddr_capwap localaddr;
if (capwap_connect_socket(&g_wtp.net, &peeraddr) < 0) {
log_printf(LOG_EMERG, "Cannot bind control address");
wtp_socket_io_stop();
capwap_close_sockets(&g_wtp.net);
return;
}
/* Retrieve local address */
if (capwap_getsockname(&g_wtp.net, &localaddr) < 0) {
log_printf(LOG_EMERG, "Cannot get local endpoint address");
wtp_socket_io_stop();
capwap_close_sockets(&g_wtp.net);
return;
}
/* */
capwap_crypt_setconnection(&g_wtp.dtls, g_wtp.net.socket, &localaddr, &peeraddr);
/* */
if (!g_wtp.enabledtls) {
wtp_dfa_change_state(CAPWAP_JOIN_STATE); /* Bypass DTLS connection */
} else {
wtp_start_dtlssetup(); /* Create DTLS connection */
}
return;
}
}
/* No Discovery response received */
g_wtp.discoverycount++;
if (g_wtp.discoverycount >= WTP_MAX_DISCOVERY_COUNT) {
/* Timeout discovery state */
wtp_dfa_change_state(CAPWAP_SULKING_STATE);
return;
}
wtp_send_discovery_request();
/* Wait before send another Discovery Request */
discoveryinterval = capwap_get_rand(g_wtp.discoveryinterval - WTP_MIN_DISCOVERY_INTERVAL) +
WTP_MIN_DISCOVERY_INTERVAL;
w->repeat = discoveryinterval / 1000.0;
ev_timer_again(EV_A_ w);
}
/* */
void wtp_dfa_state_discovery_enter(void)
{
long discoveryinterval;
discoveryinterval = capwap_get_rand(g_wtp.discoveryinterval - WTP_MIN_DISCOVERY_INTERVAL) +
WTP_MIN_DISCOVERY_INTERVAL;
/* Wait before send Discovery Request */
ev_timer_init(&g_wtp.timercontrol, wtp_dfa_state_discovery_timeout,
0., discoveryinterval / 1000.0);
ev_timer_again(EV_DEFAULT_UC_ &g_wtp.timercontrol);
}
/* */
void wtp_dfa_state_discovery(struct capwap_parsed_packet* packet)
{
unsigned short binding;
struct capwap_resultcode_element* resultcode;
int i;
struct wtp_discovery_response* response;
struct capwap_array* controlip;
ASSERT(packet != NULL);
if (packet->rxmngpacket->ctrlmsg.type != CAPWAP_DISCOVERY_RESPONSE) {
log_printf(LOG_DEBUG, "Unexpected message %d in state Discovery",
packet->rxmngpacket->ctrlmsg.type);
return;
}
/* */
binding = GET_WBID_HEADER(packet->rxmngpacket->header);
if (binding != g_wtp.binding) {
log_printf(LOG_DEBUG, "Discovery Response for invalid binding");
return;
}
if (g_wtp.localseqnumber != packet->rxmngpacket->ctrlmsg.seq) {
log_printf(LOG_DEBUG, "Discovery Response with invalid sequence (%d != %d)",
g_wtp.localseqnumber, packet->rxmngpacket->ctrlmsg.seq);
return;
}
/* */
g_wtp.localseqnumber++;
/* Check the success of the Request */
resultcode = (struct capwap_resultcode_element*)capwap_get_message_element_data(packet,
CAPWAP_ELEMENT_RESULTCODE);
if (resultcode && !CAPWAP_RESULTCODE_OK(resultcode->code))
return;
response = (struct wtp_discovery_response*)
capwap_array_get_item_pointer(g_wtp.acdiscoveryresponse, g_wtp.acdiscoveryresponse->count);
/* */
response->controlipv4 = capwap_array_create(sizeof(struct capwap_controlipv4_element), 0, 0);
response->controlipv6 = capwap_array_create(sizeof(struct capwap_controlipv6_element), 0, 0);
/* Create controlipv4 */
controlip = (struct capwap_array*)capwap_get_message_element_data(packet, CAPWAP_ELEMENT_CONTROLIPV4);
if (controlip) {
for (i = 0; i < controlip->count; i++) {
struct capwap_controlipv4_element* src =
*(struct capwap_controlipv4_element**)capwap_array_get_item_pointer(controlip, i);
struct capwap_controlipv4_element* dst =
(struct capwap_controlipv4_element*)capwap_array_get_item_pointer(response->controlipv4, i);
memcpy(dst, src, sizeof(struct capwap_controlipv4_element));
}
}
/* Create controlipv6 */
controlip = (struct capwap_array*)capwap_get_message_element_data(packet, CAPWAP_ELEMENT_CONTROLIPV6);
if (controlip) {
for (i = 0; i < (controlip)->count; i++) {
struct capwap_controlipv6_element* src =
*(struct capwap_controlipv6_element**)capwap_array_get_item_pointer((controlip), i);
struct capwap_controlipv6_element* dst =
(struct capwap_controlipv6_element*)capwap_array_get_item_pointer(response->controlipv6, i);
memcpy(dst, src, sizeof(struct capwap_controlipv6_element));
}
}
}