Skip to content

Commit

Permalink
modules/CDP: added stickiness to loadbalancing of diameter peers
Browse files Browse the repository at this point in the history
  • Loading branch information
jaybeepee committed Mar 26, 2015
1 parent e172b7a commit cb2ad9c
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 149 deletions.
335 changes: 187 additions & 148 deletions modules/cdp/routing.c
Expand Up @@ -50,7 +50,7 @@

#define LB_MAX_PEERS 20 /**< maximum peers that can be loadbalanced accross i.e. same metric */

extern dp_config *config; /**< Configuration for this diameter peer */
extern dp_config *config; /**< Configuration for this diameter peer */
int gcount = 0;

/**
Expand All @@ -59,72 +59,111 @@ int gcount = 0;
* @param app_id - the application id to look for
* @param vendor_id - the vendor id to look for, 0 if not vendor specific
* @returns 0 if not found, 1 if found
*/
int peer_handles_application(peer *p,int app_id,int vendor_id)
{
int i;
LM_DBG("Checking if peer %.*s handles application %d for vendord %d\n",p->fqdn.len,p->fqdn.s,app_id,vendor_id);
if (!p || !p->applications || !p->applications_cnt) return 0;
for(i=0;i<p->applications_cnt;i++)
if (p->applications[i].id==app_id && p->applications[i].vendor==vendor_id) return 1;
return 0;
*/
int peer_handles_application(peer *p, int app_id, int vendor_id) {
int i;
LM_DBG("Checking if peer %.*s handles application %d for vendord %d\n", p->fqdn.len, p->fqdn.s, app_id, vendor_id);
if (!p || !p->applications || !p->applications_cnt) return 0;
for (i = 0; i < p->applications_cnt; i++)
if (p->applications[i].id == app_id && p->applications[i].vendor == vendor_id) return 1;
return 0;
}

/**
* Get the first peer that is connected from the list of routing entries.
* @param r - the list of routing entries to look into
* @returns - the peer or null if none connected
*/
peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id)
{
peer *peers[LB_MAX_PEERS];
int peer_count=0;
int prev_metric=0;
routing_entry *i;
peer *p;
int j;
time_t least_recent_time;

LM_DBG("get_first_connected_route in list %p for app_id %d and vendor_id %d\n",
r,app_id,vendor_id);
for(i=r;i;i=i->next){
if (peer_count >= LB_MAX_PEERS)
break;
p = get_peer_by_fqdn(&(i->fqdn));
if (!p)
LM_DBG("The peer %.*s does not seem to be connected or configured\n",
i->fqdn.len,i->fqdn.s);
else
LM_DBG("The peer %.*s state is %s\n",i->fqdn.len,i->fqdn.s,
(p->state==I_Open||p->state==R_Open)?"opened":"closed");
if (p && !p->disabled && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {
LM_DBG("The peer %.*s matches - will forward there\n",i->fqdn.len,i->fqdn.s);
if (peer_count!=0) {//check the metric
if (i->metric != prev_metric)
break;
//metric must be the same
peers[peer_count++] = p;
} else {//we're first
prev_metric = i->metric;
peers[peer_count++] = p;
}
}
}

if (peer_count==0)
return 0;

least_recent_time = peers[0]->last_selected;
p = peers[0];
for (j=1; j<peer_count; j++) {
if (peers[j]->last_selected < least_recent_time) {
least_recent_time = peers[j]->last_selected;
p = peers[j];
}
}

p->last_selected = time(NULL);
return p;
peer* get_first_connected_route(str* session_id, routing_entry *r, int app_id, int vendor_id) {
peer * peers[LB_MAX_PEERS];
int peer_count = 0;
int prev_metric = 0;
routing_entry *i;
peer *p;
int j;
time_t least_recent_time;
cdp_session_t* cdp_session;

cdp_session = cdp_get_session(*session_id);
if (cdp_session) {
/*try and find an already used peer for this session - sticky*/
if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) {
//we have an old sticky peer. let's make sure it's up and connected before we use it.
p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn);
if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
p->last_selected = time(NULL);
if (cdp_session)
AAASessionsUnlock(cdp_session->hash);
LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s);
return p;
}
}
}


LM_DBG("get_first_connected_route in list %p for app_id %d and vendor_id %d and existing session_id is [%.*s]\n",
r, app_id, vendor_id,
session_id->len, session_id->s);
for (i = r; i; i = i->next) {
if (peer_count >= LB_MAX_PEERS)
break;
p = get_peer_by_fqdn(&(i->fqdn));
if (!p)
LM_DBG("The peer %.*s does not seem to be connected or configured\n",
i->fqdn.len, i->fqdn.s);
else
LM_DBG("The peer %.*s state is %s\n", i->fqdn.len, i->fqdn.s,
(p->state == I_Open || p->state == R_Open) ? "opened" : "closed");
if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
LM_DBG("The peer %.*s matches - will forward there\n", i->fqdn.len, i->fqdn.s);
if (peer_count != 0) {//check the metric
if (i->metric != prev_metric)
break;
//metric must be the same
peers[peer_count++] = p;
} else {//we're first
prev_metric = i->metric;
peers[peer_count++] = p;
}
}
}

