2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
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>.
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
16 #include "radius_config.h"
18 #include <threading/mutex.h>
19 #include <threading/condvar.h>
20 #include <utils/linked_list.h>
22 typedef struct private_radius_config_t private_radius_config_t
;
25 * Private data of an radius_config_t object.
27 struct private_radius_config_t
{
30 * Public radius_config_t interface.
32 radius_config_t
public;
35 * list of radius sockets, as radius_socket_t
37 linked_list_t
*sockets
;
40 * Total number of sockets, in list + currently in use
45 * mutex to lock sockets list
50 * condvar to wait for sockets
62 chunk_t nas_identifier
;
65 * Preference boost for this server
70 * Is the server currently reachable
75 * Retry counter for unreachable servers
85 METHOD(radius_config_t
, get_socket
, radius_socket_t
*,
86 private_radius_config_t
*this)
90 this->mutex
->lock(this->mutex
);
91 while (this->sockets
->remove_first(this->sockets
, (void**)&skt
) != SUCCESS
)
93 this->condvar
->wait(this->condvar
, this->mutex
);
95 this->mutex
->unlock(this->mutex
);
99 METHOD(radius_config_t
, put_socket
, void,
100 private_radius_config_t
*this, radius_socket_t
*skt
, bool result
)
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
;
109 METHOD(radius_config_t
, get_nas_identifier
, chunk_t
,
110 private_radius_config_t
*this)
112 return this->nas_identifier
;
115 METHOD(radius_config_t
, get_preference
, int,
116 private_radius_config_t
*this)
120 if (this->socket_count
== 0)
121 { /* don't have sockets, huh? */
124 /* calculate preference between 0-100 + boost */
125 pref
= this->preference
;
126 pref
+= this->sockets
->get_count(this->sockets
) * 100 / this->socket_count
;
128 { /* reachable server get a boost: pref = 110-210 + boost */
131 /* Not reachable. Increase preference randomly to let it retry from
132 * time to time, especially if other servers have high load. */
134 if (this->retry
% 128 == 0)
135 { /* every 64th request gets 210, same as unloaded reachable */
138 if (this->retry
% 32 == 0)
139 { /* every 32th request gets 190, wins against average loaded */
142 if (this->retry
% 8 == 0)
143 { /* every 8th request gets 110, same as server under load */
146 /* other get ~100, less than fully loaded */
150 METHOD(radius_config_t
, get_name
, char*,
151 private_radius_config_t
*this)
156 METHOD(radius_config_t
, get_ref
, radius_config_t
*,
157 private_radius_config_t
*this)
160 return &this->public;
164 METHOD(radius_config_t
, destroy
, void,
165 private_radius_config_t
*this)
167 if (ref_put(&this->ref
))
169 this->mutex
->destroy(this->mutex
);
170 this->condvar
->destroy(this->condvar
);
171 this->sockets
->destroy_offset(this->sockets
,
172 offsetof(radius_socket_t
, destroy
));
180 radius_config_t
*radius_config_create(char *name
, char *address
,
181 u_int16_t auth_port
, u_int16_t acct_port
,
182 char *nas_identifier
, char *secret
,
183 int sockets
, int preference
)
185 private_radius_config_t
*this;
186 radius_socket_t
*socket
;
190 .get_socket
= _get_socket
,
191 .put_socket
= _put_socket
,
192 .get_nas_identifier
= _get_nas_identifier
,
193 .get_preference
= _get_preference
,
194 .get_name
= _get_name
,
199 .nas_identifier
= chunk_create(nas_identifier
, strlen(nas_identifier
)),
200 .socket_count
= sockets
,
201 .sockets
= linked_list_create(),
202 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
203 .condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
),
205 .preference
= preference
,
211 socket
= radius_socket_create(address
, auth_port
, acct_port
,
212 chunk_create(secret
, strlen(secret
)));
218 this->sockets
->insert_last(this->sockets
, socket
);
220 return &this->public;