⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 radius_client.c

📁 Ralink RT61 SoftAP Driver source code. RT61:MiniPCI
💻 C
字号:
/*
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. See README and COPYING for
 * more details.

	Module Name:
	radius_client.c

	Revision History:
	Who 		When		  What
	--------	----------	  ----------------------------------------------
	Jan, Lee	Dec --2003	  modified

*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "rt61apd.h"
#include "radius.h"
#include "radius_client.h"
#include "eloop.h"

/* Defaults for RADIUS retransmit values (exponential backoff) */
#define RADIUS_CLIENT_FIRST_WAIT 1 /* seconds */
#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */
#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts
					  * before entry is removed from retransmit list */
#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit
					  * list (oldest will be removed, if this limit is exceeded) */
#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this
					  * many failed retry attempts */

static int
Radius_change_server(rtapd *rtapd, struct hostapd_radius_server *nserv,
			 struct hostapd_radius_server *oserv, int sock, int auth);

static void Radius_client_msg_free(struct radius_msg_list *req)
{
	Radius_msg_free(req->msg);
	free(req->msg);
	free(req);
}

int Radius_client_register(rtapd *apd, RadiusType msg_type,
			   RadiusRxResult (*handler)(rtapd *apd, struct radius_msg *msg, struct radius_msg *req,
							 u8 *shared_secret, size_t shared_secret_len, void *data), void *data)
{
	struct radius_rx_handler **handlers, *newh;
	size_t *num;

	handlers = &apd->radius->auth_handlers;
	num = &apd->radius->num_auth_handlers;

	newh = (struct radius_rx_handler *)
		realloc(*handlers, (*num + 1) * sizeof(struct radius_rx_handler));
	if (newh == NULL)
		return -1;

	newh[*num].handler = handler;
	newh[*num].data = data;
	(*num)++;
	*handlers = newh;

	return 0;
}

static int Radius_client_retransmit(rtapd *rtapd, struct radius_msg_list *entry, time_t now)
{
	int s;

	s = rtapd->radius->auth_serv_sock;
	/* retransmit; remove entry if too many attempts */
	entry->attempts++;

	if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0)
		perror("send[RADIUS]");

	entry->next_try = now + entry->next_wait;
	entry->next_wait *= 2;
	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES)
	{
		DBGPRINT(RT_DEBUG_ERROR,"Removing un-ACKed RADIUS message due to too many failed retransmit attempts\n");
		return 1;
	}

	return 0;
}

static void Radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
	rtapd *rtapd = eloop_ctx;
	time_t now, first;
	struct radius_msg_list *entry, *prev, *tmp;
	int auth_failover = 0;

	entry = rtapd->radius->msgs;
	if (!entry)
		return;

	time(&now);
	first = 0;

	prev = NULL;
	while (entry)
	{
		if (now >= entry->next_try && Radius_client_retransmit(rtapd, entry, now))
		{
			if (prev)
				prev->next = entry->next;
			else
				rtapd->radius->msgs = entry->next;

			tmp = entry;
			entry = entry->next;
			Radius_client_msg_free(tmp);
			rtapd->radius->num_msgs--;
			continue;
		}

		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER)
		{
			if (entry->msg_type == RADIUS_AUTH)
				auth_failover++;
		}

		if (first == 0 || entry->next_try < first)
			first = entry->next_try;

		prev = entry;
		entry = entry->next;
	}

	if (rtapd->radius->msgs)
	{
		if (first < now)
			first = now;
		eloop_register_timeout(first - now, 0, Radius_client_timer, rtapd, NULL);
	}

	if (auth_failover && rtapd->conf->num_auth_servers > 1)
	{
		struct hostapd_radius_server *next, *old;
		old = rtapd->conf->auth_server;

		next = old + 1;
		if (next > &(rtapd->conf->auth_servers[rtapd->conf->num_auth_servers - 1]))
			next = rtapd->conf->auth_servers;
		rtapd->conf->auth_server = next;
		Radius_change_server(rtapd, next, old, rtapd->radius->auth_serv_sock, 1);
	}
}

static void Radius_client_list_add(rtapd *rtapd, struct radius_msg *msg,
				   RadiusType msg_type, u8 *shared_secret, size_t shared_secret_len)
{
	struct radius_msg_list *entry, *prev;

	if (eloop_terminated())
	{
		/* No point in adding entries to retransmit queue since event
		 * loop has already been terminated. */
		DBGPRINT(RT_DEBUG_TRACE,"eloop_terminate \n");
		Radius_msg_free(msg);
		free(msg);
		return;
	}

	entry = malloc(sizeof(*entry));
	if (entry == NULL)
	{
		DBGPRINT(RT_DEBUG_TRACE,"Failed to add RADIUS packet into retransmit list\n");
		Radius_msg_free(msg);
		free(msg);
		return;
	}

	memset(entry, 0, sizeof(*entry));
	entry->msg = msg;
	entry->msg_type = msg_type;
	entry->shared_secret = shared_secret;
	entry->shared_secret_len = shared_secret_len;
	time(&entry->first_try);
	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
	entry->attempts = 1;
	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;

	if (!rtapd->radius->msgs)
		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, Radius_client_timer, rtapd, NULL);

	entry->next = rtapd->radius->msgs;
	rtapd->radius->msgs = entry;

	if (rtapd->radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES)
	{
		DBGPRINT(RT_DEBUG_TRACE,"Removing the oldest un-ACKed RADIUS packet due to retransmit list limits.\n");
		prev = NULL;
		while (entry->next)
		{
			prev = entry;
			entry = entry->next;
		}
		if (prev)
		{
			prev->next = NULL;
			Radius_client_msg_free(entry);
		}
	} else
		rtapd->radius->num_msgs++;
}