if (peer_count == 0) {
if (cdp_session)
AAASessionsUnlock(cdp_session->hash);
return 0;
}

least_recent_time = peers[0]->last_selected;
p = peers[0];
for (j = 1; j < peer_count; j++) {
if (peers[j]->last_selected < least_recent_time) {
least_recent_time = peers[j]->last_selected;
p = peers[j];
}
}

if (cdp_session) {
if (cdp_session->sticky_peer_fqdn_buflen <= p->fqdn.len) {
LM_DBG("not enough storage for sticky peer - allocating more\n");
if (cdp_session->sticky_peer_fqdn.s)
shm_free(cdp_session->sticky_peer_fqdn.s);

cdp_session->sticky_peer_fqdn.s = (char*) shm_malloc(p->fqdn.len + 1);
if (!cdp_session->sticky_peer_fqdn.s) {
LM_ERR("no more shm memory\n");
AAASessionsUnlock(cdp_session->hash);
return 0;
}
cdp_session->sticky_peer_fqdn_buflen = p->fqdn.len + 1;
memset(cdp_session->sticky_peer_fqdn.s, 0, p->fqdn.len + 1);
}
cdp_session->sticky_peer_fqdn.len = p->fqdn.len;
memcpy(cdp_session->sticky_peer_fqdn.s, p->fqdn.s, p->fqdn.len);
AAASessionsUnlock(cdp_session->hash);
}
p->last_selected = time(NULL);
return p;
}

/**
Expand All @@ -135,92 +174,92 @@ peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id)
* - Then we look for the first connected peer in the default routes
* @param m - the Diameter message to find the destination peer for
* @returns - the connected peer or null if none connected found
*/
peer* get_routing_peer(AAAMessage *m)
{
str destination_realm={0,0},destination_host={0,0};
AAA_AVP *avp,*avp_vendor,*avp2;
AAA_AVP_LIST group;
peer *p;
routing_realm *rr;
int app_id=0,vendor_id=0;
LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s);

app_id = m->applicationId;
avp = AAAFindMatchingAVP(m,0,AVP_Vendor_Specific_Application_Id,0,AAA_FORWARD_SEARCH);
if (avp){
group = AAAUngroupAVPS(avp->data);
avp_vendor = AAAFindMatchingAVPList(group,group.head,AVP_Vendor_Id,0,0);
avp2 = AAAFindMatchingAVPList(group,group.head,AVP_Auth_Application_Id,0,0);
if (avp_vendor&&avp2){
vendor_id = get_4bytes(avp_vendor->data.s);
app_id = get_4bytes(avp2->data.s);
}
avp2 = AAAFindMatchingAVPList(group,group.head,AVP_Acct_Application_Id,0,0);
if (avp_vendor&&avp2){
vendor_id = get_4bytes(avp_vendor->data.s);
app_id = get_4bytes(avp2->data.s);
}
AAAFreeAVPList(&group);
}

avp_vendor = AAAFindMatchingAVP(m,0,AVP_Vendor_Id,0,AAA_FORWARD_SEARCH);
avp = AAAFindMatchingAVP(m,0,AVP_Auth_Application_Id,0,AAA_FORWARD_SEARCH);
if (avp){
if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
else vendor_id = 0;
app_id = get_4bytes(avp->data.s);
}
avp = AAAFindMatchingAVP(m,0,AVP_Acct_Application_Id,0,AAA_FORWARD_SEARCH);
if (avp){
if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
else vendor_id = 0;
app_id = get_4bytes(avp->data.s);
}
avp = AAAFindMatchingAVP(m,0,AVP_Destination_Host,0,AAA_FORWARD_SEARCH);
if (avp) destination_host = avp->data;
if (destination_host.len){
/* There is a destination host present in the message try and route directly there */
p = get_peer_by_fqdn(&destination_host);
if (p && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {
p->last_selected = time(NULL);
return p;
}
/* the destination host peer is not connected at the moment, try a normal route then */
}
avp = AAAFindMatchingAVP(m,0,AVP_Destination_Realm,0,AAA_FORWARD_SEARCH);
if (avp) destination_realm = avp->data;
if (!config->r_table) {
LM_ERR("get_routing_peer(): Empty routing table.\n");
return 0;
}
if (destination_realm.len){
/* first search for the destination realm */
for(rr=config->r_table->realms;rr;rr=rr->next)
if (rr->realm.len == destination_realm.len &&
strncasecmp(rr->realm.s,destination_realm.s,destination_realm.len)==0)
break;
if (rr) {
p = get_first_connected_route(rr->routes,app_id,vendor_id);
if (p) return p;
else LM_ERR("get_routing_peer(): No connected Route peer found for Realm <%.*s>. Trying DefaultRoutes next...\n",
destination_realm.len,destination_realm.s);
}
}
/* if not found in the realms or no destination_realm,
* get the first connected host in default routes */
LM_DBG("no routing peer found, trying default route\n");
p = get_first_connected_route(config->r_table->routes,app_id,vendor_id);
if (!p){
LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n",
app_id,vendor_id);
}
return p;
*/
peer* get_routing_peer(AAAMessage *m) {
str destination_realm = {0, 0}, destination_host = {0, 0}, session_id;
AAA_AVP *avp, *avp_vendor, *avp2;
AAA_AVP_LIST group;
peer *p;
routing_realm *rr;
int app_id = 0, vendor_id = 0;

LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s);

