comparison extensions/app_radgw/rgw_clients.c @ 544:a0e3af6f94fb

Improve the RADIUS duplicates management. Default cache is set to 60 seconds.
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 14 Sep 2010 16:04:59 +0900
parents 6994e9a3c528
children b0f9b0e1b564
comparison
equal deleted inserted replaced
543:40141acabee7 544:a0e3af6f94fb
38 /* Probably some changes are needed to support RADIUS Proxies */ 38 /* Probably some changes are needed to support RADIUS Proxies */
39 39
40 #include "rgw.h" 40 #include "rgw.h"
41 41
42 #define REVERSE_DNS_SIZE_MAX 512 /* length of our buffer for reverse DNS */ 42 #define REVERSE_DNS_SIZE_MAX 512 /* length of our buffer for reverse DNS */
43 #define DUPLICATE_CHECK_LIFETIME 60 /* number of seconds that the received RADIUS records are kept for duplicate checking . TODO: make it configurable if needed */
43 44
44 /* Ordered lists of clients. The order relationship is a memcmp on the address zone. 45 /* Ordered lists of clients. The order relationship is a memcmp on the address zone.
45 For same addresses, the port is compared. 46 For same addresses, the port is compared.
46 The same address cannot be added twice, once with a 0-port and once with another port value. 47 The same address cannot be added twice, once with a 0-port and once with another port value.
47 */ 48 */
48 static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip); 49 static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip);
49 static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6); 50 static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6);
50 51
51 /* Mutex to protect the previous lists */ 52 /* Lock to protect the previous lists. We use a rwlock because this list is mostly static, to allow parallel reading */
52 static pthread_mutex_t cli_mtx = PTHREAD_MUTEX_INITIALIZER; 53 static pthread_rwlock_t cli_rwl = PTHREAD_RWLOCK_INITIALIZER;
54
55 /* Structure describing one received RADIUS message, for duplicate checks purpose. */
56 struct req_info {
57 uint16_t port; /* UDP source port of the request */
58 uint8_t id; /* The identifier in the request header */
59 uint8_t auth[16]; /* Request authenticator, since some RADIUS clients do not implement the id mechanism properly. */
60 struct radius_msg *ans; /* The replied answer if any, in case the previous answer got lost. */
61
62 int nbdup; /* Number of times this request was received as a duplicate */
63 struct fd_list by_id; /* The list of requests ordered by their id, port, and auth */
64 time_t received; /* When was the last duplicate received? */
65 struct fd_list by_time; /* The list of requests ordered by the 'received' value . */
66 };
67
68 static pthread_t dbt_expire = (pthread_t)NULL; /* The thread that will remove old requests information from all clients (one thread for all) */
53 69
54 /* Structure describing one client */ 70 /* Structure describing one client */
55 struct rgw_client { 71 struct rgw_client {
56 /* Link information in global list */ 72 /* Link information in global list (cli_ip or cli_ip6) */
57 struct fd_list chain; 73 struct fd_list chain;
58 74
59 /* Reference count */ 75 /* Reference count */
60 int refcount; 76 int refcount;
61 77
79 struct { 95 struct {
80 unsigned char * data; 96 unsigned char * data;
81 size_t len; 97 size_t len;
82 } key; 98 } key;
83 99
84 /* information of previous msg received, for duplicate checks -- we keep the last DUPLICATE_MESSAGES_BUFFER messages on each port. */ 100 /* information of previous msg received, for duplicate checks. */
85 #define DUPLICATE_MESSAGES_BUFFER 200 /* This should actually be replaced with a time-based dynamic list! TODO... */
86 struct { 101 struct {
87 int cnt; /* Counts the number of (different) requests we received */ 102 pthread_mutex_t dupl_lock; /* The mutex protecting the following lists */
88 struct { 103 struct fd_list dupl_by_id; /* The list of req_info structures ordered by their id, port, and auth */
89 uint16_t port; /* The source UDP port of the request */ 104 struct fd_list dupl_by_time; /* The list of req_info structures ordered by their time (approximative) */
90 uint8_t id; /* The identifier in the request */ 105 } dupl_info[2]; /*[0] for auth, [1] for acct. */
91 uint8_t auth[16]; /* The request authenticator, because some NAS are not using identifier properly. */
92 struct radius_msg * ans; /* When the answer has been sent already, keep it so we can send it back */
93 int nbdup; /* count the number of duplicate RADIUS requests we received on this message */
94 } msg_info[DUPLICATE_MESSAGES_BUFFER];
95 } duplicates_info[2]; /*[0] for auth, [1] for acct. */
96 }; 106 };
97 107
98 108
99 109 /* Create a new req_info structure and initialize its data from a RADIUS request message */
100 /* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */ 110 static struct req_info * dupl_new_req_info(struct rgw_radius_msg_meta *msg) {
111 struct req_info * ret = NULL;
112 CHECK_MALLOC_DO( ret = malloc(sizeof(struct req_info)), return NULL );
113 memset(ret, 0, sizeof(struct req_info));
114 ret->port = msg->port;
115 ret->id = msg->radius.hdr->identifier;
116 memcpy(&ret->auth[0], &msg->radius.hdr->authenticator[0], 16);
117 fd_list_init(&ret->by_id, ret);
118 fd_list_init(&ret->by_time, ret);
119 ret->received = time(NULL);
120 return ret;
121 }
122
123 /* Destroy a req_info structure, after it has been unlinked */
124 static void dupl_free_req_info(struct req_info * r) {
125 CHECK_PARAMS_DO( r && FD_IS_LIST_EMPTY(&r->by_id) && FD_IS_LIST_EMPTY(&r->by_time), return );
126 if (r->ans) {
127 /* Free this RADIUS message */
128 radius_msg_free(r->ans);
129 free(r->ans);
130 }
131
132 /* Use r->nbdup for some purpose? */
133
134 free(r);
135 }
136
137 /* The core of the purge thread */
138 static int dupl_purge_list(struct fd_list * clients) {
139 struct fd_list *li = NULL;
140 for (li = clients->next; li != clients; li = li->next) {
141 struct rgw_client * client = (struct rgw_client *)li;
142 int p;
143 for (p=0; p<=1; p++) {
144 /* Lock this list */
145 time_t now;
146 CHECK_POSIX( pthread_mutex_lock(&client->dupl_info[p].dupl_lock) );
147
148 now = time(NULL);
149
150 while (!FD_IS_LIST_EMPTY(&client->dupl_info[p].dupl_by_time)) {
151 /* Check the first item in the list */
152 struct req_info * r = (struct req_info *)(client->dupl_info[p].dupl_by_time.next->o);
153
154 if (now - r->received > DUPLICATE_CHECK_LIFETIME) {
155 /* Remove this record */
156 fd_list_unlink(&r->by_time);
157 fd_list_unlink(&r->by_id);
158 dupl_free_req_info(r);
159 } else {
160 /* We are done for this list */
161 break;
162 }
163 }
164
165 CHECK_POSIX( pthread_mutex_unlock(&client->dupl_info[p].dupl_lock) );
166 }
167 }
168 return 0;
169 }
170
171 /* Thread that purges old RADIUS requests */
172 static void * dupl_th(void * arg) {
173 /* Set the thread name */
174 fd_log_threadname ( "app_radgw:duplicate_purge" );
175
176 /* The thread will be canceled */
177 while (1) {
178
179 /* We don't use a cond var, we simply wake up every 5 seconds. If the size of the duplicate cache is critical, it might be changed */
180 sleep(5);
181
182 /* When we wake up, we will check all clients duplicate lists one by one */
183 CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), break );
184
185 CHECK_FCT_DO( dupl_purge_list(&cli_ip), break );
186 CHECK_FCT_DO( dupl_purge_list(&cli_ip6), break );
187
188 CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), break );
189
190 /* Loop */
191 }
192
193 /* If we reach this part, some fatal error was encountered */
194 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
195 TRACE_DEBUG(FULL, "Thread terminated");
196 return NULL;
197 }
198
199
200 /* create a new rgw_client. the arguments are MOVED into the structure (to limit malloc & free calls). */
101 static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) 201 static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
102 { 202 {
103 struct rgw_client *tmp = NULL; 203 struct rgw_client *tmp = NULL;
104 char buf[255]; 204 char buf[255];
105 int ret; 205 int ret, i;
106 int loc = 0; 206 int loc = 0;
107 207
108 /* Check if the IP address is local */ 208 /* Check if the IP address is local */
109 if ( ( ((*ip_port)->sa_family == AF_INET ) && ( IN_IS_ADDR_LOOPBACK( &((struct sockaddr_in *)(*ip_port))->sin_addr ) ) ) 209 if ( ( ((*ip_port)->sa_family == AF_INET ) && ( IN_IS_ADDR_LOOPBACK( &((struct sockaddr_in *)(*ip_port))->sin_addr ) ) )
110 ||( ((*ip_port)->sa_family == AF_INET6) && ( IN6_IS_ADDR_LOOPBACK( &((struct sockaddr_in6 *)(*ip_port))->sin6_addr) ) )) { 210 ||( ((*ip_port)->sa_family == AF_INET6) && ( IN6_IS_ADDR_LOOPBACK( &((struct sockaddr_in6 *)(*ip_port))->sin6_addr) ) )) {
123 /* Create the new object */ 223 /* Create the new object */
124 CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) ); 224 CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) );
125 memset(tmp, 0, sizeof(struct rgw_client)); 225 memset(tmp, 0, sizeof(struct rgw_client));
126 fd_list_init(&tmp->chain, NULL); 226 fd_list_init(&tmp->chain, NULL);
127 227
228 /* Initialize the duplicate list info */
229 for (i=0; i<=1; i++) {
230 CHECK_POSIX( pthread_mutex_init(&tmp->dupl_info[0].dupl_lock, NULL) );
231 fd_list_init(&tmp->dupl_info[0].dupl_by_id, NULL);
232 fd_list_init(&tmp->dupl_info[0].dupl_by_time, NULL);
233 }
128 tmp->type = type; 234 tmp->type = type;
129 235
130 if (loc) { 236 if (loc) {
131 tmp->is_local = 1; 237 tmp->is_local = 1;
132 } else { 238 } else {
153 /* Done! */ 259 /* Done! */
154 *res = tmp; 260 *res = tmp;
155 return 0; 261 return 0;
156 } 262 }
157 263
158
159 /* Decrease refcount on a client; the lock must be held when this function is called. */ 264 /* Decrease refcount on a client; the lock must be held when this function is called. */
160 static void client_unlink(struct rgw_client * client) 265 static void client_unlink(struct rgw_client * client)
161 { 266 {
162 client->refcount -= 1; 267 client->refcount -= 1;
163 268
174 free(client->sa); 279 free(client->sa);
175 free(client->key.data); 280 free(client->key.data);
176 281
177 /* Free the duplicate info */ 282 /* Free the duplicate info */
178 for (idx=0; idx <= 1; idx++){ 283 for (idx=0; idx <= 1; idx++){
179 int i = 0; 284 CHECK_POSIX_DO( pthread_mutex_lock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
180 for (i = 0; i < DUPLICATE_MESSAGES_BUFFER; i++) { 285
181 if (client->duplicates_info[idx].msg_info[i].ans) { 286 while (!FD_IS_LIST_EMPTY(&client->dupl_info[idx].dupl_by_id)) {
182 /* Free this RADIUS message */ 287 struct req_info * r = (struct req_info *)(client->dupl_info[idx].dupl_by_id.next->o);
183 radius_msg_free(client->duplicates_info[idx].msg_info[i].ans); 288 fd_list_unlink( &r->by_id );
184 free(client->duplicates_info[idx].msg_info[i].ans); 289 fd_list_unlink( &r->by_time );
185 } 290 dupl_free_req_info(r);
186 } 291 }
292
293 CHECK_POSIX_DO( pthread_mutex_unlock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
294
187 } 295 }
188 296
189 free(client); 297 free(client);
190 } 298 }
191 } 299 }
219 } \ 327 } \
220 *res = (struct rgw_client *)(ref->prev); \ 328 *res = (struct rgw_client *)(ref->prev); \
221 return ENOENT; \ 329 return ENOENT; \
222 } 330 }
223 /* Function to look for an existing rgw_client, or the previous element. 331 /* Function to look for an existing rgw_client, or the previous element.
224 The cli_mtx must be held when calling this function. 332 The cli_rwl must be held for reading (at least) when calling this function.
225 Returns ENOENT if the matching client does not exist, and res points to the previous element in the list. 333 Returns ENOENT if the matching client does not exist, and res points to the previous element in the list.
226 Returns EEXIST if the matching client is found, and res points to this element. 334 Returns EEXIST if the matching client is found, and res points to this element.
227 Returns other error code on other error. */ 335 Returns other error code on other error. */
228 static int client_search(struct rgw_client ** res, struct sockaddr * ip_port ) 336 static int client_search(struct rgw_client ** res, struct sockaddr * ip_port )
229 { 337 {
267 375
268 TRACE_ENTRY("%p %p", ip_port, ref); 376 TRACE_ENTRY("%p %p", ip_port, ref);
269 377
270 CHECK_PARAMS(ip_port && ref); 378 CHECK_PARAMS(ip_port && ref);
271 379
272 CHECK_POSIX( pthread_mutex_lock(&cli_mtx) ); 380 CHECK_POSIX( pthread_rwlock_rdlock(&cli_rwl) );
273 381
274 ret = client_search(ref, ip_port); 382 ret = client_search(ref, ip_port);
275 if (ret == EEXIST) { 383 if (ret == EEXIST) {
276 (*ref)->refcount ++; 384 (*ref)->refcount ++;
277 ret = 0; 385 ret = 0;
278 } else { 386 } else {
279 *ref = NULL; 387 *ref = NULL;
280 } 388 }
281 389
282 CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) ); 390 CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
283 391
284 return ret; 392 return ret;
285 } 393 }
286 394
287 int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli) 395 int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli)
288 { 396 {
289 int p, i, dup = 0; 397 int p, dup = 0;
398 struct fd_list * li;
399 struct req_info * r;
290 400
291 TRACE_ENTRY("%p %p", msg, cli); 401 TRACE_ENTRY("%p %p", msg, cli);
292 402
293 CHECK_PARAMS( msg && cli ); 403 CHECK_PARAMS( msg && cli );
294 404
295 if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH) 405 if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH)
296 p = 0; 406 p = 0;
297 else 407 else
298 p = 1; 408 p = 1;
299 409
300 /* Check in the previous DUPLICATE_MESSAGES_BUFFER messages if we have received the same identifier / authenticator / port combination */ 410 CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
301 for (i = cli->duplicates_info[p].cnt - 1; i >= cli->duplicates_info[p].cnt - DUPLICATE_MESSAGES_BUFFER; i--) { 411
302 if ( (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].id == (*msg)->radius.hdr->identifier) 412 /* Search if we have this message in our list */
303 && (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].port == (*msg)->port) 413 for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
304 && !memcmp(&cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].auth[0], &(*msg)->radius.hdr->authenticator[0], 16)) { 414 int cmp = 0;
305 /* We already received this request */ 415 r = (struct req_info *)(li->o);
306 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].nbdup++; 416 if (r->id < (*msg)->radius.hdr->identifier)
307 dup = 1; 417 continue;
308 TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d).", 418 if (r->id > (*msg)->radius.hdr->identifier)
309 (*msg)->radius.hdr->identifier,
310 ntohs((*msg)->port),
311 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].nbdup);
312 if (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans) {
313 /* Resend the answer */
314 CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type,
315 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans->buf,
316 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans->buf_used,
317 cli->sa,
318 (*msg)->port), );
319 }
320 rgw_msg_free(msg);
321 break; 419 break;
322 } 420 if (r->port < (*msg)->port)
323 } 421 continue;
324 422 if (r->port > (*msg)->port)
325 /* If we did no already receive this request, save it for later */ 423 break;
326 if (!dup) { 424 cmp = memcmp(&r->auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
327 /* It's a new request, save its data */ 425 if (cmp < 0)
328 int i = cli->duplicates_info[p].cnt % DUPLICATE_MESSAGES_BUFFER; 426 continue;
329 427 if (cmp > 0);
330 cli->duplicates_info[p].msg_info[i].port = (*msg)->port; 428 break;
331 cli->duplicates_info[p].msg_info[i].id = (*msg)->radius.hdr->identifier; 429 dup = 1;
332 memcpy(&cli->duplicates_info[p].msg_info[i].auth[0], &(*msg)->radius.hdr->authenticator[0], 16); 430 break;
333 if (cli->duplicates_info[p].msg_info[i].ans) { 431 }
334 /* Free the old answer */ 432
335 radius_msg_free(cli->duplicates_info[p].msg_info[i].ans); 433 if (dup) {
336 free(cli->duplicates_info[p].msg_info[i].ans); 434 time_t now = time(NULL);
337 cli->duplicates_info[p].msg_info[i].ans = NULL; 435 r->nbdup += 1;
338 } 436 TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d, previously seen %d secs ago).",
339 cli->duplicates_info[p].msg_info[i].nbdup = 0; 437 r->id, ntohs(r->port), r->nbdup, now - r->received);
438
439 if (r->ans) {
440 /* Resend the answer */
441 CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, r->ans->buf, r->ans->buf_used, cli->sa, r->port), );
340 442
341 cli->duplicates_info[p].cnt += 1; 443 /* Should we delete 'r' so that a further duplicate will again be converted to Diameter? */
342 } 444 }
445
446 /* Update the timestamp */
447 r->received = now;
448 fd_list_unlink(&r->by_time);
449 fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* Move as last entry, since it is the most recent */
450
451 /* Delete the request message */
452 rgw_msg_free(msg);
453
454 } else {
455 /* The message was not a duplicate, we save it */
456 /* li currently points the the next entry in list_by_id */
457 CHECK_MALLOC_DO( r= dupl_new_req_info(*msg), { CHECK_POSIX_DO(pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ), ); return ENOMEM; } );
458 fd_list_insert_before(li, &r->by_id);
459 fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* it is the most recent */
460 }
461
462 CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
343 463
344 return 0; 464 return 0;
345 } 465 }
346 466
347 /* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */
348 /* Also update the client list of aliases if needed */
349 /* NOTE: This function does nothing if the client is a RADIUS Proxy... */
350 /* Check if the message has a valid authenticator, and update the meta-data accordingly */ 467 /* Check if the message has a valid authenticator, and update the meta-data accordingly */
351 int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth) 468 int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth)
352 { 469 {
353 unsigned char * key; 470 unsigned char * key;
354 size_t keylen; 471 size_t keylen;
387 { 504 {
388 TRACE_ENTRY(); 505 TRACE_ENTRY();
389 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); 506 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) );
390 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); 507 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) );
391 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) ); 508 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) );
509
510 /* Create the thread that will purge old RADIUS duplicates */
511 CHECK_POSIX( pthread_create( &dbt_expire, NULL, dupl_th, NULL) );
512
392 return 0; 513 return 0;
393 } 514 }
394 515
395 516
396 /* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter 517 /* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter
397 message with the appropriate routing information */ 518 message with the appropriate routing information */
519 /* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */
520 /* Also update the client list of aliases if needed */
398 int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam) 521 int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam)
399 { 522 {
400 int idx; 523 int idx;
401 int valid_nas_info = 0; 524 int valid_nas_info = 0;
402 struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; 525 struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
711 void rgw_clients_dispose(struct rgw_client ** ref) 834 void rgw_clients_dispose(struct rgw_client ** ref)
712 { 835 {
713 TRACE_ENTRY("%p", ref); 836 TRACE_ENTRY("%p", ref);
714 CHECK_PARAMS_DO(ref, return); 837 CHECK_PARAMS_DO(ref, return);
715 838
716 CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), ); 839 CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl), );
717 client_unlink(*ref); 840 client_unlink(*ref);
718 *ref = NULL; 841 *ref = NULL;
719 CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), ); 842 CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), );
720 } 843 }
721 844
722 int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type ) 845 int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
723 { 846 {
724 struct rgw_client * prev = NULL, *new = NULL; 847 struct rgw_client * prev = NULL, *new = NULL;
736 TRACE_DEBUG_sSA(FULL, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); 859 TRACE_DEBUG_sSA(FULL, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
737 TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" ); 860 TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" );
738 } 861 }
739 862
740 /* Lock the lists */ 863 /* Lock the lists */
741 CHECK_POSIX( pthread_mutex_lock(&cli_mtx) ); 864 CHECK_POSIX( pthread_rwlock_wrlock(&cli_rwl) );
742 865
743 /* Check if the same entry does not already exist */ 866 /* Check if the same entry does not already exist */
744 ret = client_search(&prev, ip_port ); 867 ret = client_search(&prev, ip_port );
745 if (ret == ENOENT) { 868 if (ret == ENOENT) {
746 /* No duplicate found, Ok to add */ 869 /* No duplicate found, Ok to add */
767 TRACE_DEBUG_sSA(NONE, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" ); 890 TRACE_DEBUG_sSA(NONE, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
768 TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" ); 891 TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" );
769 } 892 }
770 end: 893 end:
771 /* release the lists */ 894 /* release the lists */
772 CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) ); 895 CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
773 896
774 return ret; 897 return ret;
775 } 898 }
776 899
777 static void dump_cli_list(struct fd_list *senti) 900 static void dump_cli_list(struct fd_list *senti)
788 void rgw_clients_dump(void) 911 void rgw_clients_dump(void)
789 { 912 {
790 if ( ! TRACE_BOOL(FULL) ) 913 if ( ! TRACE_BOOL(FULL) )
791 return; 914 return;
792 915
793 CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ ); 916 CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), /* ignore error */ );
794 917
795 if (!FD_IS_LIST_EMPTY(&cli_ip)) 918 if (!FD_IS_LIST_EMPTY(&cli_ip))
796 fd_log_debug(" RADIUS IP clients list:\n"); 919 fd_log_debug(" RADIUS IP clients list:\n");
797 dump_cli_list(&cli_ip); 920 dump_cli_list(&cli_ip);
798 921
799 if (!FD_IS_LIST_EMPTY(&cli_ip6)) 922 if (!FD_IS_LIST_EMPTY(&cli_ip6))
800 fd_log_debug(" RADIUS IPv6 clients list:\n"); 923 fd_log_debug(" RADIUS IPv6 clients list:\n");
801 dump_cli_list(&cli_ip6); 924 dump_cli_list(&cli_ip6);
802 925
803 CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ ); 926 CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
804 } 927 }
805 928
806 void rgw_clients_fini(void) 929 void rgw_clients_fini(void)
807 { 930 {
808 struct fd_list * client; 931 struct fd_list * client;
809 932
810 TRACE_ENTRY(); 933 TRACE_ENTRY();
811 934
812 CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ ); 935 CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl), /* ignore error */ );
813 936
937 CHECK_FCT_DO( fd_thr_term(&dbt_expire), /* continue */ );
938
814 /* empty the lists */ 939 /* empty the lists */
815 while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) { 940 while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) {
816 client = cli_ip.next; 941 client = cli_ip.next;
817 fd_list_unlink(client); 942 fd_list_unlink(client);
818 client_unlink((struct rgw_client *)client); 943 client_unlink((struct rgw_client *)client);
821 client = cli_ip6.next; 946 client = cli_ip6.next;
822 fd_list_unlink(client); 947 fd_list_unlink(client);
823 client_unlink((struct rgw_client *)client); 948 client_unlink((struct rgw_client *)client);
824 } 949 }
825 950
826 CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ ); 951 CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
827 952
828 } 953 }
829 954
830 int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli) 955 int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli)
831 { 956 {
832 int p,i; 957 int p;
958 struct fd_list * li;
833 959
834 TRACE_ENTRY("%p %p %p", msg, req, cli); 960 TRACE_ENTRY("%p %p %p", msg, req, cli);
835 CHECK_PARAMS( msg && *msg && cli ); 961 CHECK_PARAMS( msg && *msg && cli );
836 962
837 if (!req) { 963 if (!req) {
853 rgw_msg_dump((struct rgw_radius_msg_meta *)*msg); 979 rgw_msg_dump((struct rgw_radius_msg_meta *)*msg);
854 980
855 /* Send the message */ 981 /* Send the message */
856 CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) ); 982 CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) );
857 983
858 /* update the duplicate cache in rgw_clients */ 984 /* update the duplicate cache */
859 if (req->serv_type == RGW_PLG_TYPE_AUTH) 985 if (req->serv_type == RGW_PLG_TYPE_AUTH)
860 p = 0; 986 p = 0;
861 else 987 else
862 p = 1; 988 p = 1;
863 for (i = cli->duplicates_info[p].cnt - 1; i >= cli->duplicates_info[p].cnt - DUPLICATE_MESSAGES_BUFFER; i--) { 989
864 /* Search the entry corresponding to the request */ 990 CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
865 if ( (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].id == req->radius.hdr->identifier) 991
866 && (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].port == req->port) 992 /* Search this message in our list */
867 && !memcmp(&cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].auth[0], &req->radius.hdr->authenticator[0], 16)) { 993 for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
868 /* This should not happen, but just in case */ 994 int cmp = 0;
869 if (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans) { 995 struct req_info * r = (struct req_info *)(li->o);
870 radius_msg_free(cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans); 996 if (r->id < req->radius.hdr->identifier)
871 free(cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans); 997 continue;
872 } 998 if (r->id > req->radius.hdr->identifier)
873 /* Now save the answer message */
874 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans = *msg;
875 *msg = NULL;
876 break; 999 break;
877 } 1000 if (r->port < req->port)
878 } 1001 continue;
879 1002 if (r->port > req->port)
880 /* If we have not found the request in our circular buffer, it is probably too small */ 1003 break;
1004 cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
1005 if (cmp < 0)
1006 continue;
1007 if (cmp > 0);
1008 break;
1009
1010 /* We have the request in our duplicate cache */
1011 /* This should not happen, but just in case... */
1012 if (r->ans) {
1013 radius_msg_free(r->ans);
1014 free(r->ans);
1015 }
1016
1017 /* Now save the message */
1018 r->ans = *msg;
1019 *msg = NULL;
1020
1021 /* Update the timestamp */
1022 {
1023 time_t now = time(NULL);
1024 TRACE_DEBUG(FULL, "Sent RADIUS answer %d seconds after the request was received.", now - r->received);
1025 r->received = now;
1026 fd_list_unlink(&r->by_time); /* Move as last entry, since it is the most recent */
1027 fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time);
1028 }
1029 break;
1030 }
1031
1032 CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
1033
1034 /* If we have not found the request in our list, the purge time is probably too small */
881 if (*msg) { 1035 if (*msg) {
882 TODO("Implement a dynamic list for RADIUS duplicates detection based on expiry time instead of number of messages"); 1036 TODO("Augment the purge time...");
883 TRACE_DEBUG(INFO, "The circular buffer has circled before the Diameter answer was received, you should definitely increase DUPLICATE_MESSAGES_BUFFER value."); 1037 /* If we receive the duplicate request again, it will be converted to Diameter... */
884 /* We don't re-save the value */
885 radius_msg_free(*msg); 1038 radius_msg_free(*msg);
886 free(*msg); 1039 free(*msg);
887 *msg = NULL; 1040 *msg = NULL;
888 } 1041 }
889 1042
"Welcome to our mercurial repository"