#include <errno.h>
#include <unistd.h>
+#include <time.h>
#include <radius_message.h>
#include <radius_mppe.h>
#include <sa/eap/eap_method.h>
typedef struct private_tnc_pdp_t private_tnc_pdp_t;
-
+typedef struct client_entry_t client_entry_t;
/**
* Default RADIUS port, when not configured
*/
*/
#define MAX_PACKET 4096
+#define RADIUS_RETRANSMIT_TIMEOUT 30 /* seconds */
+
/**
* private data of tnc_pdp_t
*/
*/
chunk_t secret;
+ /**
+ * RADIUS clients
+ */
+ linked_list_t *clients;
+
/**
* MD5 hasher
*/
};
+/**
+ * Client entry helping to detect RADIUS packet retransmissions
+ */
+struct client_entry_t {
+
+ /**
+ * IP host address and port of client
+ */
+ host_t *host;
+
+ /**
+ * Time of last RADIUS Access-Request received from client
+ */
+ time_t last_time;
+
+ /**
+ * Identifier of last RADIUS Access-Request received from client
+ */
+ uint8_t last_id;
+};
+
+static void free_client_entry(client_entry_t *this)
+{
+ this->host->destroy(this->host);
+ free(this);
+}
+
/**
* Open IPv4 or IPv6 UDP socket
*/
{
radius_message_t *request;
char buffer[MAX_PACKET];
+ client_entry_t *client;
+ bool retransmission = FALSE, found = FALSE, stale;
+ enumerator_t *enumerator;
int bytes_read = 0;
host_t *source;
+ uint8_t id;
+ time_t now;
+
union {
struct sockaddr_in in4;
struct sockaddr_in6 in6;
} src;
+
struct iovec iov = {
.iov_base = buffer,
.iov_len = MAX_PACKET,
};
+
struct msghdr msg = {
.msg_name = &src,
.msg_namelen = sizeof(src),
if (request->verify(request, NULL, this->secret, this->hasher,
this->signer))
{
- process_eap(this, request, source);
+ id = request->get_identifier(request);
+ now = time(NULL);
+
+ enumerator = this->clients->create_enumerator(this->clients);
+ while (enumerator->enumerate(enumerator, &client))
+ {
+ stale = client->last_time < now - RADIUS_RETRANSMIT_TIMEOUT;
+
+ if (source->equals(source, client->host))
+ {
+ retransmission = !stale && client->last_id == id;
+ client->last_id = id;
+ client->last_time = now;
+ found = TRUE;
+ }
+ else if (stale)
+ {
+ this->clients->remove_at(this->clients, enumerator);
+ free_client_entry(client);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!found)
+ {
+ client = malloc_thing(client_entry_t);
+ client->host = source->clone(source);
+ client->last_id = id;
+ client->last_time = now;
+ this->clients->insert_last(this->clients, client);
+ }
+ if (retransmission)
+ {
+ DBG1(DBG_CFG, "ignoring RADIUS Access-Request 0x%02x, "
+ "already processing", id);
+ }
+ else
+ {
+ process_eap(this, request, source);
+ }
}
request->destroy(request);
}
lib->watcher->remove(lib->watcher, this->radius_ipv6);
close(this->radius_ipv6);
}
+ if (this->clients)
+ {
+ this->clients->destroy_function(this->clients, (void*)free_client_entry);
+ }
DESTROY_IF(this->server);
DESTROY_IF(this->signer);
DESTROY_IF(this->hasher);
this->radius_ipv4 = open_udp_socket(AF_INET, radius_port);
this->radius_ipv6 = open_udp_socket(AF_INET6, radius_port);
this->secret = chunk_from_str(secret);
+ this->clients = linked_list_create();
this->type = eap_type_from_string(eap_type_str);
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);