int Radius_client_send(rtapd *rtapd, struct radius_msg *msg, RadiusType msg_type)
{
	u8 *shared_secret;
	size_t shared_secret_len;
	char *name;
	int s, res;

	shared_secret = rtapd->conf->auth_server->shared_secret;
	shared_secret_len = rtapd->conf->auth_server->shared_secret_len;
	Radius_msg_finish(msg, shared_secret, shared_secret_len);
	name = "authentication";
	s = rtapd->radius->auth_serv_sock;

	res = send(s, msg->buf, msg->buf_used, 0);
	if (res < 0)
		perror("send[RADIUS]");

	Radius_client_list_add(rtapd, msg, msg_type, shared_secret, shared_secret_len);

	return res;
}

static void Radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
	rtapd *rtapd = eloop_ctx;
	RadiusType msg_type = (RadiusType) sock_ctx;
	int len, i,len_80211hdr=24;
	unsigned char buf[3000];
	struct radius_msg *msg;
	struct radius_rx_handler *handlers;
	size_t num_handlers;
	struct radius_msg_list *req, *prev_req;

	DBGPRINT(RT_DEBUG_INFO,"RADIUS_CLIENT_RECEIVE : msg_type= %d \n", msg_type);
	len = recv(sock, buf, sizeof(buf), 0);
	if (len < 0)
	{
		perror("recv[RADIUS]");
		return;
	}
	if (len == sizeof(buf))
	{
		DBGPRINT(RT_DEBUG_ERROR,"Possibly too long UDP frame for our buffer - dropping it\n");
		return;
	}
	
	if(buf[0]!=0xff)
	{
		int i;
		DBGPRINT(RT_DEBUG_INFO,"  r_dump %d bytes:",len);
		for (i = 0; i < len_80211hdr; i++)
			DBGPRINT(RT_DEBUG_INFO," %02x", buf[i]);
		DBGPRINT(RT_DEBUG_INFO,"\n");
	}

	msg = Radius_msg_parse(buf, len);
	if (msg == NULL)
	{
		DBGPRINT(RT_DEBUG_ERROR,"Parsing incoming RADIUS frame failed\n");
		return;
	}

	handlers = rtapd->radius->auth_handlers;
	num_handlers = rtapd->radius->num_auth_handlers;

	prev_req = NULL;
	req = rtapd->radius->msgs;
	while (req)
	{
		/* TODO: also match by src addr:port of the packet when using
		 * alternative RADIUS servers (?) */
		if (req->msg_type == msg_type && req->msg->hdr->identifier == msg->hdr->identifier)
			break;

		prev_req = req;
		req = req->next;
	}

	if (req == NULL)
	{
		goto fail;
	}

	/* Remove ACKed RADIUS packet from retransmit list */
	if (prev_req)
		prev_req->next = req->next;
	else
		rtapd->radius->msgs = req->next;
	rtapd->radius->num_msgs--;

	for (i = 0; i < num_handlers; i++)
	{
		RadiusRxResult res;
		res = handlers[i].handler(rtapd, msg, req->msg, req->shared_secret, req->shared_secret_len, handlers[i].data);
		switch (res) 
		{
			case RADIUS_RX_PROCESSED:
			Radius_msg_free(msg);
			free(msg);
			/* continue */
			case RADIUS_RX_QUEUED:
			Radius_client_msg_free(req);
			return;
			case RADIUS_RX_UNKNOWN:
			/* continue with next handler */
			break;
		}
	}

	DBGPRINT(RT_DEBUG_ERROR,"No RADIUS RX handler found (type=%d code=%d id=%d) - dropping "
		   "packet\n", msg_type, msg->hdr->code, msg->hdr->identifier);
	Radius_client_msg_free(req);

 fail:
	Radius_msg_free(msg);
	free(msg);
}

u8 Radius_client_get_id(rtapd *rtapd)
{
	struct radius_msg_list *entry, *prev, *remove;
	u8 id = rtapd->radius->next_radius_identifier++;

	/* remove entries with matching id from retransmit list to avoid
	 * using new reply from the RADIUS server with an old request */
	entry = rtapd->radius->msgs;
	prev = NULL;
	while (entry)
	{
		if (entry->msg->hdr->identifier == id)
		{
			if (prev)
				prev->next = entry->next;
			else
				rtapd->radius->msgs = entry->next;
			remove = entry;
		} else
			remove = NULL;
		prev = entry;
		entry = entry->next;

		if (remove)
			Radius_client_msg_free(remove);
	}

	return id;
}

