Changeset 544:a0e3af6f94fb in freeDiameter for extensions/app_radgw
- Timestamp:
- Sep 14, 2010, 4:04:59 PM (14 years ago)
- Branch:
- default
- Phase:
- public
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/app_radgw/rgw_clients.c
r539 r544 41 41 42 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 45 /* Ordered lists of clients. The order relationship is a memcmp on the address zone. … … 49 50 static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6); 50 51 51 /* Mutex to protect the previous lists */ 52 static pthread_mutex_t cli_mtx = PTHREAD_MUTEX_INITIALIZER; 52 /* Lock to protect the previous lists. We use a rwlock because this list is mostly static, to allow parallel reading */ 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 70 /* Structure describing one client */ 55 71 struct rgw_client { 56 /* Link information in global list */72 /* Link information in global list (cli_ip or cli_ip6) */ 57 73 struct fd_list chain; 58 74 … … 82 98 } key; 83 99 84 /* information of previous msg received, for duplicate checks -- we keep the last DUPLICATE_MESSAGES_BUFFER messages on each port. */ 85 #define DUPLICATE_MESSAGES_BUFFER 200 /* This should actually be replaced with a time-based dynamic list! TODO... */ 100 /* information of previous msg received, for duplicate checks. */ 86 101 struct { 87 int cnt; /* Counts the number of (different) requests we received */ 88 struct { 89 uint16_t port; /* The source UDP port of the request */ 90 uint8_t id; /* The identifier in the request */ 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. */ 102 pthread_mutex_t dupl_lock; /* The mutex protecting the following lists */ 103 struct fd_list dupl_by_id; /* The list of req_info structures ordered by their id, port, and auth */ 104 struct fd_list dupl_by_time; /* The list of req_info structures ordered by their time (approximative) */ 105 } dupl_info[2]; /*[0] for auth, [1] for acct. */ 96 106 }; 97 107 98 108 99 100 /* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */ 109 /* Create a new req_info structure and initialize its data from a RADIUS request message */ 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 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 203 struct rgw_client *tmp = NULL; 104 204 char buf[255]; 105 int ret ;205 int ret, i; 106 206 int loc = 0; 107 207 … … 126 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 234 tmp->type = type; 129 235 … … 156 262 } 157 263 158 159 264 /* Decrease refcount on a client; the lock must be held when this function is called. */ 160 265 static void client_unlink(struct rgw_client * client) … … 177 282 /* Free the duplicate info */ 178 283 for (idx=0; idx <= 1; idx++){ 179 int i = 0;180 for (i = 0; i < DUPLICATE_MESSAGES_BUFFER; i++) {181 if (client->duplicates_info[idx].msg_info[i].ans) {182 /* Free this RADIUS message */183 radius_msg_free(client->duplicates_info[idx].msg_info[i].ans);184 free(client->duplicates_info[idx].msg_info[i].ans);185 }284 CHECK_POSIX_DO( pthread_mutex_lock( &client->dupl_info[idx].dupl_lock ), /* continue */ ); 285 286 while (!FD_IS_LIST_EMPTY(&client->dupl_info[idx].dupl_by_id)) { 287 struct req_info * r = (struct req_info *)(client->dupl_info[idx].dupl_by_id.next->o); 288 fd_list_unlink( &r->by_id ); 289 fd_list_unlink( &r->by_time ); 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 … … 222 330 } 223 331 /* Function to look for an existing rgw_client, or the previous element. 224 The cli_ mtx must be heldwhen calling this function.332 The cli_rwl must be held for reading (at least) when calling this function. 225 333 Returns ENOENT if the matching client does not exist, and res points to the previous element in the list. 226 334 Returns EEXIST if the matching client is found, and res points to this element. … … 270 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 382 ret = client_search(ref, ip_port); … … 280 388 } 281 389 282 CHECK_POSIX( pthread_ mutex_unlock(&cli_mtx) );390 CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) ); 283 391 284 392 return ret; … … 287 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 401 TRACE_ENTRY("%p %p", msg, cli); … … 298 408 p = 1; 299 409 300 /* Check in the previous DUPLICATE_MESSAGES_BUFFER messages if we have received the same identifier / authenticator / port combination */ 301 for (i = cli->duplicates_info[p].cnt - 1; i >= cli->duplicates_info[p].cnt - DUPLICATE_MESSAGES_BUFFER; i--) { 302 if ( (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].id == (*msg)->radius.hdr->identifier) 303 && (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].port == (*msg)->port) 304 && !memcmp(&cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].auth[0], &(*msg)->radius.hdr->authenticator[0], 16)) { 305 /* We already received this request */ 306 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].nbdup++; 307 dup = 1; 308 TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d).", 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); 410 CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) ); 411 412 /* Search if we have this message in our list */ 413 for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) { 414 int cmp = 0; 415 r = (struct req_info *)(li->o); 416 if (r->id < (*msg)->radius.hdr->identifier) 417 continue; 418 if (r->id > (*msg)->radius.hdr->identifier) 321 419 break; 322 } 323 } 324 325 /* If we did no already receive this request, save it for later */ 326 if (!dup) { 327 /* It's a new request, save its data */ 328 int i = cli->duplicates_info[p].cnt % DUPLICATE_MESSAGES_BUFFER; 329 330 cli->duplicates_info[p].msg_info[i].port = (*msg)->port; 331 cli->duplicates_info[p].msg_info[i].id = (*msg)->radius.hdr->identifier; 332 memcpy(&cli->duplicates_info[p].msg_info[i].auth[0], &(*msg)->radius.hdr->authenticator[0], 16); 333 if (cli->duplicates_info[p].msg_info[i].ans) { 334 /* Free the old answer */ 335 radius_msg_free(cli->duplicates_info[p].msg_info[i].ans); 336 free(cli->duplicates_info[p].msg_info[i].ans); 337 cli->duplicates_info[p].msg_info[i].ans = NULL; 338 } 339 cli->duplicates_info[p].msg_info[i].nbdup = 0; 420 if (r->port < (*msg)->port) 421 continue; 422 if (r->port > (*msg)->port) 423 break; 424 cmp = memcmp(&r->auth[0], &(*msg)->radius.hdr->authenticator[0], 16); 425 if (cmp < 0) 426 continue; 427 if (cmp > 0); 428 break; 429 dup = 1; 430 break; 431 } 432 433 if (dup) { 434 time_t now = time(NULL); 435 r->nbdup += 1; 436 TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d, previously seen %d secs ago).", 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; 342 } 443 /* Should we delete 'r' so that a further duplicate will again be converted to Diameter? */ 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 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 467 /* Check if the message has a valid authenticator, and update the meta-data accordingly */ 351 468 int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth) … … 390 507 CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); 391 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 513 return 0; 393 514 } … … 396 517 /* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter 397 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 521 int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam) 399 522 { … … 714 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 840 client_unlink(*ref); 718 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 … … 739 862 740 863 /* Lock the lists */ 741 CHECK_POSIX( pthread_ mutex_lock(&cli_mtx) );864 CHECK_POSIX( pthread_rwlock_wrlock(&cli_rwl) ); 742 865 743 866 /* Check if the same entry does not already exist */ … … 770 893 end: 771 894 /* release the lists */ 772 CHECK_POSIX( pthread_ mutex_unlock(&cli_mtx) );895 CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) ); 773 896 774 897 return ret; … … 791 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 918 if (!FD_IS_LIST_EMPTY(&cli_ip)) … … 801 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 … … 810 933 TRACE_ENTRY(); 811 934 812 CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ ); 813 935 CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl), /* ignore error */ ); 936 937 CHECK_FCT_DO( fd_thr_term(&dbt_expire), /* continue */ ); 938 814 939 /* empty the lists */ 815 940 while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) { … … 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 } … … 830 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 960 TRACE_ENTRY("%p %p %p", msg, req, cli); … … 856 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 985 if (req->serv_type == RGW_PLG_TYPE_AUTH) 860 986 p = 0; 861 987 else 862 988 p = 1; 863 for (i = cli->duplicates_info[p].cnt - 1; i >= cli->duplicates_info[p].cnt - DUPLICATE_MESSAGES_BUFFER; i--) { 864 /* Search the entry corresponding to the request */ 865 if ( (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].id == req->radius.hdr->identifier) 866 && (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].port == req->port) 867 && !memcmp(&cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].auth[0], &req->radius.hdr->authenticator[0], 16)) { 868 /* This should not happen, but just in case */ 869 if (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans) { 870 radius_msg_free(cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans); 871 free(cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans); 872 } 873 /* Now save the answer message */ 874 cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans = *msg; 875 *msg = NULL; 989 990 CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) ); 991 992 /* Search this message in our list */ 993 for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) { 994 int cmp = 0; 995 struct req_info * r = (struct req_info *)(li->o); 996 if (r->id < req->radius.hdr->identifier) 997 continue; 998 if (r->id > req->radius.hdr->identifier) 876 999 break; 877 } 878 } 879 880 /* If we have not found the request in our circular buffer, it is probably too small */ 1000 if (r->port < req->port) 1001 continue; 1002 if (r->port > req->port) 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 1035 if (*msg) { 882 TODO("Implement a dynamic list for RADIUS duplicates detection based on expiry time instead of number of messages"); 883 TRACE_DEBUG(INFO, "The circular buffer has circled before the Diameter answer was received, you should definitely increase DUPLICATE_MESSAGES_BUFFER value."); 884 /* We don't re-save the value */ 1036 TODO("Augment the purge time..."); 1037 /* If we receive the duplicate request again, it will be converted to Diameter... */ 885 1038 radius_msg_free(*msg); 886 1039 free(*msg);
Note: See TracChangeset
for help on using the changeset viewer.