mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
9.5 KiB
362 lines
9.5 KiB
/*
|
|
* $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 <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#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;i<config->peers_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;i<p->applications_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;
|
|
}
|