Navigation


source: freeDiameter/freeDiameter/peers.c @ 29:5ba91682f0bc

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

Added a test for cnxctx (tbc) and fixed some bugs

File size: 15.5 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2009, 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#include "fD.h"
37
38/* Global list of peers */
39struct fd_list   fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
40pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
41
42
43/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
44int fd_peer_alloc(struct fd_peer ** ptr)
45{
46        struct fd_peer *p;
47       
48        TRACE_ENTRY("%p", ptr);
49        CHECK_PARAMS(ptr);
50       
51        if (*ptr) {
52                p = *ptr;
53        } else {
54                CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
55                *ptr = p;
56        }
57       
58        /* Now initialize the content */
59        memset(p, 0, sizeof(struct fd_peer));
60       
61        fd_list_init(&p->p_hdr.chain, p);
62       
63        fd_list_init(&p->p_hdr.info.pi_endpoints, NULL);
64        fd_list_init(&p->p_hdr.info.pi_apps, NULL);
65       
66        p->p_eyec = EYEC_PEER;
67        fd_list_init(&p->p_expiry, p);
68        fd_list_init(&p->p_actives, p);
69        p->p_hbh = lrand48();
70        CHECK_FCT( fd_fifo_new(&p->p_events) );
71        CHECK_FCT( fd_fifo_new(&p->p_recv) );
72        CHECK_FCT( fd_fifo_new(&p->p_tosend) );
73        fd_list_init(&p->p_sentreq, p);
74       
75        return 0;
76}
77
78/* Add a new peer entry */
79int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
80{
81        struct fd_peer *p = NULL;
82        struct fd_list * li;
83        int ret = 0;
84        TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
85        CHECK_PARAMS(info && info->pi_diamid);
86       
87        /* Create a structure to contain the new peer information */
88        CHECK_FCT( fd_peer_alloc(&p) );
89       
90        /* Copy the informations from the parameters received */
91        CHECK_MALLOC( p->p_hdr.info.pi_diamid = strdup(info->pi_diamid) );
92        if (info->pi_realm) {
93                CHECK_MALLOC( p->p_hdr.info.pi_realm = strdup(info->pi_realm) );
94        }
95       
96        p->p_hdr.info.pi_flags.pro3 = info->pi_flags.pro3;
97        p->p_hdr.info.pi_flags.pro4 = info->pi_flags.pro4;
98        p->p_hdr.info.pi_flags.alg  = info->pi_flags.alg;
99        p->p_hdr.info.pi_flags.sec  = info->pi_flags.sec;
100        p->p_hdr.info.pi_flags.exp  = info->pi_flags.exp;
101       
102        p->p_hdr.info.pi_lft     = info->pi_lft;
103        p->p_hdr.info.pi_port    = info->pi_port;
104        p->p_hdr.info.pi_tctimer = info->pi_tctimer;
105        p->p_hdr.info.pi_twtimer = info->pi_twtimer;
106       
107        if (info->pi_sec_data.priority) {
108                CHECK_MALLOC( p->p_hdr.info.pi_sec_data.priority = strdup(info->pi_sec_data.priority) );
109        }
110       
111        /* Move the items from one list to the other */
112        if (info->pi_endpoints.next)
113                while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
114                        li = info->pi_endpoints.next;
115                        fd_list_unlink(li);
116                        fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
117                }
118       
119       
120        /* The internal data */
121        if (orig_dbg) {
122                CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
123        } else {
124                CHECK_MALLOC( p->p_dbgorig = strdup("unknown") );
125        }
126        p->p_cb = cb;
127        p->p_cb_data = cb_data;
128       
129        /* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
130        CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
131       
132        for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
133                struct fd_peer * next = (struct fd_peer *)li;
134                int cmp = strcasecmp( p->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamid );
135                if (cmp > 0)
136                        continue;
137                if (cmp == 0)
138                        ret = EEXIST;
139                break;
140        }
141       
142        /* We can insert the new peer object */
143        if (! ret) {
144                /* Update expiry list */
145                CHECK_FCT_DO( ret = fd_p_expi_update( p ), goto out );
146               
147                /* Insert the new element in the list */
148                fd_list_insert_before( li, &p->p_hdr.chain );
149        }
150
151out:   
152        CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
153        if (ret) {
154                CHECK_FCT( fd_peer_free(&p) );
155        } else {
156                CHECK_FCT( fd_psm_begin(p) );
157        }
158        return ret;
159}
160
161
162#define free_null( _v )         \
163        if (_v) {               \
164                free(_v);       \
165                (_v) = NULL;    \
166        }
167       
168#define free_list( _l )                                                 \
169        while (!FD_IS_LIST_EMPTY(_l)) {                                 \
170                struct fd_list * __li = ((struct fd_list *)(_l))->next; \
171                fd_list_unlink(__li);                                   \
172                free(__li);                                             \
173        }
174
175/* Destroy a structure once all cleanups have been performed */
176int fd_peer_free(struct fd_peer ** ptr)
177{
178        struct fd_peer *p;
179        void * t;
180       
181        TRACE_ENTRY("%p", ptr);
182        CHECK_PARAMS(ptr);
183        p = *ptr;
184        *ptr = NULL;
185        CHECK_PARAMS(p);
186       
187        CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
188       
189        free_null(p->p_hdr.info.pi_diamid); 
190        free_null(p->p_hdr.info.pi_realm); 
191        free_list( &p->p_hdr.info.pi_endpoints );
192        /* Assume the security data is already freed */
193        free_null(p->p_hdr.info.pi_prodname);
194        free_list( &p->p_hdr.info.pi_apps );
195       
196        free_null(p->p_dbgorig);
197        ASSERT(FD_IS_LIST_EMPTY(&p->p_expiry));
198        ASSERT(FD_IS_LIST_EMPTY(&p->p_actives));
199       
200        CHECK_FCT( fd_thr_term(&p->p_psm) );
201        while ( fd_fifo_tryget(p->p_events, &t) == 0 ) {
202                struct fd_event * ev = t;
203                TRACE_DEBUG(FULL, "Found event %d(%p) in queue of peer %p being destroyed", ev->code, ev->data, p);
204                free(ev);
205        }
206        CHECK_FCT( fd_fifo_del(&p->p_events) );
207       
208        CHECK_FCT( fd_thr_term(&p->p_inthr) );
209        while ( fd_fifo_tryget(p->p_recv, &t) == 0 ) {
210                struct msg * m = t;
211                TRACE_DEBUG(FULL, "Found message %p in incoming queue of peer %p being destroyed", m, p);
212                /* We simply destroy, the remote peer will re-send to someone else...*/
213                CHECK_FCT(fd_msg_free(m));
214        }
215        CHECK_FCT( fd_fifo_del(&p->p_recv) );
216       
217        CHECK_FCT( fd_thr_term(&p->p_outthr) );
218        while ( fd_fifo_tryget(p->p_tosend, &t) == 0 ) {
219                struct msg * m = t;
220                TRACE_DEBUG(FULL, "Found message %p in outgoing queue of peer %p being destroyed, requeue", m, p);
221                /* We simply requeue in global, the routing thread will re-handle it. */
222               
223        }
224        CHECK_FCT( fd_fifo_del(&p->p_tosend) );
225       
226        while (!FD_IS_LIST_EMPTY(&p->p_sentreq)) {
227                struct sentreq * sr = (struct sentreq *)(p->p_sentreq.next);
228                fd_list_unlink(&sr->chain);
229                TRACE_DEBUG(FULL, "Found message %p in list of sent requests to peer %p being destroyed, requeue (fallback)", sr->req, p);
230                CHECK_FCT(fd_fifo_post(fd_g_outgoing, &sr->req));
231                free(sr);
232        }
233       
234        if (p->p_cnxctx) {
235                fd_cnx_destroy(p->p_cnxctx);
236        }
237       
238        if (p->p_cb)
239                (*p->p_cb)(NULL, p->p_cb_data);
240       
241        free(p);
242       
243        return 0;
244}
245
246/* Terminate peer module (destroy all peers) */
247int fd_peer_fini()
248{
249        struct fd_list * li;
250        struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
251        int list_empty;
252        struct timespec wait_until, now;
253       
254        TRACE_ENTRY();
255       
256        CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
257       
258        TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
259       
260        CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
261        for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
262                struct fd_peer * peer = (struct fd_peer *)li;
263               
264                if (peer->p_hdr.info.pi_state != STATE_ZOMBIE) {
265                        CHECK_FCT_DO( fd_psm_terminate(peer), /* continue */ );
266                } else {
267                        li = li->prev; /* to avoid breaking the loop */
268                        fd_list_unlink(&peer->p_hdr.chain);
269                        fd_list_insert_before(&purge, &peer->p_hdr.chain);
270                }
271        }
272        list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
273        CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
274       
275        if (!list_empty) {
276                CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
277                TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT);
278                wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT;
279                wait_until.tv_nsec = now.tv_nsec;
280        }
281       
282        while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
283               
284                /* Allow the PSM(s) to execute */
285                pthread_yield();
286               
287                /* Remove zombie peers */
288                CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
289                for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
290                        struct fd_peer * peer = (struct fd_peer *)li;
291                        if (peer->p_hdr.info.pi_state == STATE_ZOMBIE) {
292                                li = li->prev; /* to avoid breaking the loop */
293                                fd_list_unlink(&peer->p_hdr.chain);
294                                fd_list_insert_before(&purge, &peer->p_hdr.chain);
295                        }
296                }
297                list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
298                CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
299                CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
300        }
301       
302        if (!list_empty) {
303                TRACE_DEBUG(INFO, "Forcing connections shutdown");
304                CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
305                while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
306                        struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next);
307                        fd_psm_abord(peer);
308                        fd_list_unlink(&peer->p_hdr.chain);
309                        fd_list_insert_before(&purge, &peer->p_hdr.chain);
310                }
311                CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
312        }
313       
314        /* Free memory objects of all peers */
315        while (!FD_IS_LIST_EMPTY(&purge)) {
316                struct fd_peer * peer = (struct fd_peer *)(purge.next);
317                fd_list_unlink(&peer->p_hdr.chain);
318                fd_peer_free(&peer);
319        }
320       
321        return 0;
322}
323
324/* Dump info of one peer */
325void fd_peer_dump(struct fd_peer * peer, int details)
326{
327        if (peer->p_eyec != EYEC_PEER) {
328                fd_log_debug("  Invalid peer @ %p !\n", peer);
329                return;
330        }
331
332        fd_log_debug(">  %s\t%s", STATE_STR(peer->p_hdr.info.pi_state), peer->p_hdr.info.pi_diamid);
333        if (details > INFO) {
334                fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.pi_realm);
335                if (peer->p_hdr.info.pi_prodname)
336                        fd_log_debug("\t['%s' %u]", peer->p_hdr.info.pi_prodname, peer->p_hdr.info.pi_firmrev);
337        }
338        fd_log_debug("\n");
339        if (details > FULL) {
340                /* Dump all info */
341                fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig);
342                fd_log_debug("\tFlags : %s%s%s%s%s - %s%s%s\n", 
343                                peer->p_hdr.info.pi_flags.pro3 == PI_P3_DEFAULT ? "" :
344                                        (peer->p_hdr.info.pi_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
345                                peer->p_hdr.info.pi_flags.pro4 == PI_P4_DEFAULT ? "" :
346                                        (peer->p_hdr.info.pi_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
347                                peer->p_hdr.info.pi_flags.alg ? "PrefTCP." : "",
348                                peer->p_hdr.info.pi_flags.sec == PI_SEC_DEFAULT ? "" :
349                                        (peer->p_hdr.info.pi_flags.sec == PI_SEC_NONE ? "IPSec." : "InbandTLS."),
350                                peer->p_hdr.info.pi_flags.exp ? "Expire." : "",
351                                peer->p_hdr.info.pi_flags.inband_none ? "InbandIPsec." : "",
352                                peer->p_hdr.info.pi_flags.inband_tls ?  "InbandTLS." : "",
353                                peer->p_hdr.info.pi_flags.relay ? "Relay (0xffffff)" : "No relay"
354                                );
355                fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.pi_lft);
356               
357                TODO("Dump remaining useful information");
358        }
359}
360
361/* Dump the list of peers */
362void fd_peer_dump_list(int details)
363{
364        struct fd_list * li;
365       
366        fd_log_debug("Dumping list of peers :\n");
367        CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
368       
369        for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
370                struct fd_peer * np = (struct fd_peer *)li;
371                fd_peer_dump(np, details);
372        }
373       
374        CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
375}
376
377/* Handle an incoming CER request on a new connection */
378int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
379{
380        struct msg * msg;
381        struct dict_object *avp_oh_model;
382        avp_code_t code = AC_ORIGIN_HOST;
383        struct avp *avp_oh;
384        struct avp_hdr * avp_hdr;
385        struct fd_list * li;
386        int found = 0;
387        int ret = 0;
388        struct fd_peer * peer;
389        struct cnx_incoming * ev_data;
390       
391        TRACE_ENTRY("%p %p", cer, cnx);
392        CHECK_PARAMS(cer && *cer && cnx && *cnx);
393       
394        msg = *cer; 
395       
396        /* Find the Diameter Identity of the remote peer in the message */
397        CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT) );
398        CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
399        CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
400       
401        /* Search if we already have this peer id in our list */
402        CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
403       
404        for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
405                peer = (struct fd_peer *)li;
406                int cmp = strncasecmp( avp_hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, avp_hdr->avp_value->os.len );
407                if (cmp > 0)
408                        continue;
409                if (cmp == 0)
410                        found = 1;
411                break;
412        }
413       
414        if (!found) {
415                /* Create a new peer entry for this new remote peer */
416                peer = NULL;
417                CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
418               
419                /* Set the peer Diameter Id and the responder flag parameters */
420                CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = malloc(avp_hdr->avp_value->os.len + 1), { ret = ENOMEM; goto out; } );
421                CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
422                peer->p_flags.pf_responder = 1;
423               
424                /* Upgrade the lock to write lock */
425                CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&fd_g_peers_rw), goto out );
426               
427                /* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
428                fd_list_insert_before( li, &peer->p_hdr.chain );
429               
430                /* Release the write lock */
431                CHECK_POSIX_DO( ret = pthread_rwlock_unlock(&fd_g_peers_rw), goto out );
432               
433                /* Start the PSM, which will receive the event bellow */
434                CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
435        }
436               
437        /* Send the new connection event to the PSM */
438        CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
439        memset(ev_data, 0, sizeof(ev_data));
440       
441        ev_data->cer = msg;
442        ev_data->cnx = *cnx;
443        ev_data->validate = !found;
444       
445        CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(ev_data), ev_data), goto out );
446       
447out:   
448        CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
449
450        if (ret == 0) {
451                /* Reset the "out" parameters, so that they are not cleanup on function return. */
452                *cer = NULL;
453                *cnx = NULL;
454        }
455       
456        return ret;
457}
458
459/* Save a callback to accept / reject incoming unknown peers */
460int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
461{
462       
463        TODO("...");
464        return ENOTSUP;
465}
466
467/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error or if the peer is rejected */
468int fd_peer_validate( struct fd_peer * peer )
469{
470        TODO("Default to reject");
471        TODO("Call all callbacks in turn");
472        TODO("Save cb2 in the peer if needed");
473        return ENOTSUP;
474}
Note: See TracBrowser for help on using the repository browser.