Moved TLS stack to its own library
[strongswan.git] / src / libcharon / plugins / eap_radius / radius_server.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "radius_server.h"
17
18 #include <threading/mutex.h>
19 #include <threading/condvar.h>
20 #include <utils/linked_list.h>
21
22 typedef struct private_radius_server_t private_radius_server_t;
23
24 /**
25 * Private data of an radius_server_t object.
26 */
27 struct private_radius_server_t {
28
29 /**
30 * Public radius_server_t interface.
31 */
32 radius_server_t public;
33
34 /**
35 * RADIUS server address
36 */
37 host_t *host;
38
39 /**
40 * list of radius sockets, as radius_socket_t
41 */
42 linked_list_t *sockets;
43
44 /**
45 * Total number of sockets, in list + currently in use
46 */
47 int socket_count;
48
49 /**
50 * mutex to lock sockets list
51 */
52 mutex_t *mutex;
53
54 /**
55 * condvar to wait for sockets
56 */
57 condvar_t *condvar;
58
59 /**
60 * RADIUS secret
61 */
62 chunk_t secret;
63
64 /**
65 * NAS-Identifier
66 */
67 chunk_t nas_identifier;
68
69 /**
70 * Preference boost for this server
71 */
72 int preference;
73
74 /**
75 * Is the server currently reachable
76 */
77 bool reachable;
78
79 /**
80 * Retry counter for unreachable servers
81 */
82 int retry;
83 };
84
85 METHOD(radius_server_t, get_socket, radius_socket_t*,
86 private_radius_server_t *this)
87 {
88 radius_socket_t *skt;
89
90 this->mutex->lock(this->mutex);
91 while (this->sockets->remove_first(this->sockets, (void**)&skt) != SUCCESS)
92 {
93 this->condvar->wait(this->condvar, this->mutex);
94 }
95 this->mutex->unlock(this->mutex);
96 return skt;
97 }
98
99 METHOD(radius_server_t, put_socket, void,
100 private_radius_server_t *this, radius_socket_t *skt, bool result)
101 {
102 this->mutex->lock(this->mutex);
103 this->sockets->insert_last(this->sockets, skt);
104 this->mutex->unlock(this->mutex);
105 this->condvar->signal(this->condvar);
106 this->reachable = result;
107 }
108
109 METHOD(radius_server_t, get_nas_identifier, chunk_t,
110 private_radius_server_t *this)
111 {
112 return this->nas_identifier;
113 }
114
115 METHOD(radius_server_t, get_preference, int,
116 private_radius_server_t *this)
117 {
118 int pref;
119
120 if (this->socket_count == 0)
121 { /* don't have sockets, huh? */
122 return -1;
123 }
124 /* calculate preference between 0-100 + boost */
125 pref = this->preference;
126 pref += this->sockets->get_count(this->sockets) * 100 / this->socket_count;
127 if (this->reachable)
128 { /* reachable server get a boost: pref = 110-210 + boost */
129 return pref + 110;
130 }
131 /* Not reachable. Increase preference randomly to let it retry from
132 * time to time, especially if other servers have high load. */
133 this->retry++;
134 if (this->retry % 128 == 0)
135 { /* every 64th request gets 210, same as unloaded reachable */
136 return pref + 110;
137 }
138 if (this->retry % 32 == 0)
139 { /* every 32th request gets 190, wins against average loaded */
140 return pref + 90;
141 }
142 if (this->retry % 8 == 0)
143 { /* every 8th request gets 110, same as server under load */
144 return pref + 10;
145 }
146 /* other get ~100, less than fully loaded */
147 return pref;
148 }
149
150 METHOD(radius_server_t, get_address, host_t*,
151 private_radius_server_t *this)
152 {
153 return this->host;
154 }
155
156 METHOD(radius_server_t, destroy, void,
157 private_radius_server_t *this)
158 {
159 DESTROY_IF(this->host);
160 this->mutex->destroy(this->mutex);
161 this->condvar->destroy(this->condvar);
162 this->sockets->destroy_offset(this->sockets,
163 offsetof(radius_socket_t, destroy));
164 free(this);
165 }
166
167 /**
168 * See header
169 */
170 radius_server_t *radius_server_create(char *server, u_int16_t port,
171 char *nas_identifier, char *secret, int sockets, int preference)
172 {
173 private_radius_server_t *this;
174 radius_socket_t *socket;
175
176 INIT(this,
177 .public = {
178 .get_socket = _get_socket,
179 .put_socket = _put_socket,
180 .get_nas_identifier = _get_nas_identifier,
181 .get_preference = _get_preference,
182 .get_address = _get_address,
183 .destroy = _destroy,
184 },
185 .reachable = TRUE,
186 .nas_identifier = chunk_create(nas_identifier, strlen(nas_identifier)),
187 .socket_count = sockets,
188 .sockets = linked_list_create(),
189 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
190 .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
191 .host = host_create_from_dns(server, 0, port),
192 .preference = preference,
193 );
194
195 if (!this->host)
196 {
197 destroy(this);
198 return NULL;
199 }
200 while (sockets--)
201 {
202 socket = radius_socket_create(this->host,
203 chunk_create(secret, strlen(secret)));
204 if (!socket)
205 {
206 destroy(this);
207 return NULL;
208 }
209 this->sockets->insert_last(this->sockets, socket);
210 }
211 return &this->public;
212 }