/src/strongswan/src/libradius/radius_config.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2010 Martin Willi |
3 | | * |
4 | | * Copyright (C) secunet Security Networks AG |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU General Public License as published by the |
8 | | * Free Software Foundation; either version 2 of the License, or (at your |
9 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, but |
12 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | | * for more details. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * Copyright (C) 2015 Thom Troy |
19 | | * |
20 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
21 | | * of this software and associated documentation files (the "Software"), to deal |
22 | | * in the Software without restriction, including without limitation the rights |
23 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
24 | | * copies of the Software, and to permit persons to whom the Software is |
25 | | * furnished to do so, subject to the following conditions: |
26 | | * |
27 | | * The above copyright notice and this permission notice shall be included in |
28 | | * all copies or substantial portions of the Software. |
29 | | * |
30 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
31 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
32 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
33 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
34 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
35 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
36 | | * THE SOFTWARE. |
37 | | */ |
38 | | |
39 | | #include "radius_config.h" |
40 | | |
41 | | #include <threading/mutex.h> |
42 | | #include <threading/condvar.h> |
43 | | #include <collections/linked_list.h> |
44 | | |
45 | | typedef struct private_radius_config_t private_radius_config_t; |
46 | | |
47 | | /** |
48 | | * Private data of an radius_config_t object. |
49 | | */ |
50 | | struct private_radius_config_t { |
51 | | |
52 | | /** |
53 | | * Public radius_config_t interface. |
54 | | */ |
55 | | radius_config_t public; |
56 | | |
57 | | /** |
58 | | * list of radius sockets, as radius_socket_t |
59 | | */ |
60 | | linked_list_t *sockets; |
61 | | |
62 | | /** |
63 | | * Total number of sockets, in list + currently in use |
64 | | */ |
65 | | int socket_count; |
66 | | |
67 | | /** |
68 | | * mutex to lock sockets list |
69 | | */ |
70 | | mutex_t *mutex; |
71 | | |
72 | | /** |
73 | | * condvar to wait for sockets |
74 | | */ |
75 | | condvar_t *condvar; |
76 | | |
77 | | /** |
78 | | * Server name |
79 | | */ |
80 | | char *name; |
81 | | |
82 | | /** |
83 | | * NAS-Identifier |
84 | | */ |
85 | | chunk_t nas_identifier; |
86 | | |
87 | | /** |
88 | | * Preference boost for this server |
89 | | */ |
90 | | int preference; |
91 | | |
92 | | /** |
93 | | * Is the server currently reachable |
94 | | */ |
95 | | bool reachable; |
96 | | |
97 | | /** |
98 | | * Retry counter for unreachable servers |
99 | | */ |
100 | | int retry; |
101 | | |
102 | | /** |
103 | | * reference count |
104 | | */ |
105 | | refcount_t ref; |
106 | | }; |
107 | | |
108 | | METHOD(radius_config_t, get_socket, radius_socket_t*, |
109 | | private_radius_config_t *this) |
110 | 0 | { |
111 | 0 | radius_socket_t *skt; |
112 | |
|
113 | 0 | this->mutex->lock(this->mutex); |
114 | 0 | while (this->sockets->remove_first(this->sockets, (void**)&skt) != SUCCESS) |
115 | 0 | { |
116 | 0 | this->condvar->wait(this->condvar, this->mutex); |
117 | 0 | } |
118 | 0 | this->mutex->unlock(this->mutex); |
119 | 0 | return skt; |
120 | 0 | } |
121 | | |
122 | | METHOD(radius_config_t, put_socket, void, |
123 | | private_radius_config_t *this, radius_socket_t *skt, bool result) |
124 | 0 | { |
125 | 0 | this->mutex->lock(this->mutex); |
126 | 0 | this->sockets->insert_last(this->sockets, skt); |
127 | 0 | this->mutex->unlock(this->mutex); |
128 | 0 | this->condvar->signal(this->condvar); |
129 | 0 | this->reachable = result; |
130 | 0 | } |
131 | | |
132 | | METHOD(radius_config_t, get_nas_identifier, chunk_t, |
133 | | private_radius_config_t *this) |
134 | 0 | { |
135 | 0 | return this->nas_identifier; |
136 | 0 | } |
137 | | |
138 | | METHOD(radius_config_t, get_preference, int, |
139 | | private_radius_config_t *this) |
140 | 0 | { |
141 | 0 | int pref; |
142 | |
|
143 | 0 | if (this->socket_count == 0) |
144 | 0 | { /* don't have sockets, huh? */ |
145 | 0 | return -1; |
146 | 0 | } |
147 | | /* calculate preference between 0-100 + boost */ |
148 | 0 | pref = this->preference; |
149 | 0 | pref += this->sockets->get_count(this->sockets) * 100 / this->socket_count; |
150 | 0 | if (this->reachable) |
151 | 0 | { /* reachable server get a boost: pref = 110-210 + boost */ |
152 | 0 | return pref + 110; |
153 | 0 | } |
154 | | /* Not reachable. Increase preference randomly to let it retry from |
155 | | * time to time, especially if other servers have high load. */ |
156 | 0 | this->retry++; |
157 | 0 | if (this->retry % 128 == 0) |
158 | 0 | { /* every 64th request gets 210, same as unloaded reachable */ |
159 | 0 | return pref + 110; |
160 | 0 | } |
161 | 0 | if (this->retry % 32 == 0) |
162 | 0 | { /* every 32th request gets 190, wins against average loaded */ |
163 | 0 | return pref + 90; |
164 | 0 | } |
165 | 0 | if (this->retry % 8 == 0) |
166 | 0 | { /* every 8th request gets 110, same as server under load */ |
167 | 0 | return pref + 10; |
168 | 0 | } |
169 | | /* other get ~100, less than fully loaded */ |
170 | 0 | return pref; |
171 | 0 | } |
172 | | |
173 | | METHOD(radius_config_t, get_name, char*, |
174 | | private_radius_config_t *this) |
175 | 0 | { |
176 | 0 | return this->name; |
177 | 0 | } |
178 | | |
179 | | METHOD(radius_config_t, get_ref, radius_config_t*, |
180 | | private_radius_config_t *this) |
181 | 0 | { |
182 | 0 | ref_get(&this->ref); |
183 | 0 | return &this->public; |
184 | 0 | } |
185 | | |
186 | | |
187 | | METHOD(radius_config_t, destroy, void, |
188 | | private_radius_config_t *this) |
189 | 0 | { |
190 | 0 | if (ref_put(&this->ref)) |
191 | 0 | { |
192 | 0 | this->mutex->destroy(this->mutex); |
193 | 0 | this->condvar->destroy(this->condvar); |
194 | 0 | this->sockets->destroy_offset(this->sockets, |
195 | 0 | offsetof(radius_socket_t, destroy)); |
196 | 0 | free(this); |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | /** |
201 | | * See header |
202 | | */ |
203 | | radius_config_t *radius_config_create(char *name, char *address, char *source, |
204 | | uint16_t auth_port, uint16_t acct_port, |
205 | | char *nas_identifier, char *secret, |
206 | | int sockets, int preference, |
207 | | u_int tries, double timeout, double base) |
208 | 0 | { |
209 | 0 | private_radius_config_t *this; |
210 | 0 | radius_socket_t *socket; |
211 | |
|
212 | 0 | INIT(this, |
213 | 0 | .public = { |
214 | 0 | .get_socket = _get_socket, |
215 | 0 | .put_socket = _put_socket, |
216 | 0 | .get_nas_identifier = _get_nas_identifier, |
217 | 0 | .get_preference = _get_preference, |
218 | 0 | .get_name = _get_name, |
219 | 0 | .get_ref = _get_ref, |
220 | 0 | .destroy = _destroy, |
221 | 0 | }, |
222 | 0 | .reachable = TRUE, |
223 | 0 | .nas_identifier = chunk_create(nas_identifier, strlen(nas_identifier)), |
224 | 0 | .socket_count = sockets, |
225 | 0 | .sockets = linked_list_create(), |
226 | 0 | .mutex = mutex_create(MUTEX_TYPE_DEFAULT), |
227 | 0 | .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), |
228 | 0 | .name = name, |
229 | 0 | .preference = preference, |
230 | 0 | .ref = 1, |
231 | 0 | ); |
232 | |
|
233 | 0 | while (sockets--) |
234 | 0 | { |
235 | 0 | socket = radius_socket_create(address, source, auth_port, acct_port, |
236 | 0 | chunk_create(secret, strlen(secret)), |
237 | 0 | tries, timeout, base); |
238 | 0 | if (!socket) |
239 | 0 | { |
240 | 0 | destroy(this); |
241 | 0 | return NULL; |
242 | 0 | } |
243 | 0 | this->sockets->insert_last(this->sockets, socket); |
244 | 0 | } |
245 | 0 | return &this->public; |
246 | 0 | } |