瀏覽代碼

modules/CDP: added stickiness to loadbalancing of diameter peers

jaybeepee 10 年之前
父節點
當前提交
cb2ad9c003
共有 3 個文件被更改,包括 192 次插入149 次删除
  1. 187 148
      modules/cdp/routing.c
  2. 3 1
      modules/cdp/session.c
  3. 2 0
      modules/cdp/session.h

+ 187 - 148
modules/cdp/routing.c

@@ -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;
 
 /**
@@ -59,15 +59,14 @@ 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;
 }
 
 /**
@@ -75,56 +74,96 @@ int peer_handles_application(peer *p,int app_id,int vendor_id)
  * @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;
 }
 
 /**
@@ -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;
 }

+ 3 - 1
modules/cdp/session.c

@@ -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);
 	}
 }

+ 2 - 0
modules/cdp/session.h

@@ -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;