actube/src/ac/wtpman.c

811 lines
18 KiB
C

/*
This file is part of actube.
actube is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libcapwap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include "cw/capwap.h"
#include "cw/sock.h"
#include "cw/log.h"
#include "cw/timer.h"
#include "cw/cw.h"
#include "cw/dtls.h"
#include "cw/dbg.h"
#include "cw/conn.h"
#include "cw/format.h"
#include "ac.h"
#include "conf.h"
#include "socklist.h"
#include "wtpman.h"
#include "wtplist.h"
#include "cw/val.h"
#include "actube.h"
#include "cw/dot11.h"
static void wtpman_remove(struct wtpman *wtpman)
{
wtplist_lock();
wtplist_remove(wtpman);
wtplist_unlock();
wtpman_destroy(wtpman);
}
static void wtpman_run_discovery(void *arg)
{
struct wtpman *wtpman = (struct wtpman *) arg;
time_t timer = cw_timer_start(10);
wtpman->conn->capwap_state = CAPWAP_STATE_DISCOVERY;
while (!cw_timer_timeout(timer)
&& wtpman->conn->capwap_state == CAPWAP_STATE_DISCOVERY) {
int rc;
rc = cw_read_messages(wtpman->conn);
if (cw_result_is_ok(rc)) {
wtpman->conn->capwap_state = CAPWAP_STATE_JOIN;
cw_dbg(DBG_INFO, "Discovery has detected mods: %s %s",
wtpman->conn->cmod->name,
wtpman->conn->bmod->name);
wtplist_lock();
discovery_cache_add(discovery_cache,
(struct sockaddr *) &wtpman->conn->
addr, wtpman->conn->cmod,
wtpman->conn->bmod);
wtplist_unlock();
}
}
return;
}
static int wtpman_dtls_setup(void *arg)
{
char cipherstr[512];
char sock_buf[SOCK_ADDR_BUFSIZE];
struct wtpman *wtpman = (struct wtpman *) arg;
/* try to accept the connection */
if (!dtls_accept(wtpman->conn)) {
cw_dbg(DBG_DTLS, "Error establishing DTLS session with %s",
sock_addr2str_p(&wtpman->conn->addr, sock_buf));
return 0;
}
cw_dbg(DBG_DTLS, "DTLS session established with %s, %s",
sock_addr2str_p(&wtpman->conn->addr, sock_buf),
dtls_get_cipher(wtpman->conn, cipherstr));
return 1;
}
/*
static int wtpman_join(void *arg)
{
int rc;
char sock_buf[SOCK_ADDR_BUFSIZE];
struct wtpman *wtpman = (struct wtpman *) arg;
struct cw_Conn *conn = wtpman->conn;
time_t timer, wait_join;
cw_dbg(DBG_INFO, "Join State - %s",
sock_addr2str(&conn->addr, sock_buf));
wait_join =
cw_ktv_get_word(conn->global_cfg, "wait-join", CAPWAP_WAIT_JOIN);
timer = cw_timer_start(wait_join);
while (!cw_timer_timeout(timer)
&& wtpman->conn->capwap_state == CAPWAP_STATE_JOIN) {
rc = cw_read_messages(wtpman->conn);
if (rc < 0) {
if (errno == EAGAIN)
continue;
break;
}
cw_dbg_ktv_dump(conn->remote_cfg, DBG_INFO,
"-------------dump------------",
"DMP", "---------end dump --------");
}
if (rc != 0) {
cw_log(LOG_ERR, "Error joining WTP %s", cw_strerror(rc));
return 0;
}
if (wtpman->conn->capwap_state != CAPWAP_STATE_JOIN_COMPLETE) {
cw_dbg(DBG_MSG_ERR,
"No join request from %s after %d seconds, WTP died.",
sock_addr2str(&wtpman->conn->addr, sock_buf), wait_join);
return 0;
}
return 1;
}
*/
void wtpman_image_data(struct wtpman *wtpman)
{
/* char sock_buf[SOCK_ADDR_BUFSIZE];
struct cw_Conn *conn = wtpman->conn;
// Image upload
const char *filename = mbag_get_str(conn->outgoing, CW_ITEM_IMAGE_FILENAME, NULL);
if (!filename) {
cw_log(LOG_ERR,
"Can't send image to %s. No Image Filename Item found.",
sock_addr2str(&conn->addr,sock_buf));
return;
}
cw_dbg(DBG_INFO, "Sending image file '%s' to '%s'.", filename,
sock_addr2str(&conn->addr,sock_buf));
FILE *infile = fopen(filename, "rb");
if (infile == NULL) {
cw_log(LOG_ERR, "Can't open image %s: %s",
sock_addr2str(&conn->addr,sock_buf), strerror(errno));
return;
}
CW_CLOCK_DEFINE(clk);
cw_clock_start(&clk);
mbag_item_t *eof = mbag_set_const_ptr(conn->outgoing, CW_ITEM_IMAGE_FILEHANDLE,
infile);
int rc = 0;
while (conn->capwap_state == CW_STATE_IMAGE_DATA && rc == 0 && eof != NULL) {
rc = cw_send_request(conn, CAPWAP_MSG_IMAGE_DATA_REQUEST);
eof = mbag_get(conn->outgoing, CW_ITEM_IMAGE_FILEHANDLE);
}
if (rc) {
cw_log(LOG_ERR, "Error sending image to %s: %s",
sock_addr2str(&conn->addr,sock_buf), cw_strrc(rc));
} else {
cw_dbg(DBG_INFO, "Image '%s' sucessful sent to %s in %0.1f seconds.",
filename, sock_addr2str(&conn->addr,sock_buf), cw_clock_stop(&clk));
conn->capwap_state = CW_STATE_NONE;
}
fclose(infile);
wtpman_remove(wtpman);
*/
}
void *wtpman_run_data(void *wtpman_arg)
{
return NULL;
/*
struct wtpman *wtpman = (struct wtpman *) wtpman_arg;
struct cw_Conn *conn = wtpman->conn;
uint8_t data[1001];
memset(data, 0, 1000);
cw_log(LOG_ERR, "I am the data thread**********************************************************************\n");
while (1) {
sleep(5);
/ // conn->write_data(conn, data, 100); /
cw_log(LOG_ERR, "O was the data thread***********************************************************\n");
}
*/
}
int cw_run_state_machine(struct cw_Conn *conn, time_t * timer)
{
int timerval;
cw_StateMachineState_t search, *result;
while (1) {
search.state = conn->capwap_state;
search.prevstate = conn->capwap_prevstate;
result = mavl_get(conn->msgset->statemachine_states, &search);
cw_dbg(DBG_STATE, "State transition: [%s -> %s]",
cw_strstate(conn->capwap_prevstate),
cw_strstate(conn->capwap_state)
);
if (result == NULL) {
cw_log(LOG_ERR, "State not found");
return 0;
}
if (result->jump_state) {
conn->capwap_state = result->jump_state;
conn->capwap_prevstate = result->jump_prevstate;
cw_dbg(DBG_STATE, "Jump to state: [%s->%s]",
cw_strstate(conn->capwap_prevstate),
cw_strstate(conn->capwap_state));
continue;
}
if (result->dbgmsg) {
cw_dbg(DBG_STATE, "%s", result->dbgmsg);
}
if (result->timer_key) {
timerval =
cw_cfg_get_word(conn->local_cfg, result->timer_key,
result->timer_default);
*timer = cw_timer_start(timerval);
cw_dbg(DBG_STATE, "Starting timer: [%s] - %d seconds.",
result->timer_key, timerval);
}
return result->retval;
}
}
/*#define CW_TRANSITION(prestate,state) (prestate<<16|state)*/
int run_update(struct wtpman *wtpman)
{
int rc;
if (!wtpman->update)
return EAGAIN;
rc = cw_send_request(wtpman->conn, CAPWAP_MSG_CONFIGURATION_UPDATE_REQUEST);
cw_cfg_clear(wtpman->conn->update_cfg);
wtpman->update=0;
return rc;
}
static int dataman_process_msg(struct cw_Conn *nc, uint8_t * rawmsg, int len,
struct sockaddr *from)
{
char rframe[1000];
int offs = cw_get_hdr_msg_offset(rawmsg);
uint8_t * frame = rawmsg + offs;
int frame_len = len-offs;
cw_dbg_dot11_frame(frame,frame_len);
/* dot11_init_assoc_resp(frame);
dot11_copy_mac(dot11_get_sa(dot11frame),dot11_get_da(frame));
dot11_copy_mac(dot11_get_bssid(dot11frame),dot11_get_bssid(frame));
dot11_copy_mac(dot11_get_da(dot11frame),dot11_get_sa(frame));
dot11_set_seq(frame,0);
*/
if ( dot11_get_type_and_subtype(frame) == DOT11_ASSOC_REQ){
int l;
uint8_t rates[] = {
12,0x82,0x84,0x8b,0x96,0x0c,0x12,0x18,0x24,0x30,0x48,0x60,0x6c
};
cw_dbg(DBG_X, "there is an assoc request!");
uint8_t rframe[1000];
nc->mtu=800;
dot11_init_assoc_resp(rframe);
dot11_set_duration(rframe,100);
dot11_copy_mac(dot11_get_sa(frame),dot11_get_da(rframe));
dot11_copy_mac(dot11_get_bssid(frame),dot11_get_bssid(rframe));
dot11_copy_mac(dot11_get_da(frame),dot11_get_sa(rframe));
dot11_set_seq(rframe,dot11_get_seq(frame));
dot11_assoc_resp_set_cap(rframe,dot11_assoc_req_get_cap(frame));
dot11_assoc_resp_set_status_code(rframe,0);
dot11_assoc_resp_set_assoc_id(rframe,17);
l=24+6;
l+=dot11_put_supported_rates(rframe+l,rates);
cw_dbg_dot11_frame(rframe,l);
uint8_t buf[1024];
int hlen;
hlen = cw_init_capwap_packet(buf,1,0,NULL,NULL);
cw_set_hdr_flags(buf, CAPWAP_FLAG_HDR_T, 1);
cw_send_capwap_packet(nc,buf,hlen,rframe,l);
}
return 0;
}
static void *wtpman_data_main(void *arg)
{
struct wtpman * wtpman = arg;
struct cw_Conn * nc = wtpman->dconn;
nc->process_packet=conn_process_packet;
nc->process_message=dataman_process_msg;
while (1){
time_t timer = cw_timer_start(2);
while (!cw_timer_timeout(timer)){
cw_read_messages(nc);
}
}
}
static void *wtpman_main(void *arg)
{
//mavl_t r;
int rc;
time_t timer;
//char sock_buf[SOCK_ADDR_BUFSIZE];
struct cw_Conn *conn;
// int last_state;
struct wtpman *wtpman = (struct wtpman *) arg;
wtpman->conn->seqnum = 0;
conn = wtpman->conn;
// wtpman->conn->remote_cfg = cw_cfg_create();
if (!wtpman->dtlsmode) {
/* We were invoked with an unencrypted packet,
* so assume, it is a discovery request */
wtpman_run_discovery(arg);
wtpman_remove(wtpman);
return NULL;
}
/* reject connections to our multi- or broadcast sockets */
if (socklist[wtpman->socklistindex].type != SOCKLIST_UNICAST_SOCKET) {
cw_dbg(DBG_DTLS, "Reject multi");
wtpman_remove(wtpman);
return NULL;
}
conn->capwap_state = CAPWAP_STATE_DTLS_SETUP;
/* establish dtls session */
if (!wtpman_dtls_setup(wtpman)) {
wtpman_remove(wtpman);
return NULL;
}
/*last_state = conn->capwap_state;
conn->capwap_state = CAPWAP_STATE_JOIN;
*/
conn->capwap_prevstate = CAPWAP_STATE_DTLS_SETUP;
conn->capwap_state = CAPWAP_STATE_JOIN;
rc = 0;
wtpman->update=0;
while (1) {
//int wait_join;
//int wait_change_state;
if (!cw_run_state_machine(conn, &timer)) {
cw_dbg(DBG_INFO, "WTP died");
wtpman_remove(wtpman);
return NULL;
};
while (!cw_timer_timeout(timer)) {
rc = run_update(wtpman);
if (rc !=EAGAIN)
break;
rc = cw_read_messages(wtpman->conn);
if (rc < 0) {
if (errno == EAGAIN){
continue;
}
}
break;
}
if (rc < 0) {
conn->capwap_prevstate = conn->capwap_state;
conn->capwap_state = CAPWAP_STATE_TIMEOUT;
}
}
exit(0);
return NULL;
}
void wtpman_destroy(struct wtpman *wtpman)
{
if (wtpman->conn)
conn_destroy(wtpman->conn);
if (wtpman->wtp_cfg)
cw_cfg_destroy(wtpman->wtp_cfg);
free(wtpman);
}
static void copy(struct cw_ElemHandlerParams * params)
{
cw_cfg_copy(params->cfg, params->conn->remote_cfg,DBG_CFG_UPDATES,"GlobalCfg");
}
static void catch_cfg(struct cw_ElemHandlerParams * params, int create, const char *format, ...)
{
const char *wtpname;
char filename[200];
FILE *f;
cw_Cfg_t * cfg_list[3];
if (!cw_cfg_get_bool(params->conn->global_cfg,"actube/save-initial-wtp-config",0))
return;
cfg_list[0]=params->cfg;
cfg_list[1]=params->conn->remote_cfg;
cfg_list[2]=NULL;
wtpname = cw_cfg_get_l(cfg_list,"capwap/wtp-name","default");
sprintf(filename,"wtp-initial-%s.ckv",wtpname);
if (create)
f = fopen(filename,"w");
else
f = fopen(filename,"a");
if (f==NULL)
return;
if (create)
fprintf(f,"#\n# Initial config for WTP '%s' - catched by ACTube\n#\n\n",wtpname);
if (format !=NULL){
va_list args;
va_start(args,format);
vfprintf(f,format,args);
va_end(args);
}
cw_cfg_write_to_file(f, params->cfg);
fclose(f);
}
static int discovery_cb(struct cw_ElemHandlerParams * params, struct cw_MsgCb_data * d)
{
cw_dbg(DBG_X,"DISCOVERY Callback");
copy(params);
catch_cfg(params,1,"\n#\n# Discovery Request\n#\n");
cw_cfg_clear(params->cfg);
if (d->parent)
return d->parent->fun(params,d->parent);
return 0;
}
static int join_cb(struct cw_ElemHandlerParams * params, struct cw_MsgCb_data *d)
{
catch_cfg(params,0,"\n#\n# Join Request\n#\n");
if (d->parent)
return d->parent->fun(params,d->parent);
return 0;
}
/*
static int fill_update_cfg(struct cw_Conn * conn)
{
struct cw_Cfg_iter cfi;
struct cw_Cfg_entry *e;
cw_Cfg_t * u;
u=cw_cfg_create();
if( cw_cfg_load("status_response.ckv",u)){
cw_log(LOG_ERR,"Cant load file");
stop();
};
cw_cfg_dump(conn->remote_cfg);
cw_cfg_iter_init(u, &cfi, NULL);
while ((e = cw_cfg_iter_next(&cfi, NULL))!=NULL){
const char * r;
r = cw_cfg_get(conn->remote_cfg, e->key, "[]");
// cw_dbg(DBG_CFG_UPDATES,"check: %s: %s",e->key,e->val);
if (strcmp(r,e->val)==0)
continue;
cw_dbg(DBG_CFG_UPDATES,"Status reps: %s: %s -> %s",e->key,r,e->val);
}
return 0;
}
*/
static int cfg_status_cb(struct cw_ElemHandlerParams * params, struct cw_MsgCb_data *d)
{
catch_cfg(params,0,"\n#\n# Configuration Status Request\n#\n");
if (d->parent)
return d->parent->fun(params,d->parent);
return 0;
}
static int event_cb(struct cw_ElemHandlerParams * params, struct cw_MsgCb_data *d)
{
struct cw_Conn * conn = (struct cw_Conn*)params->conn;
struct wtpman * wtpman = (struct wtpman *)conn->data;
char filename[200];
cw_dbg(DBG_X,"WTP EVENT Callback");
copy(params);
const char * wtpname = cw_cfg_get(conn->remote_cfg,"capwap/wtp-name","default");
sprintf(filename,"wtp-event-%d-%s.ckv",wtpman->ctr++,wtpname);
cw_cfg_save(filename,params->cfg,NULL);
return 0;
}
static int change_state_event_cb(struct cw_ElemHandlerParams * params,struct cw_MsgCb_data * d)
{
struct cw_Conn * conn = (struct cw_Conn*)params->conn;
struct wtpman * wtpman = (struct wtpman *)conn->data;
char filename[200];
cw_dbg(DBG_X,"WTP EVENT Callback");
copy(params);
const char * wtpname = cw_cfg_get(conn->remote_cfg,"capwap/wtp-name","default");
sprintf(filename,"wtp-change-event-%d-%s.ckv",wtpman->ctr++,wtpname);
cw_cfg_save(filename,params->cfg,NULL);
return 0;
}
struct wtpman *wtpman_create(int socklistindex, struct sockaddr *srcaddr,
int dtlsmode, cw_Cfg_t * global_cfg)
{
struct sockaddr dbgaddr;
socklen_t dbgaddrl;
int sockfd, replyfd, data_sockfd,data_replyfd;
char sock_buf[SOCK_ADDR_BUFSIZE];
struct wtpman *wtpman;
wtpman = malloc(sizeof(struct wtpman));
if (!wtpman)
return 0;
wtpman->ctr=0;
if (socklist[socklistindex].type != SOCKLIST_UNICAST_SOCKET) {
int port = sock_getport(&socklist[socklistindex].addr);
replyfd = socklist_find_reply_socket(srcaddr, port);
data_replyfd=replyfd;
if (replyfd == -1) {
cw_log(LOG_ERR,
"Can't find reply socket for request from %s",
sock_addr2str(srcaddr, sock_buf));
free(wtpman);
return NULL;
}
} else {
replyfd = socklist[socklistindex].sockfd;
data_replyfd = socklist[socklistindex].data_sockfd;
}
sockfd = replyfd; /*//socklist[socklistindex].reply_sockfd; */
data_sockfd = data_replyfd;
dbgaddrl = sizeof(dbgaddr);
getsockname(sockfd, &dbgaddr, &dbgaddrl);
cw_dbg(DBG_INFO, "Creating wtpman on socket %d, %s:%d", sockfd,
sock_addr2str(&dbgaddr, sock_buf), sock_getport(&dbgaddr));
memset(wtpman, 0, sizeof(struct wtpman));
wtpman->global_cfg = global_cfg;
wtpman->conn = cw_conn_create(sockfd, srcaddr, 100);
if (!wtpman->conn) {
wtpman_destroy(wtpman);
return NULL;
}
wtpman->dconn = cw_conn_create(data_sockfd, srcaddr, 100);
if (!wtpman->dconn) {
wtpman_destroy(wtpman);
return NULL;
}
cw_conn_register_msg_cb(wtpman->conn,
CAPWAP_MSG_DISCOVERY_REQUEST,
discovery_cb);
wtpman->conn->global_cfg = global_cfg;
wtpman->conn->local_cfg = cw_cfg_create();
wtpman->wtp_cfg = cw_cfg_create();
wtpman->conn->role = CW_ROLE_AC;
wtpman->conn->data=wtpman;
wtpman->dconn->data=wtpman;
wtpman->conn->cfg_list[0]=wtpman->conn->update_cfg;
wtpman->conn->cfg_list[1]=wtpman->conn->remote_cfg;
wtpman->conn->cfg_list[2]=wtpman->conn->local_cfg;
wtpman->conn->cfg_list[3]=wtpman->conn->global_cfg;
wtpman->conn->cfg_list[4]=NULL;
wtpman->conn->data_sock = socklist[socklistindex].data_sockfd;
sock_copyaddr(&wtpman->conn->data_addr,
(struct sockaddr *) &wtpman->conn->addr);
// wtpman->conn->mods = conf_mods;
wtpman->conn->strict_capwap = conf_strict_capwap;
wtpman->conn->strict_hdr = conf_strict_headers;
// wtpman->conn->setup_complete = setup_complete;
/*
// wtpman->conn->radios = mbag_i_create();
// wtpman->conn->radios_upd = mbag_i_create();
// wtpman->conn->local = ac_config;
//wtpman->conn->capwap_mode=0; //CW_MODE_STD; //CISCO;
// wtpman->conn->capwap_mode = CW_MODE_CISCO;
//wtpman->conn->strict_capwap_hdr=0;
*/
/* when created caused by a packet in DTLS mode, we try
* to find out the modules to load, for detected connection
* from discovery request */
if (dtlsmode) {
int rc;
struct cw_Mod *cmod, *bmod;
rc = discovery_cache_get(discovery_cache, srcaddr, &cmod,
&bmod);
if (rc) {
cw_dbg(DBG_INFO, "Initializing with mod %s %s",
cmod->name, bmod->name);
wtpman->conn->msgset =
cw_mod_get_msg_set(wtpman->conn, cmod, bmod);
wtpman->dconn->msgset=wtpman->conn->msgset;
wtpman->conn->detected = 1;
cmod->setup_cfg(wtpman->conn);
// if (wtpman->conn->setup_complete)
// wtpman->conn->setup_complete(wtpman->conn);
cw_conn_register_msg_cb(wtpman->conn,
CAPWAP_MSG_JOIN_REQUEST,
join_cb);
cw_conn_register_msg_cb(wtpman->conn,
CAPWAP_MSG_CONFIGURATION_STATUS_REQUEST,
cfg_status_cb);
cw_conn_register_msg_cb(wtpman->conn,
CAPWAP_MSG_WTP_EVENT_REQUEST,
event_cb);
/* cw_conn_register_msg_cb(wtpman->conn,
CAPWAP_MSG_CHANGE_STATE_EVENT_REQUEST,
change_state_event_cb);*/
}
}
cw_dbg(DBG_X,"WTPMAN_CREATED: %p",wtpman);
return wtpman;
}
void wtpman_addpacket(struct wtpman *wtpman, uint8_t * packet, int len)
{
conn_q_add_packet(wtpman->conn, packet, len);
}
void wtpman_start(struct wtpman *wtpman, int dtlsmode)
{
cw_dbg(DBG_INFO, "Starting wtpman, DTLS mode = %d", dtlsmode);
wtpman->dtlsmode = dtlsmode;
pthread_create(&wtpman->thread, NULL, wtpman_main, (void *) wtpman);
pthread_create(&wtpman->thread, NULL, wtpman_data_main, (void *) wtpman);
return;
}
void wtpman_datapacket(struct wtpman *wtpman, uint8_t * packet, int len)
{
conn_q_add_packet(wtpman->dconn, packet, len);
}