/* * $Id$ * * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com * * The initial version of this code was written by Dragos Vingarzan * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the * Fruanhofer Institute. It was and still is maintained in a separate * branch of the original SER. We are therefore migrating it to * Kamailio/SR and look forward to maintaining it from here on out. * 2011/2012 Smile Communications, Pty. Ltd. * ported/maintained/improved by * Jason Penton (jason(dot)penton(at)smilecoms.com and * Richard Good (richard(dot)good(at)smilecoms.com) as part of an * effort to add full IMS support to Kamailio/SR using a new and * improved architecture * * NB: Alot of this code was originally part of OpenIMSCore, * FhG Fokus. * Copyright (C) 2004-2006 FhG Fokus * Thanks for great work! This is an effort to * break apart the various CSCF functions into logically separate * components. We hope this will drive wider use. We also feel * that in this way the architecture is more complete and thereby easier * to manage in the Kamailio/SR environment * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include "diameter.h" #include "timer.h" #include "peermanager.h" #include "globals.h" #include "peerstatemachine.h" peer_list_t *peer_list=0; /**< list of peers */ gen_lock_t *peer_list_lock=0; /**< lock for the list of peers */ extern dp_config *config; /**< Configuration for this diameter peer */ extern char *dp_states[]; AAAMsgIdentifier *hopbyhop_id=0;/**< Current id for Hop-by-hop */ AAAMsgIdentifier *endtoend_id=0;/**< Current id for End-to-end */ gen_lock_t *msg_id_lock; /**< lock for the message identifier changes */ /** * Initializes the Peer Manager. * The initial list of peers is taken from the configuration provided * @param config - configuration for initial peers * @returns 1 */ int peer_manager_init(dp_config *config) { int i; peer *p; LM_DBG("peer_manager_init(): Peer Manager initialization...\n"); peer_list = shm_malloc(sizeof(peer_list_t)); peer_list->head = 0; peer_list->tail = 0; peer_list_lock = lock_alloc(); peer_list_lock = lock_init(peer_list_lock); hopbyhop_id = shm_malloc(sizeof(AAAMsgIdentifier)); endtoend_id = shm_malloc(sizeof(AAAMsgIdentifier)); msg_id_lock = lock_alloc(); msg_id_lock = lock_init(msg_id_lock); srand((unsigned int)time(0)); *hopbyhop_id = rand(); *endtoend_id = (time(0)&0xFFF)<<20; *endtoend_id |= rand() & 0xFFFFF; for(i=0;ipeers_cnt;i++){ p = new_peer(config->peers[i].fqdn,config->peers[i].realm,config->peers[i].port,config->peers[i].src_addr); if (!p) continue; p->is_dynamic = 0; add_peer(p); } add_timer(1,0,&peer_timer,0); return 1; } /** * Destroys the Peer Manager and disconnects all peer sockets. */ void peer_manager_destroy() { peer *foo,*bar; lock_get(peer_list_lock); foo = peer_list->head; while(foo){ if (foo->I_sock>0) close(foo->I_sock); if (foo->R_sock>0) close(foo->R_sock); bar = foo->next; free_peer(foo,1); foo = bar; } /* lock_get(msg_id_lock); */ shm_free(hopbyhop_id); shm_free(endtoend_id); lock_destroy(msg_id_lock); lock_dealloc((void*)msg_id_lock); shm_free(peer_list); lock_destroy(peer_list_lock); lock_dealloc((void*)peer_list_lock); LM_DBG("peer_manager_init(): ...Peer Manager destroyed\n"); } /** * Logs the list of peers * @param level - log level to print to */ void log_peer_list() { /* must have lock on peer_list_lock when calling this!!! */ peer *p; int i; LM_DBG("--- Peer List: ---\n"); for(p = peer_list->head;p;p = p->next){ LM_DBG(ANSI_GREEN" S["ANSI_YELLOW"%s"ANSI_GREEN"] "ANSI_BLUE"%.*s:%d"ANSI_GREEN" D["ANSI_RED"%c"ANSI_GREEN"]\n",dp_states[p->state],p->fqdn.len,p->fqdn.s,p->port,p->is_dynamic?'X':' '); for(i=0;iapplications_cnt;i++) LM_DBG(ANSI_YELLOW"\t [%d,%d]"ANSI_GREEN"\n",p->applications[i].id,p->applications[i].vendor); } LM_DBG("------------------\n"); } /** * Adds a peer to the peer list * @param p - peer to add */ void add_peer(peer *p) { if (!p) return; lock_get(peer_list_lock); p->next = 0; p->prev = peer_list->tail; if (!peer_list->head) peer_list->head = p; if (peer_list->tail) peer_list->tail->next = p; peer_list->tail = p; lock_release(peer_list_lock); } /** * Removes a peer from the peer list * @param p - the peer to remove */ void remove_peer(peer *p) { peer *i; if (!p) return; i = peer_list->head; while(i&&i!=p) i = i->next; if (i){ if (i->prev) i->prev->next = i->next; else peer_list->head = i->next; if (i->next) i->next->prev = i->prev; else peer_list->tail = i->prev; } } /** * Finds a peer based on the TCP socket. * @param sock - socket to look for * @returns the peer* or NULL if not found */ peer *get_peer_from_sock(int sock) { peer *i; lock_get(peer_list_lock); i = peer_list->head; while(i&&i->I_sock!=sock&&i->R_sock!=sock) i = i->next; lock_release(peer_list_lock); return i; } /** * Finds a peer based on the FQDN and Realm. * @param fqdn - the FQDN to look for * @param realm - the Realm to look for * @returns the peer* or NULL if not found */ peer *get_peer_from_fqdn(str fqdn,str realm) { peer *i; str dumb; lock_get(peer_list_lock); i = peer_list->head; while(i){ if (fqdn.len == i->fqdn.len && strncasecmp(fqdn.s,i->fqdn.s,fqdn.len)==0) break; i = i->next; } lock_release(peer_list_lock); if (!i&&config->accept_unknown_peers){ i = new_peer(fqdn,realm,3868,dumb); if (i){ i->is_dynamic=1; touch_peer(i); add_peer(i); } } return i; } /** * Finds a peer based on the FQDN. * @param fqdn - the FQDN to look for * @returns the peer* or NULL if not found */ peer *get_peer_by_fqdn(str *fqdn) { peer *i; lock_get(peer_list_lock); i = peer_list->head; while(i){ if (fqdn->len == i->fqdn.len && strncasecmp(fqdn->s,i->fqdn.s,fqdn->len)==0) break; i = i->next; } lock_release(peer_list_lock); return i; } /** * Timer function for peer management. * This is registered as a timer by peer_manager_init() and gets called every * #PEER_MANAGER_TIMER seconds. Then it looks on what changed and triggers events. * @param now - time of call * @param ptr - generic pointer for timers - not used */ int peer_timer(time_t now,void *ptr) { peer *p,*n; int i; LM_DBG("peer_timer(): taking care of peers...\n"); lock_get(peer_list_lock); p = peer_list->head; while(p){ lock_get(p->lock); n = p->next; if (p->disabled && (p->state != Closed || p->state != Closing)) { LM_DBG("Peer [%.*s] has been disabled - shutting down\n", p->fqdn.len, p->fqdn.s); if (p->state == I_Open) sm_process(p, Stop, 0, 1, p->I_sock); if (p->state == R_Open) sm_process(p, Stop, 0, 1, p->R_sock); lock_release(p->lock); p = n; continue; } if (p->activity+config->tc<=now){ LM_DBG("peer_timer(): Peer %.*s \tState %d \n",p->fqdn.len,p->fqdn.s,p->state); switch (p->state){ /* initiating connection */ case Closed: if (p->is_dynamic && config->drop_unknown_peers){ remove_peer(p); free_peer(p,1); break; } if (!p->disabled) { touch_peer(p); sm_process(p,Start,0,1,0); } break; /* timeouts */ case Wait_Conn_Ack: case Wait_I_CEA: case Closing: case Wait_Returns: case Wait_Conn_Ack_Elect: touch_peer(p); sm_process(p,Timeout,0,1,0); break; /* inactivity detected */ case I_Open: case R_Open: if (p->waitingDWA){ p->waitingDWA = 0; if (p->state==I_Open) sm_process(p,I_Peer_Disc,0,1,p->I_sock); if (p->state==R_Open) sm_process(p,R_Peer_Disc,0,1,p->R_sock); LM_WARN("Inactivity on peer [%.*s] and no DWA, Closing peer...\n", p->fqdn.len, p->fqdn.s); } else { p->waitingDWA = 1; Snd_DWR(p); touch_peer(p); LM_DBG("Inactivity on peer [%.*s], sending DWR... - if we don't get a reply, the peer will be closed\n", p->fqdn.len, p->fqdn.s); } break; /* ignored states */ /* unknown states */ default: LM_ERR("peer_timer(): Peer %.*s inactive in state %d\n", p->fqdn.len,p->fqdn.s,p->state); } } lock_release(p->lock); p = n; } lock_release(peer_list_lock); log_peer_list(); i = config->tc/5; if (i<=0) i=1; return i; } /** * Generates the next Hop-by-hop identifier. * @returns the new identifier to be used in messages */ inline AAAMsgIdentifier next_hopbyhop() { AAAMsgIdentifier x; lock_get(msg_id_lock); *hopbyhop_id = (*hopbyhop_id)+1; x = *hopbyhop_id; lock_release(msg_id_lock); return x; } /** * Generates the next End-to-end identifier. * @returns the new identifier to be used in messages */ inline AAAMsgIdentifier next_endtoend() { AAAMsgIdentifier x; lock_get(msg_id_lock); *endtoend_id = (*endtoend_id)+1; x = *endtoend_id; lock_release(msg_id_lock); return x; }