Mercurial > hg > freeDiameter
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 |