reserve a socket only during request(), avoids thread pool starvation

reduced thread pool default to 1
This commit is contained in:
Martin Willi
2009-03-27 10:52:22 +00:00
parent 8f83b1472b
commit 510be1a32a
2 changed files with 36 additions and 36 deletions
+34 -32
View File
@@ -71,11 +71,6 @@ struct private_radius_client_t {
*/ */
radius_client_t public; radius_client_t public;
/**
* The clients socket from the pool
*/
entry_t *socket;
/** /**
* RADIUS servers State attribute * RADIUS servers State attribute
*/ */
@@ -165,7 +160,7 @@ bool radius_client_init()
return FALSE; return FALSE;
} }
count = lib->settings->get_int(lib->settings, count = lib->settings->get_int(lib->settings,
"charon.plugins.eap_radius.sockets", 5); "charon.plugins.eap_radius.sockets", 1);
sockets = linked_list_create(); sockets = linked_list_create();
mutex = mutex_create(MUTEX_DEFAULT); mutex = mutex_create(MUTEX_DEFAULT);
@@ -274,13 +269,14 @@ static radius_message_t* request(private_radius_client_t *this,
radius_message_t *req) radius_message_t *req)
{ {
char virtual[] = {0x00,0x00,0x00,0x05}; char virtual[] = {0x00,0x00,0x00,0x05};
radius_message_t *response; entry_t *socket;
chunk_t data; chunk_t data;
fd_set fds; int i;
int i;
socket = get_socket();
/* set Message Identifier */ /* set Message Identifier */
req->set_identifier(req, this->socket->identifier++); req->set_identifier(req, socket->identifier++);
/* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */ /* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */
req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual))); req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual)));
/* add our NAS-Identifier */ /* add our NAS-Identifier */
@@ -291,30 +287,34 @@ static radius_message_t* request(private_radius_client_t *this,
req->add(req, RAT_STATE, this->state); req->add(req, RAT_STATE, this->state);
} }
/* sign the request */ /* sign the request */
req->sign(req, this->socket->rng, this->socket->signer); req->sign(req, socket->rng, socket->signer);
data = req->get_encoding(req); data = req->get_encoding(req);
FD_ZERO(&fds);
FD_SET(this->socket->fd, &fds);
/* timeout after 2, 3, 4, 5 seconds */ /* timeout after 2, 3, 4, 5 seconds */
for (i = 2; i <= 5; i++) for (i = 2; i <= 5; i++)
{ {
radius_message_t *response;
bool retransmit = FALSE; bool retransmit = FALSE;
struct timeval tv; struct timeval tv;
char buf[1024]; char buf[1024];
int res, retry = 0; fd_set fds;
int res;
if (send(this->socket->fd, data.ptr, data.len, 0) != data.len) if (send(socket->fd, data.ptr, data.len, 0) != data.len)
{ {
DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno)); DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
put_socket(socket);
return NULL; return NULL;
} }
tv.tv_sec = i;
tv.tv_usec = 0;
while (TRUE) while (TRUE)
{ {
tv.tv_sec = i; FD_ZERO(&fds);
tv.tv_usec = 0; FD_SET(socket->fd, &fds);
res = select(socket->fd + 1, &fds, NULL, NULL, &tv);
res = select(this->socket->fd + 1, &fds, NULL, NULL, &tv); /* TODO: updated tv to time not waited. Linux does this for us. */
if (res < 0) if (res < 0)
{ /* failed */ { /* failed */
DBG1(DBG_CFG, "waiting for RADIUS message failed: %s", DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
@@ -327,7 +327,7 @@ static radius_message_t* request(private_radius_client_t *this,
retransmit = TRUE; retransmit = TRUE;
break; break;
} }
res = recv(this->socket->fd, buf, sizeof(buf), MSG_DONTWAIT); res = recv(socket->fd, buf, sizeof(buf), MSG_DONTWAIT);
if (res <= 0) if (res <= 0)
{ {
DBG1(DBG_CFG, "receiving RADIUS message failed: %s", DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
@@ -338,20 +338,15 @@ static radius_message_t* request(private_radius_client_t *this,
if (response) if (response)
{ {
if (response->verify(response, req->get_authenticator(req), if (response->verify(response, req->get_authenticator(req),
secret, this->socket->hasher, this->socket->signer)) secret, socket->hasher, socket->signer))
{ {
save_state(this, response); save_state(this, response);
put_socket(socket);
return response; return response;
} }
response->destroy(response); response->destroy(response);
} }
/* might be a maliciously injected packet, read onother one. DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
* Limit the number of retries, an attacker could us trick into
* a loop otherwise. */
if (retry++ > 5)
{
break;
}
} }
if (!retransmit) if (!retransmit)
{ {
@@ -359,6 +354,7 @@ static radius_message_t* request(private_radius_client_t *this,
} }
} }
DBG1(DBG_CFG, "RADIUS server is not responding"); DBG1(DBG_CFG, "RADIUS server is not responding");
put_socket(socket);
return NULL; return NULL;
} }
@@ -370,6 +366,7 @@ static chunk_t decrypt_mppe_key(private_radius_client_t *this, u_int16_t salt,
{ {
chunk_t A, R, P, seed; chunk_t A, R, P, seed;
u_char *c, *p; u_char *c, *p;
hasher_t *hasher;
/** /**
* From RFC2548 (encryption): * From RFC2548 (encryption):
@@ -384,6 +381,12 @@ static chunk_t decrypt_mppe_key(private_radius_client_t *this, u_int16_t salt,
return chunk_empty; return chunk_empty;
} }
hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
if (!hasher)
{
return chunk_empty;
}
A = chunk_create((u_char*)&salt, sizeof(salt)); A = chunk_create((u_char*)&salt, sizeof(salt));
R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5); R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
P = chunk_alloca(C.len); P = chunk_alloca(C.len);
@@ -395,8 +398,8 @@ static chunk_t decrypt_mppe_key(private_radius_client_t *this, u_int16_t salt,
while (c < C.ptr + C.len) while (c < C.ptr + C.len)
{ {
/* b(i) = MD5(S + c(i-1)) */ /* b(i) = MD5(S + c(i-1)) */
this->socket->hasher->get_hash(this->socket->hasher, secret, NULL); hasher->get_hash(hasher, secret, NULL);
this->socket->hasher->get_hash(this->socket->hasher, seed, p); hasher->get_hash(hasher, seed, p);
/* p(i) = b(i) xor c(1) */ /* p(i) = b(i) xor c(1) */
memxor(p, c, HASH_SIZE_MD5); memxor(p, c, HASH_SIZE_MD5);
@@ -406,6 +409,7 @@ static chunk_t decrypt_mppe_key(private_radius_client_t *this, u_int16_t salt,
c += HASH_SIZE_MD5; c += HASH_SIZE_MD5;
p += HASH_SIZE_MD5; p += HASH_SIZE_MD5;
} }
hasher->destroy(hasher);
/* remove truncation, first byte is key length */ /* remove truncation, first byte is key length */
if (*P.ptr >= P.len) if (*P.ptr >= P.len)
@@ -469,7 +473,6 @@ static chunk_t decrypt_msk(private_radius_client_t *this,
*/ */
static void destroy(private_radius_client_t *this) static void destroy(private_radius_client_t *this)
{ {
put_socket(this->socket);
free(this->state.ptr); free(this->state.ptr);
free(this); free(this);
} }
@@ -485,7 +488,6 @@ radius_client_t *radius_client_create()
this->public.decrypt_msk = (chunk_t(*)(radius_client_t*, radius_message_t *, radius_message_t *))decrypt_msk; this->public.decrypt_msk = (chunk_t(*)(radius_client_t*, radius_message_t *, radius_message_t *))decrypt_msk;
this->public.destroy = (void(*)(radius_client_t*))destroy; this->public.destroy = (void(*)(radius_client_t*))destroy;
this->socket = get_socket();
this->state = chunk_empty; this->state = chunk_empty;
return &this->public; return &this->public;
@@ -31,10 +31,8 @@ typedef struct radius_client_t radius_client_t;
* RADIUS client functionality. * RADIUS client functionality.
* *
* To communicate with a RADIUS server, create a client and send messages over * To communicate with a RADIUS server, create a client and send messages over
* it. All instances share a fixed size pool of sockets. During construction, * it. All instances share a fixed size pool of sockets. The client reserves
* one sockets gets reserved for the client, so each client uses a different * a socket during request() and releases it afterwards.
* but fixed port during its lifetime. On destruction, the socket is restored
* to the pool.
*/ */
struct radius_client_t { struct radius_client_t {