app_id = m->applicationId;
session_id = m->sessionId->data;
avp = AAAFindMatchingAVP(m, 0, AVP_Vendor_Specific_Application_Id, 0, AAA_FORWARD_SEARCH);
if (avp) {
group = AAAUngroupAVPS(avp->data);
avp_vendor = AAAFindMatchingAVPList(group, group.head, AVP_Vendor_Id, 0, 0);
avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Auth_Application_Id, 0, 0);
if (avp_vendor && avp2) {
vendor_id = get_4bytes(avp_vendor->data.s);
app_id = get_4bytes(avp2->data.s);
}
avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Acct_Application_Id, 0, 0);
if (avp_vendor && avp2) {
vendor_id = get_4bytes(avp_vendor->data.s);
app_id = get_4bytes(avp2->data.s);
}
AAAFreeAVPList(&group);
}

avp_vendor = AAAFindMatchingAVP(m, 0, AVP_Vendor_Id, 0, AAA_FORWARD_SEARCH);
avp = AAAFindMatchingAVP(m, 0, AVP_Auth_Application_Id, 0, AAA_FORWARD_SEARCH);
if (avp) {
if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
else vendor_id = 0;
app_id = get_4bytes(avp->data.s);
}

avp = AAAFindMatchingAVP(m, 0, AVP_Acct_Application_Id, 0, AAA_FORWARD_SEARCH);
if (avp) {
if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
else vendor_id = 0;
app_id = get_4bytes(avp->data.s);
}

avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Host, 0, AAA_FORWARD_SEARCH);
if (avp) destination_host = avp->data;

if (destination_host.len) {
/* There is a destination host present in the message try and route directly there */
p = get_peer_by_fqdn(&destination_host);
if (p && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
p->last_selected = time(NULL);
return p;
}
/* the destination host peer is not connected at the moment, try a normal route then */
}

avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH);
if (avp) destination_realm = avp->data;

if (!config->r_table) {
LM_ERR("get_routing_peer(): Empty routing table.\n");
return 0;
}

if (destination_realm.len) {
/* first search for the destination realm */
for (rr = config->r_table->realms; rr; rr = rr->next)
if (rr->realm.len == destination_realm.len &&
strncasecmp(rr->realm.s, destination_realm.s, destination_realm.len) == 0)
break;
if (rr) {
p = get_first_connected_route(&session_id, rr->routes, app_id, vendor_id);
if (p) return p;
else LM_ERR("get_routing_peer(): No connected Route peer found for Realm <%.*s>. Trying DefaultRoutes next...\n",
destination_realm.len, destination_realm.s);
}
}
/* if not found in the realms or no destination_realm,
* get the first connected host in default routes */
LM_DBG("no routing peer found, trying default route\n");
p = get_first_connected_route(&session_id, config->r_table->routes, app_id, vendor_id);
if (!p) {
LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n",
app_id, vendor_id);
}
return p;
}
4 changes: 3 additions & 1 deletion modules/cdp/session.c
Expand Up @@ -123,7 +123,9 @@ void free_session(cdp_session_t *x)

if(x->dest_host.s) shm_free(x->dest_host.s);
if(x->dest_realm.s) shm_free(x->dest_realm.s);

if (x->sticky_peer_fqdn_buflen && x->sticky_peer_fqdn.s) {
shm_free(x->sticky_peer_fqdn.s);
}
shm_free(x);
}
}
Expand Down
2 changes: 2 additions & 0 deletions modules/cdp/session.h
Expand Up @@ -230,6 +230,8 @@ typedef struct _cdp_session_t {
unsigned int vendor_id; /**< specific vendor id for this session */
cdp_session_type_t type;
str dest_host, dest_realm; /*the destination host and realm, used only for auth, for the moment*/
str sticky_peer_fqdn; /*peer that we would like to stick for for this session*/
int sticky_peer_fqdn_buflen; /*length of buffer available for sticky peer*/
union {
cdp_auth_session_t auth;
cdp_acc_session_t acc;
Expand Down

0 comments on commit cb2ad9c

Please sign in to comment.