318 lines
6.7 KiB
C
318 lines
6.7 KiB
C
/*
|
|
* Copyright (C) 2012 Martin Willi
|
|
* Copyright (C) 2012 revosec AG
|
|
*
|
|
* This program 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 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program 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.
|
|
*/
|
|
|
|
#include "load_tester_control.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include <daemon.h>
|
|
#include <collections/hashtable.h>
|
|
#include <threading/thread.h>
|
|
#include <threading/mutex.h>
|
|
#include <threading/condvar.h>
|
|
#include <processing/jobs/callback_job.h>
|
|
|
|
typedef struct private_load_tester_control_t private_load_tester_control_t;
|
|
typedef struct init_listener_t init_listener_t;
|
|
|
|
/**
|
|
* Private data of an load_tester_control_t object.
|
|
*/
|
|
struct private_load_tester_control_t {
|
|
|
|
/**
|
|
* Public load_tester_control_t interface.
|
|
*/
|
|
load_tester_control_t public;
|
|
|
|
/**
|
|
* Load tester control stream service
|
|
*/
|
|
stream_service_t *service;
|
|
};
|
|
|
|
/**
|
|
* Listener to follow initiation progress
|
|
*/
|
|
struct init_listener_t {
|
|
|
|
/**
|
|
* implements listener_t
|
|
*/
|
|
listener_t listener;
|
|
|
|
/**
|
|
* Output stream to log to
|
|
*/
|
|
FILE *stream;
|
|
|
|
/**
|
|
* IKE_SAs we have started to initiate
|
|
*/
|
|
hashtable_t *initiated;
|
|
|
|
/**
|
|
* IKE_SAs we have completed to initiate (success or failure)
|
|
*/
|
|
hashtable_t *completed;
|
|
|
|
/**
|
|
* Mutex to lock IKE_SA tables
|
|
*/
|
|
mutex_t *mutex;
|
|
|
|
/**
|
|
* Condvar to wait for completion
|
|
*/
|
|
condvar_t *condvar;
|
|
};
|
|
|
|
/**
|
|
* Hashtable hash function
|
|
*/
|
|
static u_int hash(uintptr_t id)
|
|
{
|
|
return id;
|
|
}
|
|
|
|
/**
|
|
* Hashtable hash function
|
|
*/
|
|
static bool equals(uintptr_t a, uintptr_t b)
|
|
{
|
|
return a == b;
|
|
}
|
|
|
|
METHOD(listener_t, alert, bool,
|
|
init_listener_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
|
|
{
|
|
if (alert == ALERT_RETRANSMIT_SEND)
|
|
{
|
|
uintptr_t id;
|
|
bool match = FALSE;
|
|
|
|
id = ike_sa->get_unique_id(ike_sa);
|
|
this->mutex->lock(this->mutex);
|
|
if (this->initiated->get(this->initiated, (void*)id))
|
|
{
|
|
match = TRUE;
|
|
}
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (match)
|
|
{
|
|
fprintf(this->stream, "*");
|
|
fflush(this->stream);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
METHOD(listener_t, ike_state_change, bool,
|
|
init_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
|
|
{
|
|
if (state == IKE_ESTABLISHED || state == IKE_DESTROYING)
|
|
{
|
|
uintptr_t id;
|
|
bool match = FALSE;
|
|
|
|
id = ike_sa->get_unique_id(ike_sa);
|
|
this->mutex->lock(this->mutex);
|
|
if (this->initiated->get(this->initiated, (void*)id))
|
|
{
|
|
match = !this->completed->put(this->completed, (void*)id, (void*)id);
|
|
}
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (match)
|
|
{
|
|
this->condvar->signal(this->condvar);
|
|
fprintf(this->stream, state == IKE_ESTABLISHED ? "+" : "-");
|
|
fflush(this->stream);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Logging callback function used during initiate
|
|
*/
|
|
static bool initiate_cb(init_listener_t *this, debug_t group, level_t level,
|
|
ike_sa_t *ike_sa, const char *message)
|
|
{
|
|
uintptr_t id;
|
|
|
|
if (ike_sa)
|
|
{
|
|
id = ike_sa->get_unique_id(ike_sa);
|
|
this->mutex->lock(this->mutex);
|
|
this->initiated->put(this->initiated, (void*)id, (void*)id);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Accept connections, initiate load-test, write progress to stream
|
|
*/
|
|
static bool on_accept(private_load_tester_control_t *this, stream_t *io)
|
|
{
|
|
init_listener_t *listener;
|
|
enumerator_t *enumerator;
|
|
peer_cfg_t *peer_cfg;
|
|
child_cfg_t *child_cfg;
|
|
u_int i, count, failed = 0, delay = 0;
|
|
char buf[16] = "";
|
|
FILE *stream;
|
|
|
|
stream = io->get_file(io);
|
|
if (!stream)
|
|
{
|
|
return FALSE;
|
|
}
|
|
fflush(stream);
|
|
if (fgets(buf, sizeof(buf), stream) == NULL)
|
|
{
|
|
fclose(stream);
|
|
return FALSE;
|
|
}
|
|
if (sscanf(buf, "%u %u", &count, &delay) < 1)
|
|
{
|
|
fclose(stream);
|
|
return FALSE;
|
|
}
|
|
|
|
INIT(listener,
|
|
.listener = {
|
|
.ike_state_change = _ike_state_change,
|
|
.alert = _alert,
|
|
},
|
|
.stream = stream,
|
|
.initiated = hashtable_create((void*)hash, (void*)equals, count),
|
|
.completed = hashtable_create((void*)hash, (void*)equals, count),
|
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
|
|
);
|
|
|
|
charon->bus->add_listener(charon->bus, &listener->listener);
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
|
|
"load-test");
|
|
if (!peer_cfg)
|
|
{
|
|
failed++;
|
|
fprintf(stream, "!");
|
|
continue;
|
|
}
|
|
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
|
|
if (!enumerator->enumerate(enumerator, &child_cfg))
|
|
{
|
|
enumerator->destroy(enumerator);
|
|
peer_cfg->destroy(peer_cfg);
|
|
failed++;
|
|
fprintf(stream, "!");
|
|
continue;
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
switch (charon->controller->initiate(charon->controller,
|
|
peer_cfg, child_cfg->get_ref(child_cfg),
|
|
(void*)initiate_cb, listener, 0, FALSE))
|
|
{
|
|
case NEED_MORE:
|
|
/* Callback returns FALSE once it got track of this IKE_SA.
|
|
* FALL */
|
|
case SUCCESS:
|
|
fprintf(stream, ".");
|
|
break;
|
|
default:
|
|
fprintf(stream, "!");
|
|
break;
|
|
}
|
|
if (delay)
|
|
{
|
|
usleep(delay * 1000);
|
|
}
|
|
fflush(stream);
|
|
}
|
|
|
|
listener->mutex->lock(listener->mutex);
|
|
while (listener->completed->get_count(listener->completed) < count - failed)
|
|
{
|
|
listener->condvar->wait(listener->condvar, listener->mutex);
|
|
}
|
|
listener->mutex->unlock(listener->mutex);
|
|
|
|
charon->bus->remove_listener(charon->bus, &listener->listener);
|
|
|
|
listener->initiated->destroy(listener->initiated);
|
|
listener->completed->destroy(listener->completed);
|
|
listener->mutex->destroy(listener->mutex);
|
|
listener->condvar->destroy(listener->condvar);
|
|
free(listener);
|
|
|
|
fprintf(stream, "\n");
|
|
fclose(stream);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
METHOD(load_tester_control_t, destroy, void,
|
|
private_load_tester_control_t *this)
|
|
{
|
|
DESTROY_IF(this->service);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
load_tester_control_t *load_tester_control_create()
|
|
{
|
|
private_load_tester_control_t *this;
|
|
char *uri;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.destroy = _destroy,
|
|
},
|
|
);
|
|
|
|
uri = lib->settings->get_str(lib->settings,
|
|
"%s.plugins.load-tester.socket", "unix://" LOAD_TESTER_SOCKET,
|
|
lib->ns);
|
|
this->service = lib->streams->create_service(lib->streams, uri, 10);
|
|
if (this->service)
|
|
{
|
|
this->service->on_accept(this->service, (stream_service_cb_t)on_accept,
|
|
this, JOB_PRIO_CRITICAL, 0);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_CFG, "creating load-tester control socket failed");
|
|
}
|
|
return &this->public;
|
|
}
|