void Radius_client_flush(rtapd *rtapd)
{
	struct radius_msg_list *entry, *prev;

	if (!rtapd->radius)
		return;

	eloop_cancel_timeout(Radius_client_timer, rtapd, NULL);

	entry = rtapd->radius->msgs;
	rtapd->radius->msgs = NULL;
	rtapd->radius->num_msgs = 0;
	while (entry)
	{
		prev = entry;
		entry = entry->next;
		Radius_client_msg_free(prev);
	}
}

static int
Radius_change_server(rtapd *rtapd, struct hostapd_radius_server *nserv,
			 struct hostapd_radius_server *oserv, int sock, int auth)
{
	struct sockaddr_in serv;
	if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
		memcmp(nserv->shared_secret, oserv->shared_secret, nserv->shared_secret_len) != 0)
	{
		/* Pending RADIUS packets used different shared
		 * secret, so they would need to be modified. Could
		 * update all message authenticators and
		 * User-Passwords, etc. and retry with new server. For
		 * now, just drop all pending packets. */
		Radius_client_flush(rtapd);
	} 
	else
	{
		/* Reset retry counters for the new server */
		struct radius_msg_list *entry;
		entry = rtapd->radius->msgs;
		while (entry)
		{
			entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
			entry->attempts = 0;
			entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
			entry = entry->next;
		}
		if (rtapd->radius->msgs)
		{
			eloop_cancel_timeout(Radius_client_timer, rtapd, NULL);
			eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, Radius_client_timer, rtapd, NULL);
		}
	}
	// bind before connect to assign local port
/*Comment by rory
	memset(&serv, 0, sizeof(serv));
	port = 2048;
	serv.sin_family = AF_INET;
	//serv.sin_addr.s_addr = inet_addr("192.168.1.138");
	serv.sin_addr.s_addr = rtapd->conf->own_ip_addr.s_addr;
	serv.sin_port = htons(port);
	if (bind(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0)
	{
		perror("bind");
		return -1;
	}*/
	memset(&serv, 0, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_addr.s_addr = nserv->addr.s_addr;
	serv.sin_port = htons(nserv->port);
	if (connect(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0)
	{
		perror("connect[radius]");
		return -1;
	}
	return 0;
}

static void Radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
{
	rtapd *rtapd = eloop_ctx;
	struct hostapd_radius_server *oserv;

	if (rtapd->radius->auth_serv_sock >= 0 && rtapd->conf->auth_servers &&
		rtapd->conf->auth_server != rtapd->conf->auth_servers)
	{
		oserv = rtapd->conf->auth_server;
		rtapd->conf->auth_server = rtapd->conf->auth_servers;
		Radius_change_server(rtapd, rtapd->conf->auth_server, oserv, rtapd->radius->auth_serv_sock, 1);
	}

	if (rtapd->conf->radius_retry_primary_interval)
		eloop_register_timeout(rtapd->conf->radius_retry_primary_interval, 0, Radius_retry_primary_timer, rtapd, NULL);
}

static int Radius_client_init_auth(rtapd *rtapd)
{
	rtapd->radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (rtapd->radius->auth_serv_sock < 0)
	{
		perror("socket[PF_INET,SOCK_DGRAM]");
		return -1;
	}

	Radius_change_server(rtapd, rtapd->conf->auth_server, NULL, rtapd->radius->auth_serv_sock, 1);

	if (eloop_register_read_sock(rtapd->radius->auth_serv_sock, Radius_client_receive, rtapd, (void *) RADIUS_AUTH))
	{
		DBGPRINT(RT_DEBUG_ERROR,"Could not register read socket for authentication server\n");
		return -1;
	}

	return 0;
}

int Radius_client_init(rtapd *rtapd)
{
	if (rtapd->radius == NULL)
	{		 
		rtapd->radius = malloc(sizeof(struct radius_client_data));
		memset(rtapd->radius, 0, sizeof(struct radius_client_data));
		rtapd->radius->auth_serv_sock = -1;
	}
	if (rtapd->radius == NULL)
		return -1;

	if( rtapd->radius->auth_serv_sock < 0)
	{
		if (rtapd->conf->auth_server && Radius_client_init_auth(rtapd))
			return -1;
		if (rtapd->conf->radius_retry_primary_interval)
			eloop_register_timeout(rtapd->conf->radius_retry_primary_interval, 0, Radius_retry_primary_timer, rtapd, NULL);
	}
	return 0;
}

void Radius_client_deinit(rtapd *rtapd)
{
	if (!rtapd->radius)
		return;

	eloop_cancel_timeout(Radius_retry_primary_timer, rtapd, NULL);

	Radius_client_flush(rtapd);
	free(rtapd->radius->auth_handlers);
	free(rtapd->radius);
	rtapd->radius = NULL;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -