Navigation


source: freeDiameter/extensions/app_radgw/rgw_clients.c @ 706:4ffbc9f1e922

Last change on this file since 706:4ffbc9f1e922 was 706:4ffbc9f1e922, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Large UNTESTED commit with the following changes:

  • Improved DiameterIdentity? handling (esp. interationalization issues), and improve efficiency of some string operations in peers, sessions, and dictionary modules (closes #7)
  • Cleanup in the session module to free only unreferenced sessions (#16)
  • Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
  • Improved peer state machine algorithm to counter SCTP multistream race condition.
File size: 39.3 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2010, WIDE Project and NICT                                                              *
6* All rights reserved.                                                                                   *
7*                                                                                                        *
8* Redistribution and use of this software in source and binary forms, with or without modification, are  *
9* permitted provided that the following conditions are met:                                              *
10*                                                                                                        *
11* * Redistributions of source code must retain the above                                                 *
12*   copyright notice, this list of conditions and the                                                    *
13*   following disclaimer.                                                                                *
14*                                                                                                        *
15* * Redistributions in binary form must reproduce the above                                              *
16*   copyright notice, this list of conditions and the                                                    *
17*   following disclaimer in the documentation and/or other                                               *
18*   materials provided with the distribution.                                                            *
19*                                                                                                        *
20* * Neither the name of the WIDE Project or NICT nor the                                                 *
21*   names of its contributors may be used to endorse or                                                  *
22*   promote products derived from this software without                                                  *
23*   specific prior written permission of WIDE Project and                                                *
24*   NICT.                                                                                                *
25*                                                                                                        *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                                             *
34*********************************************************************************************************/
35
36/* Manage the list of RADIUS clients, along with their shared secrets. */
37
38/* Probably some changes are needed to support RADIUS Proxies */
39
40#include "rgw.h"
41
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 */
44
45/* Ordered lists of clients. The order relationship is a memcmp on the address zone.
46   For same addresses, the port is compared.
47   The same address cannot be added twice, once with a 0-port and once with another port value.
48 */
49static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip);
50static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6);
51
52/* Lock to protect the previous lists. We use a rwlock because this list is mostly static, to allow parallel reading */
53static pthread_rwlock_t cli_rwl = PTHREAD_RWLOCK_INITIALIZER;
54
55/* Structure describing one received RADIUS message, for duplicate checks purpose. */
56struct 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
68static pthread_t dbt_expire = (pthread_t)NULL; /* The thread that will remove old requests information from all clients (one thread for all) */
69
70/* Structure describing one client */
71struct rgw_client {
72        /* Link information in global list (cli_ip or cli_ip6) */
73        struct fd_list          chain;
74       
75        /* Reference count */
76        int                     refcount;
77       
78        /* The address and optional port (alloc'd during configuration file parsing). */
79        union {
80                struct sockaddr         *sa; /* generic pointer */
81                struct sockaddr_in      *sin;
82                struct sockaddr_in6     *sin6;
83        };
84       
85        /* The FQDN, realm, and optional aliases */
86        int                      is_local; /* true if the RADIUS client runs on the same host -- we use Diameter Identity in that case */
87        enum rgw_cli_type        type; /* is it a proxy ? */
88        DiamId_t                 fqdn; /* malloc'd here */
89        size_t                   fqdn_len;
90        DiamId_t                 realm; /* references another string, do not free */
91        size_t                   realm_len;
92        struct {
93                os0_t            name;
94                size_t           len;
95        }                       *aliases; /* Received aliases */
96        size_t                   aliases_nb;
97       
98        /* The secret key data. */
99        struct {
100                unsigned char * data;
101                size_t          len;
102        }                       key;
103       
104        /* information of previous msg received, for duplicate checks. */
105        struct {
106                pthread_mutex_t dupl_lock;    /* The mutex protecting the following lists */
107                struct fd_list  dupl_by_id;   /* The list of req_info structures ordered by their id, port, and auth */
108                struct fd_list  dupl_by_time; /* The list of req_info structures ordered by their time (approximative) */
109        } dupl_info[2]; /*[0] for auth, [1] for acct. */
110};
111
112
113/* Create a new req_info structure and initialize its data from a RADIUS request message */
114static struct req_info * dupl_new_req_info(struct rgw_radius_msg_meta *msg) {
115        struct req_info * ret = NULL;
116        CHECK_MALLOC_DO( ret = malloc(sizeof(struct req_info)), return NULL );
117        memset(ret, 0, sizeof(struct req_info));
118        ret->port = msg->port;
119        ret->id   = msg->radius.hdr->identifier;
120        memcpy(&ret->auth[0], &msg->radius.hdr->authenticator[0], 16);
121        fd_list_init(&ret->by_id, ret);
122        fd_list_init(&ret->by_time, ret);
123        ret->received = time(NULL);
124        return ret;
125}
126
127/* Destroy a req_info structure, after it has been unlinked */
128static void dupl_free_req_info(struct req_info * r) {
129        CHECK_PARAMS_DO( r && FD_IS_LIST_EMPTY(&r->by_id) && FD_IS_LIST_EMPTY(&r->by_time), return );
130        if (r->ans) {
131                /* Free this RADIUS message */
132                radius_msg_free(r->ans);
133                free(r->ans);
134        }
135       
136        /* Use r->nbdup for some purpose? */
137       
138        free(r);
139}
140
141/* The core of the purge thread */
142static int dupl_purge_list(struct fd_list * clients) {
143
144        struct fd_list *li = NULL;
145       
146        for (li = clients->next; li != clients; li = li->next) {
147                struct rgw_client * client = (struct rgw_client *)li;
148                int p;
149               
150                for (p=0; p<=1; p++) {
151               
152                        /* Lock this list */
153                        time_t now;
154                        CHECK_POSIX( pthread_mutex_lock(&client->dupl_info[p].dupl_lock) );
155                       
156                        now = time(NULL);
157                       
158                        while (!FD_IS_LIST_EMPTY(&client->dupl_info[p].dupl_by_time)) {
159                       
160                                /* Check the first item in the list */
161                                struct req_info * r = (struct req_info *)(client->dupl_info[p].dupl_by_time.next->o);
162                               
163                                if (now - r->received > DUPLICATE_CHECK_LIFETIME) {
164                               
165                                        TRACE_DEBUG(ANNOYING + 1, "Purging RADIUS request (id: %02hhx, port: %hu, dup #%d, age %d secs)", r->id, ntohs(r->port), r->nbdup, now - r->received);
166                                       
167                                        /* Remove this record */
168                                        fd_list_unlink(&r->by_time);
169                                        fd_list_unlink(&r->by_id);
170                                        dupl_free_req_info(r);
171                                } else {
172                                        /* We are done for this list */
173                                        break;
174                                }
175                        }
176                       
177                        CHECK_POSIX( pthread_mutex_unlock(&client->dupl_info[p].dupl_lock) );
178                }
179        }
180        return 0;
181}
182
183/* Thread that purges old RADIUS requests */
184static void * dupl_th(void * arg) {
185        /* Set the thread name */
186        fd_log_threadname ( "app_radgw:duplicate_purge" );
187       
188        /* The thread will be canceled */
189        while (1) {
190               
191                /* 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 */
192                sleep(5);
193               
194                /* When we wake up, we will check all clients duplicate lists one by one */
195                CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), break );
196               
197                CHECK_FCT_DO( dupl_purge_list(&cli_ip), break );
198                CHECK_FCT_DO( dupl_purge_list(&cli_ip6), break );
199               
200                CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), break );
201       
202                /* Loop */
203        }
204       
205        /* If we reach this part, some fatal error was encountered */
206        CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
207        TRACE_DEBUG(FULL, "Thread terminated"); 
208        return NULL;
209}
210
211
212/* create a new rgw_client. the arguments are MOVED into the structure (to limit malloc & free calls). */
213static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
214{
215        struct rgw_client *tmp = NULL;
216        DiamId_t fqdn;
217        size_t fqdn_len;
218        int ret, i;
219        int loc = 0;
220       
221        /* Check if the IP address is local */
222        if (   ( ((*ip_port)->sa_family == AF_INET ) && (   IN_IS_ADDR_LOOPBACK( &((struct sockaddr_in  *)(*ip_port))->sin_addr ) ) )
223             ||( ((*ip_port)->sa_family == AF_INET6) && (  IN6_IS_ADDR_LOOPBACK( &((struct sockaddr_in6 *)(*ip_port))->sin6_addr) ) )) {
224                /* The client is local */
225                loc = 1;
226        } else {
227                char buf[255];
228       
229                /* Search FQDN for the client */
230                ret = getnameinfo( *ip_port, sizeof(struct sockaddr_storage), &buf[0], sizeof(buf), NULL, 0, 0 );
231                if (ret) {
232                        TRACE_DEBUG(INFO, "Unable to resolve peer name: %s", gai_strerror(ret));
233                        return EINVAL;
234                }
235                fqdn = &buf[0];
236                CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&fqdn, &fqdn_len, 1),
237                        {
238                                TRACE_DEBUG(INFO, "Unable to use resolved peer name '%s' as DiameterIdentity: %s", buf, strerror(ret));
239                                return ret;
240                        } );
241        }
242       
243        /* Create the new object */
244        CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) );
245        memset(tmp, 0, sizeof(struct rgw_client));
246        fd_list_init(&tmp->chain, NULL);
247       
248        /* Initialize the duplicate list info */
249        for (i=0; i<=1; i++) {
250                CHECK_POSIX( pthread_mutex_init(&tmp->dupl_info[i].dupl_lock, NULL) );
251                fd_list_init(&tmp->dupl_info[i].dupl_by_id, NULL);
252                fd_list_init(&tmp->dupl_info[i].dupl_by_time, NULL);
253        }
254        tmp->type = type;
255       
256        if (loc) {
257                tmp->is_local = 1;
258        } else {
259                /* Copy the fqdn */
260                tmp->fqdn = fqdn;
261                tmp->fqdn_len = fqdn_len;
262
263                /* Find an appropriate realm */
264                tmp->realm = strchr(fqdn, '.');
265                if (tmp->realm) {
266                        tmp->realm += 1;
267                        tmp->realm_len = tmp->fqdn_len - (tmp->realm - fqdn);
268                }
269                if ((!tmp->realm) || (*tmp->realm == '\0')) { /* in case the fqdn was "localhost." for example, if it is possible... */
270                        tmp->realm = fd_g_config->cnf_diamrlm;
271                        tmp->realm_len = fd_g_config->cnf_diamrlm_len;
272                }
273        }
274       
275        /* move the sa info reference */
276        tmp->sa = *ip_port;
277        *ip_port = NULL;
278       
279        /* move the key material */
280        tmp->key.data = *key;
281        tmp->key.len = keylen;
282        *key = NULL;
283       
284        /* Done! */
285        *res = tmp;
286        return 0;
287}
288
289/* Decrease refcount on a client; the lock must be held when this function is called. */
290static void client_unlink(struct rgw_client * client)
291{
292        client->refcount -= 1;
293       
294        if (client->refcount <= 0) {
295                int idx;
296                /* to be sure: the refcount should be 0 only when client_fini is called */
297                ASSERT( FD_IS_LIST_EMPTY(&client->chain) );
298               
299                /* Free the data */
300                for (idx = 0; idx < client->aliases_nb; idx++)
301                        free(client->aliases[idx].name);
302                free(client->aliases);
303                free(client->fqdn);
304                free(client->sa);
305                free(client->key.data);
306               
307                /* Free the duplicate info */
308                for (idx=0; idx <= 1; idx++){
309                        CHECK_POSIX_DO( pthread_mutex_lock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
310                       
311                        while (!FD_IS_LIST_EMPTY(&client->dupl_info[idx].dupl_by_id)) {
312                                struct req_info * r = (struct req_info *)(client->dupl_info[idx].dupl_by_id.next->o);
313                                fd_list_unlink( &r->by_id );
314                                fd_list_unlink( &r->by_time );
315                                dupl_free_req_info(r);
316                        }
317                       
318                        CHECK_POSIX_DO( pthread_mutex_unlock( &client->dupl_info[idx].dupl_lock ), /* continue */ );
319
320                }
321               
322                free(client);
323        }
324}
325
326
327/* Macro to avoid duplicating the code in the next function */
328#define client_search_family( _family_ )                                                                                                \
329                case AF_INET##_family_: {                                                                                               \
330                        struct sockaddr_in##_family_ * sin##_family_ = (struct sockaddr_in##_family_ *)ip_port;                         \
331                        for (ref = cli_ip##_family_.next; ref != &cli_ip##_family_; ref = ref->next) {                                  \
332                                cmp = memcmp(&sin##_family_->sin##_family_##_addr,                                                      \
333                                             &((struct rgw_client *)ref)->sin##_family_->sin##_family_##_addr,                          \
334                                             sizeof(struct in##_family_##_addr));                                                       \
335                                if (cmp > 0) continue; /* search further in the list */                                                 \
336                                if (cmp < 0) break; /* this IP is not in the list */                                                    \
337                                /* Now compare the ports as follow: */                                                                  \
338                                     /* If the ip_port we are searching does not contain a port, just return the first match result */  \
339                                if ( (sin##_family_->sin##_family_##_port == 0)                                                         \
340                                     /* If the entry in the list does not contain a port, return it as a match */                       \
341                                  || (((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port == 0)                             \
342                                     /* If both ports are equal, it is a match */                                                       \
343                                  || (sin##_family_->sin##_family_##_port ==                                                            \
344                                                ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port)) {                     \
345                                        *res = (struct rgw_client *)ref;                                                                \
346                                        return EEXIST;                                                                                  \
347                                }                                                                                                       \
348                                /* Otherwise, the list is ordered by port value (byte order does not matter */                          \
349                                if (sin##_family_->sin##_family_##_port                                                                 \
350                                        > ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port) continue;                    \
351                                else break;                                                                                             \
352                        }                                                                                                               \
353                        *res = (struct rgw_client *)(ref->prev);                                                                        \
354                        return ENOENT;                                                                                                  \
355                }
356/* Function to look for an existing rgw_client, or the previous element.
357   The cli_rwl must be held for reading (at least) when calling this function.
358   Returns ENOENT if the matching client does not exist, and res points to the previous element in the list.
359   Returns EEXIST if the matching client is found, and res points to this element.
360   Returns other error code on other error. */
361static int client_search(struct rgw_client ** res, struct sockaddr * ip_port )
362{
363        int cmp;
364        struct fd_list *ref = NULL;
365       
366        CHECK_PARAMS(res && ip_port);
367       
368        switch (ip_port->sa_family) {
369                client_search_family()
370                                break;
371               
372                client_search_family( 6 )
373                                break;
374        }
375       
376        /* We're never supposed to reach this point */
377        ASSERT(0);
378        return EINVAL;
379}
380
381int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len)
382{
383        CHECK_PARAMS( cli && key && key_len );
384        *key = cli->key.data;
385        *key_len = cli->key.len;
386        return 0;
387}
388
389int rgw_clients_gettype(struct rgw_client * cli, enum rgw_cli_type *type)
390{
391        CHECK_PARAMS( cli && type );
392        *type = cli->type;
393        return 0;
394}
395
396
397int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref)
398{
399        int ret = 0;
400       
401        TRACE_ENTRY("%p %p", ip_port, ref);
402       
403        CHECK_PARAMS(ip_port && ref);
404       
405        CHECK_POSIX( pthread_rwlock_rdlock(&cli_rwl) );
406
407        ret = client_search(ref, ip_port);
408        if (ret == EEXIST) {
409                (*ref)->refcount ++;
410                ret = 0;
411        } else {
412                *ref = NULL;
413        }
414       
415        CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
416       
417        return ret;
418}
419
420int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli)
421{
422        int p, dup = 0;
423        struct fd_list * li;
424        struct req_info * r;
425       
426        TRACE_ENTRY("%p %p", msg, cli);
427       
428        CHECK_PARAMS( msg && cli );
429       
430        if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH)
431                p = 0;
432        else
433                p = 1;
434       
435        CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
436       
437        /* Search if we have this message in our list */
438        for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
439                int cmp = 0;
440                r = (struct req_info *)(li->o);
441                if (r->id < (*msg)->radius.hdr->identifier)
442                        continue;
443                if (r->id > (*msg)->radius.hdr->identifier)
444                        break;
445                if (r->port < (*msg)->port)
446                        continue;
447                if (r->port > (*msg)->port)
448                        break;
449                cmp = memcmp(&r->auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
450                if (cmp < 0)
451                        continue;
452                if (cmp > 0)
453                        break;
454                dup = 1;
455                break;
456        }
457       
458        if (dup) {
459                time_t now = time(NULL);
460                r->nbdup += 1;
461                TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d, previously seen %d secs ago).", 
462                                r->id, ntohs(r->port), r->nbdup, now - r->received);
463               
464                if (r->ans) {
465                        /* Resend the answer */
466                        CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, r->ans->buf, r->ans->buf_used, cli->sa, r->port),  );
467                       
468                        /* Should we delete 'r' so that a further duplicate will again be converted to Diameter? */
469                }
470               
471                /* Update the timestamp */
472                r->received = now;
473                fd_list_unlink(&r->by_time);
474                fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* Move as last entry, since it is the most recent */
475               
476                /* Delete the request message */
477                rgw_msg_free(msg);
478               
479        } else {
480                /* The message was not a duplicate, we save it */
481                /* li currently points the the next entry in list_by_id */
482                CHECK_MALLOC_DO( r= dupl_new_req_info(*msg), { CHECK_POSIX_DO(pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ), ); return ENOMEM; } );
483                fd_list_insert_before(li, &r->by_id);
484                fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time); /* it is the most recent */
485        }
486               
487        CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
488       
489        return 0;
490}
491
492/* Check if the message has a valid authenticator, and update the meta-data accordingly */
493int rgw_clients_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth)
494{
495        unsigned char * key;
496        size_t keylen;
497        int count;
498       
499        TRACE_ENTRY("%p %p %p", msg, cli, req_auth);
500       
501        CHECK_PARAMS(msg && cli);
502       
503        CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen));
504       
505        count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0);
506        if (count > 1) {
507                TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count);
508                return EINVAL;
509        }
510        if (count == 0) {
511                TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes.");
512                msg->valid_mac = 0;
513        } else {
514                if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) {
515                        TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message.");
516                        return EINVAL;
517                }
518                msg->valid_mac = 1;
519        }
520       
521        return 0;
522}
523
524static struct dict_object * cache_orig_host = NULL;
525static struct dict_object * cache_orig_realm = NULL;
526static struct dict_object * cache_route_record = NULL;
527
528int rgw_clients_init(void)
529{
530        TRACE_ENTRY();
531        CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) );
532        CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) );
533        CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &cache_route_record, ENOENT) );
534       
535        /* Create the thread that will purge old RADIUS duplicates */
536        CHECK_POSIX( pthread_create( &dbt_expire, NULL, dupl_th, NULL) );
537       
538        return 0;
539}
540
541
542/* The following function checks if a RADIUS message contains a valid NAS identifier, and initializes an empty Diameter
543 message with the appropriate routing information */
544/* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */
545/* Also update the client list of aliases if needed */
546int rgw_clients_create_origin(struct rgw_radius_msg_meta *msg, struct rgw_client * cli, struct msg ** diam)
547{
548        int idx;
549        int valid_nas_info = 0;
550        struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL;
551        size_t nas_id_len;
552        char * oh_str = NULL; size_t oh_strlen; int oh_free = 0;
553        char * or_str = NULL; size_t or_strlen;
554        char * rr_str = NULL; size_t rr_strlen;
555        char buf[REVERSE_DNS_SIZE_MAX]; /* to store DNS lookups results */
556       
557        struct avp *avp = NULL;
558        union avp_value avp_val;
559       
560        TRACE_ENTRY("%p %p %p", msg, cli, diam);
561        CHECK_PARAMS(msg && cli && diam && (*diam == NULL));
562       
563        /* Find the relevant attributes, if any */
564        for (idx = 0; idx < msg->radius.attr_used; idx++) {
565                struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]);
566                size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
567               
568                if ((attr->type == RADIUS_ATTR_NAS_IP_ADDRESS) && (attr_len = 4)) {
569                        nas_ip = attr;
570                        continue;
571                }
572                       
573                if ((attr->type == RADIUS_ATTR_NAS_IDENTIFIER) && (attr_len > 0)) {
574                        nas_id = attr;
575                        nas_id_len = attr_len;
576                        continue;
577                }
578                       
579                if ((attr->type == RADIUS_ATTR_NAS_IPV6_ADDRESS) && (attr_len = 16)) {
580                        nas_ip6 = attr;
581                        continue;
582                }
583        }
584               
585        if (!nas_ip && !nas_ip6 && !nas_id) {
586                TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute.");
587               
588                /* Get information on this peer */
589                CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
590               
591                goto diameter;
592        }
593       
594        /* Check if the message was received from the IP in NAS-IP-Address attribute */
595        if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) {
596                TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from.");
597                valid_nas_info |= 1;
598        }
599        if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) {
600                TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from.");
601                valid_nas_info |= 2;
602        }
603       
604       
605        /*
606                        In RADIUS it would be possible for a rogue NAS to forge the NAS-IP-
607                        Address attribute value.  Diameter/RADIUS translation agents MUST
608                        check a received NAS-IP-Address or NAS-IPv6-Address attribute against
609                        the source address of the RADIUS packet.  If they do not match and
610                        the Diameter/RADIUS translation agent does not know whether the
611                        packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State
612                        attribute), then by default it is assumed that the source address
613                        corresponds to a RADIUS proxy, and that the NAS Address is behind
614                        that proxy, potentially with some additional RADIUS proxies in
615                        between.  The Diameter/RADIUS translation agent MUST insert entries
616                        in the Route-Record AVP corresponding to the apparent route.  This
617                        implies doing a reverse lookup on the source address and NAS-IP-
618                        Address or NAS-IPv6-Address attributes to determine the corresponding
619                        FQDNs.
620
621                        If the source address and the NAS-IP-Address or NAS-IPv6-Address do
622                        not match, and the Diameter/RADIUS translation agent knows that it is
623                        talking directly to the NAS (e.g., there are no RADIUS proxies
624                        between it and the NAS), then the error should be logged, and the
625                        packet MUST be discarded.
626
627                        Diameter agents and servers MUST check whether the NAS-IP-Address AVP
628                        corresponds to an entry in the Route-Record AVP.  This is done by
629                        doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve
630                        the corresponding FQDN, and by checking for a match with the Route-
631                        Record AVP.  If no match is found, then an error is logged, but no
632                        other action is taken.
633        */
634        if (nas_ip || nas_ip6) {
635                if (!valid_nas_info) {
636                        if ((!cli->is_local) && (cli->type == RGW_CLI_NAS)) {
637                                TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different \nfrom the sender's. Please configure as Proxy if this is expected.\n Message discarded.");
638                                return EINVAL;
639                        } else {
640                                int ret;
641                                sSS ss;
642                                /* the peer is configured as a proxy, or running on localhost, so accept the message */
643                               
644                                /* In that case, the cli will be stored as Route-Record and the NAS-IP-Address as origin */
645                                if (!cli->is_local) {
646                                        rr_str = cli->fqdn;
647                                        rr_strlen = cli->fqdn_len;
648                                }
649                               
650                                /* We must DNS-reverse the NAS-IP*-Address */
651                                memset(&ss, 0 , sizeof(sSS));
652                                if (nas_ip) {
653                                        sSA4 * sin = (sSA4 *)&ss;
654                                        sin->sin_family = AF_INET;
655                                        memcpy(&sin->sin_addr, nas_ip + 1, sizeof(struct in_addr));
656                                } else {
657                                        sSA6 * sin6 = (sSA6 *)&ss;
658                                        sin6->sin6_family = AF_INET6;
659                                        memcpy(&sin6->sin6_addr, nas_ip6 + 1, sizeof(struct in6_addr));
660                                }
661                                CHECK_SYS_DO( getnameinfo( (sSA *)&ss, sSAlen(&ss), &buf[0], sizeof(buf), NULL, 0, NI_NAMEREQD),
662                                        {
663                                                if (cli->is_local) {
664                                                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
665                                                        goto diameter;
666                                                }
667                                               
668                                                TRACE_DEBUG(INFO, "The NAS-IP*-Address cannot be DNS reversed in order to create the Origin-Host AVP; rejecting the message (translation is impossible).");
669                                                return EINVAL;
670                                        } );
671                                       
672                                oh_str = &buf[0];
673                                CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
674                                        {
675                                                if (cli->is_local) {
676                                                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
677                                                        goto diameter;
678                                                }
679                                               
680                                                TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
681                                                return ret;
682                                        } );
683                                oh_free = 1;
684                               
685                                or_str = strchr(oh_str, '.');
686                                if (or_str) {
687                                        or_str ++; /* move after the first dot */
688                                        if (*or_str == '\0')
689                                                or_str = NULL; /* Discard this realm, we will use the local realm later */
690                                        else
691                                                or_strlen = oh_strlen - (or_str - oh_str);
692                                }
693                        }
694                } else {
695                        /* The attribute matches the source address, just use this in origin-host */
696                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
697                }
698               
699                goto diameter; /* we ignore the nas_id in that case */
700        }
701       
702        /* We don't have a NAS-IP*-Address attribute if we are here */
703        if (cli->is_local) {
704                /* Simple: we use our own configuration */
705                CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
706                goto diameter;
707        }
708       
709        /* At this point, we only have nas_id, and the client is not local */
710        ASSERT(nas_id);
711       
712        {
713                int found, ret;
714                struct addrinfo hint, *res, *ptr;
715               
716                /*
717                        In RADIUS it would be possible for a rogue NAS to forge the NAS-
718                        Identifier attribute.  Diameter/RADIUS translation agents SHOULD
719                        attempt to check a received NAS-Identifier attribute against the
720                        source address of the RADIUS packet, by doing an A/AAAA RR query.  If
721                        the NAS-Identifier attribute contains an FQDN, then such a query
722                        would resolve to an IP address matching the source address.  However,
723                        the NAS-Identifier attribute is not required to contain an FQDN, so
724                        such a query could fail.  If it fails, an error should be logged, but
725                        no action should be taken, other than a reverse lookup on the source
726                        address and insert the resulting FQDN into the Route-Record AVP.
727
728                        Diameter agents and servers SHOULD check whether a NAS-Identifier AVP
729                        corresponds to an entry in the Route-Record AVP.  If no match is
730                        found, then an error is logged, but no other action is taken.
731                */
732               
733                /* first, check if the nas_id is the fqdn of the peer or a known alias */
734                if (!fd_os_almostcasecmp(nas_id + 1, nas_id_len, 
735                                                cli->fqdn, cli->fqdn_len)) {
736                        TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the client");
737                        found = 1;
738                } else {
739                        for (idx = 0; idx < cli->aliases_nb; idx++) {
740                                if (!fd_os_cmp(nas_id + 1, nas_id_len, 
741                                                cli->aliases[idx].name, cli->aliases[idx].len)) {
742                                        TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache");
743                                        found = 1;
744                                        break;
745                                }
746                        }
747                }
748               
749                if (found) {
750                        /* The NAS-Identifier matches the source IP */
751                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
752
753                        goto diameter;
754                }
755               
756                /* Attempt DNS resolution of the identifier */
757                ASSERT( nas_id_len < sizeof(buf) );
758                memcpy(buf, nas_id + 1, nas_id_len);
759                buf[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0';
760               
761                /* Now check if this alias is valid for this peer */
762                memset(&hint, 0, sizeof(hint));
763                hint.ai_flags  = AI_CANONNAME;
764                ret = getaddrinfo(buf, NULL, &hint, &res);
765                if (ret == 0) {
766                        strncpy(buf, res->ai_canonname, sizeof(buf));
767                        /* The name was resolved correctly, does it match the IP of the client? */
768                        for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
769                                if (cli->sa->sa_family != ptr->ai_family)
770                                        continue;
771                                if (memcmp(cli->sa, ptr->ai_addr, sSAlen(cli->sa)))
772                                        continue;
773                               
774                                found = 1;
775                                break;
776                        }
777                        freeaddrinfo(res);
778                       
779                        if (!found) {
780                                if (cli->type == RGW_CLI_NAS) {
781                                        TRACE_DEBUG(INFO, "The NAS-Identifier value '%.*s' resolves to a different IP than the client's, discarding the message. \nConfigure this client as a Proxy if this message should be valid.", 
782                                                nas_id_len, nas_id + 1);
783                                        return EINVAL;
784                                } else {
785                                        /* This identifier matches a different IP, assume it is a proxied message */
786                                        if (!cli->is_local) {
787                                                rr_str = cli->fqdn;
788                                                rr_strlen = cli->fqdn_len;
789                                        }
790                                        oh_str = &buf[0]; /* The canonname resolved */
791                                        CHECK_FCT_DO( ret = fd_os_validate_DiameterIdentity(&oh_str, &oh_strlen, 1),
792                                                {
793                                                        TRACE_DEBUG(INFO, "Unable to use resolved client name '%s' as DiameterIdentity: %s", buf, strerror(ret));
794                                                        return ret;
795                                                } );
796                                        oh_free = 1;
797                                        or_str = strchr(oh_str, '.');
798                                        if (or_str) {
799                                                or_str ++; /* move after the first dot */
800                                                if (*or_str == '\0')
801                                                        or_str = NULL; /* Discard this realm, we will use the local realm later */
802                                                else
803                                                        or_strlen = oh_strlen - (or_str - oh_str);
804                                        }
805                                }
806                        } else {
807                                /* It is a valid alias, save it */
808                                CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(cli->aliases[0])) );
809                               
810                                CHECK_MALLOC( cli->aliases[cli->aliases_nb + 1].name = os0dup(nas_id + 1, nas_id_len ) );
811                                cli->aliases[cli->aliases_nb + 1].len = nas_id_len;
812
813                                cli->aliases_nb ++;
814                                TRACE_DEBUG(FULL, "Saved valid alias for client: '%.*s' -> '%s'", nas_id_len, nas_id + 1, cli->fqdn);
815                                CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
816                        }
817                } else {
818                        /* Error resolving the name */
819                        TRACE_DEBUG(INFO, "NAS-Identifier '%s' cannot be resolved: %s. Ignoring...", buf, gai_strerror(ret));
820                        /* Assume this is a valid identifier for the client */
821                        CHECK_FCT( rgw_clients_get_origin(cli, &oh_str, &oh_strlen, &or_str, &or_strlen) );
822                }
823        }
824       
825        /* Now, let's create the empty Diameter message with Origin-Host, -Realm, and Route-Record if needed. */
826diameter:
827        ASSERT(oh_str); /* If it is not defined here, there is a bug... */
828        if (!or_str) {
829                or_str = fd_g_config->cnf_diamrlm; /* Use local realm in that case */
830                or_strlen = fd_g_config->cnf_diamrlm_len;
831        }
832       
833        /* Create an empty Diameter message so that extensions can store their AVPs */
834        CHECK_FCT(  fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam )  );
835       
836        /* Add the Origin-Host as next AVP */
837        CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) );
838        memset(&avp_val, 0, sizeof(avp_val));
839        avp_val.os.data = (unsigned char *)oh_str;
840        avp_val.os.len = oh_strlen;
841        CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
842        CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
843       
844        /* Add the Origin-Realm as next AVP */
845        CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) );
846        memset(&avp_val, 0, sizeof(avp_val));
847        avp_val.os.data = (unsigned char *)or_str;
848        avp_val.os.len = or_strlen;
849        CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
850        CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
851       
852        if (rr_str) {
853                CHECK_FCT( fd_msg_avp_new ( cache_route_record, 0, &avp ) );
854                memset(&avp_val, 0, sizeof(avp_val));
855                avp_val.os.data = (unsigned char *)rr_str;
856                avp_val.os.len = rr_strlen;
857                CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
858                CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) );
859        }
860       
861        if (oh_free)
862                free(oh_str);
863       
864        /* Done! */
865        return 0;
866}
867
868int rgw_clients_get_origin(struct rgw_client *cli, DiamId_t *fqdn, size_t *fqdnlen, DiamId_t *realm, size_t *realmlen)
869{
870        TRACE_ENTRY("%p %p %p %p %p", cli, fqdn, fqdnlen, realm, realmlen);
871        CHECK_PARAMS(cli && fqdn && fqdnlen);
872       
873        if (cli->is_local) {
874                *fqdn = fd_g_config->cnf_diamid;
875                *fqdnlen = fd_g_config->cnf_diamid_len;
876                if (realm)
877                        *realm= fd_g_config->cnf_diamrlm;
878                if (realmlen)
879                        *realmlen= fd_g_config->cnf_diamrlm_len;
880        } else {
881                *fqdn = cli->fqdn;
882                *fqdnlen = cli->fqdn_len;
883                if (realm)
884                        *realm= cli->realm;
885                if (realmlen)
886                        *realmlen= cli->realm_len;
887        }
888               
889        return 0;
890}
891
892char * rgw_clients_id(struct rgw_client *cli)
893{
894        return cli->is_local ? "(local)" : cli->fqdn;
895}
896
897
898void rgw_clients_dispose(struct rgw_client ** ref)
899{
900        TRACE_ENTRY("%p", ref);
901        CHECK_PARAMS_DO(ref, return);
902       
903        CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl),  );
904        client_unlink(*ref);
905        *ref = NULL;
906        CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), );
907}
908
909int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen, enum rgw_cli_type type )
910{
911        struct rgw_client * prev = NULL, *new = NULL;
912        int ret;
913       
914        TRACE_ENTRY("%p %p %lu", ip_port, key, keylen);
915       
916        CHECK_PARAMS( ip_port && key && *key && keylen );
917        CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) );
918        CHECK_PARAMS( (type == RGW_CLI_NAS) || (type == RGW_CLI_PXY) );
919       
920        /* Dump the entry in debug mode */
921        if (TRACE_BOOL(FULL + 1 )) {
922                TRACE_DEBUG(FULL, "Adding %s:", (type == RGW_CLI_NAS) ? "NAS" : "PROXY"  );
923                TRACE_DEBUG_sSA(FULL,    "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
924                TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]" );
925        }
926       
927        /* Lock the lists */
928        CHECK_POSIX( pthread_rwlock_wrlock(&cli_rwl) );
929       
930        /* Check if the same entry does not already exist */
931        ret = client_search(&prev, ip_port );
932        if (ret == ENOENT) {
933                /* No duplicate found, Ok to add */
934                CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen, type ), goto end );
935                fd_list_insert_after(&prev->chain, &new->chain);
936                new->refcount++;
937                ret = 0;
938                goto end;
939        }
940       
941        if (ret == EEXIST) {
942                /* Check if the key is the same, then skip or return an error */
943                if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) ) && (type == prev->type)) {
944                        TRACE_DEBUG(INFO, "Skipping duplicate client description");
945                        ret = 0;
946                        goto end;
947                }
948               
949                fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n");
950                TRACE_DEBUG(NONE, "Previous entry: %s", (prev->type == RGW_CLI_NAS) ? "NAS" : "PROXY");
951                TRACE_DEBUG_sSA(NONE,    "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "" );
952                TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]" );
953                TRACE_DEBUG(NONE, "Conflicting entry: %s", (type == RGW_CLI_NAS) ? "NAS" : "PROXY");
954                TRACE_DEBUG_sSA(NONE,    "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "" );
955                TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]" );
956        }
957end:
958        /* release the lists */
959        CHECK_POSIX( pthread_rwlock_unlock(&cli_rwl) );
960       
961        return ret;
962}
963
964static void dump_cli_list(struct fd_list *senti)
965{
966        struct rgw_client * client = NULL;
967        struct fd_list *ref = NULL;
968       
969        for (ref = senti->next; ref != senti; ref = ref->next) {
970                client = (struct rgw_client *)ref;
971                TRACE_DEBUG_sSA(NONE,    "  - ", client->sa, NI_NUMERICHOST | NI_NUMERICSERV, (client->type == RGW_CLI_NAS) ? "" : " [PROXY]" );
972        }
973}
974
975void rgw_clients_dump(void)
976{
977        if ( ! TRACE_BOOL(FULL) )
978                return;
979       
980        CHECK_POSIX_DO( pthread_rwlock_rdlock(&cli_rwl), /* ignore error */ );
981       
982        if (!FD_IS_LIST_EMPTY(&cli_ip))
983                fd_log_debug(" RADIUS IP clients list:\n");
984        dump_cli_list(&cli_ip);
985               
986        if (!FD_IS_LIST_EMPTY(&cli_ip6))
987                fd_log_debug(" RADIUS IPv6 clients list:\n");
988        dump_cli_list(&cli_ip6);
989               
990        CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
991}
992
993void rgw_clients_fini(void)
994{
995        struct fd_list * client;
996       
997        TRACE_ENTRY();
998       
999        CHECK_POSIX_DO( pthread_rwlock_wrlock(&cli_rwl), /* ignore error */ );
1000       
1001        CHECK_FCT_DO( fd_thr_term(&dbt_expire), /* continue */ );
1002
1003        /* empty the lists */
1004        while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) {
1005                client = cli_ip.next;
1006                fd_list_unlink(client);
1007                client_unlink((struct rgw_client *)client);
1008        }
1009        while (! FD_IS_LIST_EMPTY(&cli_ip6)) {
1010                client = cli_ip6.next;
1011                fd_list_unlink(client);
1012                client_unlink((struct rgw_client *)client);
1013        }
1014       
1015        CHECK_POSIX_DO( pthread_rwlock_unlock(&cli_rwl), /* ignore error */ );
1016       
1017}
1018
1019int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli)
1020{
1021        int p;
1022        struct fd_list * li;
1023       
1024        TRACE_ENTRY("%p %p %p", msg, req, cli);
1025        CHECK_PARAMS( msg && *msg && cli );
1026       
1027        if (!req) {
1028                /* We don't support this case yet */
1029                ASSERT(0);
1030                return ENOTSUP;
1031        }
1032       
1033        /* Add all the Proxy-States back in the message */
1034        for (p = 0; p < req->ps_nb; p++) {
1035                struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(req->radius.buf + req->radius.attr_pos[req->ps_first + p]);
1036               
1037                if (radius_msg_add_attr_to_array(*msg, attr)) {
1038                        TRACE_DEBUG(INFO, "Error in radius_msg_add_attr_to_array, ENOMEM");
1039                        radius_msg_free(*msg);
1040                        free(*msg);
1041                        *msg = NULL;
1042                        return ENOMEM;
1043                }
1044        }
1045       
1046        /* Add the Message-Authenticator if needed, and other final tasks */
1047        if (radius_msg_finish_srv(*msg, cli->key.data, cli->key.len, req->radius.hdr->authenticator)) {
1048                TRACE_DEBUG(INFO, "An error occurred while preparing the RADIUS answer");
1049                radius_msg_free(*msg);
1050                free(*msg);
1051                *msg = NULL;
1052                return EINVAL;
1053        }
1054       
1055        /* Debug */
1056        TRACE_DEBUG(FULL, "RADIUS message ready for sending:");
1057        rgw_msg_dump((struct rgw_radius_msg_meta *)*msg, 0);
1058
1059        /* Send the message */
1060        CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) );
1061
1062        /* update the duplicate cache */
1063        if (req->serv_type == RGW_PLG_TYPE_AUTH)
1064                p = 0;
1065        else
1066                p = 1;
1067       
1068        CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
1069       
1070        /* Search this message in our list */
1071        for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
1072                int cmp = 0;
1073                struct req_info * r = (struct req_info *)(li->o);
1074                if (r->id < req->radius.hdr->identifier)
1075                        continue;
1076                if (r->id > req->radius.hdr->identifier)
1077                        break;
1078                if (r->port < req->port)
1079                        continue;
1080                if (r->port > req->port)
1081                        break;
1082                cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
1083                if (cmp < 0)
1084                        continue;
1085                if (cmp > 0)
1086                        break;
1087               
1088                /* We have the request in our duplicate cache */
1089                /* This should not happen, but just in case... */
1090                if (r->ans) {
1091                        radius_msg_free(r->ans);
1092                        free(r->ans);
1093                }
1094               
1095                /* Now save the message */
1096                r->ans = *msg;
1097                *msg = NULL;
1098               
1099                /* Update the timestamp */
1100                {
1101                        time_t now = time(NULL);
1102                        r->received = now;
1103                        fd_list_unlink(&r->by_time); /* Move as last entry, since it is the most recent */
1104                        fd_list_insert_before(&cli->dupl_info[p].dupl_by_time, &r->by_time);
1105                }
1106                break;
1107        }
1108               
1109        CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
1110       
1111        /* If we have not found the request in our list, the purge time is probably too small */
1112        if (*msg) {
1113                TODO("Augment the purge time...");
1114                /* If we receive the duplicate request again, it will be converted to Diameter... */
1115                radius_msg_free(*msg);
1116                free(*msg);
1117                *msg = NULL;
1118        }
1119       
1120        /* Finished */
1121        return 0;
1122}
1123
1124/* Call this function when a RADIUS request has explicitely no answer (mainly accounting) so
1125that we purge the duplicate cache and allow further message to be translated again.
1126This is useful for example when a temporary error occurred in Diameter (like UNABLE_TO_DELIVER) */
1127int rgw_client_finish_nosend(struct rgw_radius_msg_meta * req, struct rgw_client * cli)
1128{
1129        int p;
1130        struct fd_list * li;
1131       
1132        TRACE_ENTRY("%p %p", req, cli);
1133        CHECK_PARAMS( req && cli );
1134       
1135        /* update the duplicate cache */
1136        if (req->serv_type == RGW_PLG_TYPE_AUTH)
1137                p = 0;
1138        else
1139                p = 1;
1140       
1141        CHECK_POSIX( pthread_mutex_lock( &cli->dupl_info[p].dupl_lock ) );
1142       
1143        /* Search this message in our list */
1144        for (li = cli->dupl_info[p].dupl_by_id.next; li != &cli->dupl_info[p].dupl_by_id; li = li->next) {
1145                int cmp = 0;
1146                struct req_info * r = (struct req_info *)(li->o);
1147                if (r->id < req->radius.hdr->identifier)
1148                        continue;
1149                if (r->id > req->radius.hdr->identifier)
1150                        break;
1151                if (r->port < req->port)
1152                        continue;
1153                if (r->port > req->port)
1154                        break;
1155                cmp = memcmp(&r->auth[0], &req->radius.hdr->authenticator[0], 16);
1156                if (cmp < 0)
1157                        continue;
1158                if (cmp > 0)
1159                        break;
1160               
1161                /* We have the request in our duplicate cache, remove it */
1162                fd_list_unlink(&r->by_id);
1163                fd_list_unlink(&r->by_time);
1164                dupl_free_req_info(r);
1165                break;
1166        }
1167               
1168        CHECK_POSIX( pthread_mutex_unlock( &cli->dupl_info[p].dupl_lock ) );
1169       
1170        /* Finished */
1171        return 0;
1172}
1173
Note: See TracBrowser for help on using the repository browser.