Browse code

modules/CDP: added stickiness to loadbalancing of diameter peers

jaybeepee authored on 26/03/2015 13:40:53
Showing 3 changed files
... ...
@@ -50,7 +50,7 @@
50 50
 
51 51
 #define LB_MAX_PEERS 20			/**< maximum peers that can be loadbalanced accross i.e. same metric */
52 52
 
53
-extern dp_config *config;		/**< Configuration for this diameter peer 	*/
53
+extern dp_config *config; /**< Configuration for this diameter peer 	*/
54 54
 int gcount = 0;
55 55
 
56 56
 /**
... ...
@@ -59,15 +59,14 @@ int gcount = 0;
59 59
  * @param app_id - the application id to look for
60 60
  * @param vendor_id - the vendor id to look for, 0 if not vendor specific 
61 61
  * @returns 0 if not found, 1 if found
62
- */ 
63
-int peer_handles_application(peer *p,int app_id,int vendor_id)
64
-{
65
-	int i;
66
-	LM_DBG("Checking if peer %.*s handles application %d for vendord %d\n",p->fqdn.len,p->fqdn.s,app_id,vendor_id);
67
-	if (!p || !p->applications || !p->applications_cnt) return 0;
68
-	for(i=0;i<p->applications_cnt;i++)
69
-		if (p->applications[i].id==app_id && p->applications[i].vendor==vendor_id) return 1;		
70
-	return 0;
62
+ */
63
+int peer_handles_application(peer *p, int app_id, int vendor_id) {
64
+    int i;
65
+    LM_DBG("Checking if peer %.*s handles application %d for vendord %d\n", p->fqdn.len, p->fqdn.s, app_id, vendor_id);
66
+    if (!p || !p->applications || !p->applications_cnt) return 0;
67
+    for (i = 0; i < p->applications_cnt; i++)
68
+        if (p->applications[i].id == app_id && p->applications[i].vendor == vendor_id) return 1;
69
+    return 0;
71 70
 }
72 71
 
73 72
 /**
... ...
@@ -75,56 +74,96 @@ int peer_handles_application(peer *p,int app_id,int vendor_id)
75 75
  * @param r - the list of routing entries to look into
76 76
  * @returns - the peer or null if none connected
77 77
  */
78
-peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id)
79
-{
80
-	peer *peers[LB_MAX_PEERS];
81
-	int peer_count=0;
82
-	int prev_metric=0;
83
-	routing_entry *i;
84
-	peer *p;
85
-	int j;
86
-	time_t least_recent_time;
87
-
88
-	LM_DBG("get_first_connected_route in list %p for app_id %d and vendor_id %d\n",
89
-		r,app_id,vendor_id);
90
-	for(i=r;i;i=i->next){
91
-		if (peer_count >= LB_MAX_PEERS)
92
-			break;
93
-		p = get_peer_by_fqdn(&(i->fqdn));
94
-		if (!p)
95
-			LM_DBG("The peer %.*s does not seem to be connected or configured\n",
96
-				i->fqdn.len,i->fqdn.s);
97
-		else
98
-			LM_DBG("The peer %.*s state is %s\n",i->fqdn.len,i->fqdn.s,
99
-				(p->state==I_Open||p->state==R_Open)?"opened":"closed");
100
-		if (p && !p->disabled && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {
101
-			LM_DBG("The peer %.*s matches - will forward there\n",i->fqdn.len,i->fqdn.s);
102
-			if (peer_count!=0) {//check the metric
103
-				if (i->metric != prev_metric)
104
-					break;
105
-				//metric must be the same
106
-				peers[peer_count++] = p;
107
-			} else {//we're first
108
-				prev_metric = i->metric;
109
-				peers[peer_count++] = p;
110
-			}
111
-		}
112
-	}
113
-
114
-	if (peer_count==0)
115
-		return 0;
116
-
117
-	least_recent_time = peers[0]->last_selected;
118
-	p = peers[0];
119
-	for (j=1; j<peer_count; j++) {
120
-		if (peers[j]->last_selected < least_recent_time) {
121
-			least_recent_time = peers[j]->last_selected;
122
-			p = peers[j];
123
-		}
124
-	}
125
-
126
-	p->last_selected = time(NULL);
127
-	return p;
78
+peer* get_first_connected_route(str* session_id, routing_entry *r, int app_id, int vendor_id) {
79
+    peer * peers[LB_MAX_PEERS];
80
+    int peer_count = 0;
81
+    int prev_metric = 0;
82
+    routing_entry *i;
83
+    peer *p;
84
+    int j;
85
+    time_t least_recent_time;
86
+    cdp_session_t* cdp_session;
87
+
88
+    cdp_session = cdp_get_session(*session_id);
89
+    if (cdp_session) {
90
+        /*try and find an already used peer for this session - sticky*/
91
+        if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) {
92
+            //we have an old sticky peer. let's make sure it's up and connected before we use it.
93
+            p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn);
94
+            if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
95
+                p->last_selected = time(NULL);
96
+                if (cdp_session)
97
+                    AAASessionsUnlock(cdp_session->hash);
98
+                LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s);
99
+                return p;
100
+            }
101
+        }
102
+    }
103
+
104
+
105
+    LM_DBG("get_first_connected_route in list %p for app_id %d and vendor_id %d and existing session_id is [%.*s]\n",
106
+            r, app_id, vendor_id,
107
+            session_id->len, session_id->s);
108
+    for (i = r; i; i = i->next) {
109
+        if (peer_count >= LB_MAX_PEERS)
110
+            break;
111
+        p = get_peer_by_fqdn(&(i->fqdn));
112
+        if (!p)
113
+            LM_DBG("The peer %.*s does not seem to be connected or configured\n",
114
+                i->fqdn.len, i->fqdn.s);
115
+        else
116
+            LM_DBG("The peer %.*s state is %s\n", i->fqdn.len, i->fqdn.s,
117
+                (p->state == I_Open || p->state == R_Open) ? "opened" : "closed");
118
+        if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
119
+            LM_DBG("The peer %.*s matches - will forward there\n", i->fqdn.len, i->fqdn.s);
120
+            if (peer_count != 0) {//check the metric
121
+                if (i->metric != prev_metric)
122
+                    break;
123
+                //metric must be the same
124
+                peers[peer_count++] = p;
125
+            } else {//we're first
126
+                prev_metric = i->metric;
127
+                peers[peer_count++] = p;
128
+            }
129
+        }
130
+    }
131
+
132
+    if (peer_count == 0) {
133
+        if (cdp_session)
134
+            AAASessionsUnlock(cdp_session->hash);
135
+        return 0;
136
+    }
137
+
138
+    least_recent_time = peers[0]->last_selected;
139
+    p = peers[0];
140
+    for (j = 1; j < peer_count; j++) {
141
+        if (peers[j]->last_selected < least_recent_time) {
142
+            least_recent_time = peers[j]->last_selected;
143
+            p = peers[j];
144
+        }
145
+    }
146
+
147
+    if (cdp_session) {
148
+        if (cdp_session->sticky_peer_fqdn_buflen <= p->fqdn.len) {
149
+            LM_DBG("not enough storage for sticky peer - allocating more\n");
150
+            if (cdp_session->sticky_peer_fqdn.s)
151
+                shm_free(cdp_session->sticky_peer_fqdn.s);
152
+
153
+            cdp_session->sticky_peer_fqdn.s = (char*) shm_malloc(p->fqdn.len + 1);
154
+            if (!cdp_session->sticky_peer_fqdn.s) {
155
+                LM_ERR("no more shm memory\n");
156
+                AAASessionsUnlock(cdp_session->hash);
157
+                return 0;
158
+            }
159
+            cdp_session->sticky_peer_fqdn_buflen = p->fqdn.len + 1;
160
+            memset(cdp_session->sticky_peer_fqdn.s, 0, p->fqdn.len + 1);
161
+        }
162
+        cdp_session->sticky_peer_fqdn.len = p->fqdn.len;
163
+        memcpy(cdp_session->sticky_peer_fqdn.s, p->fqdn.s, p->fqdn.len);
164
+        AAASessionsUnlock(cdp_session->hash);
165
+    }
166
+    p->last_selected = time(NULL);
167
+    return p;
128 168
 }
129 169
 
130 170
 /**
... ...
@@ -135,92 +174,92 @@ peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id)
135 135
  * - Then we look for the first connected peer in the default routes
136 136
  * @param m - the Diameter message to find the destination peer for
137 137
  * @returns - the connected peer or null if none connected found
138
- */  
139
-peer* get_routing_peer(AAAMessage *m)
140
-{
141
-	str destination_realm={0,0},destination_host={0,0};
142
-	AAA_AVP *avp,*avp_vendor,*avp2;
143
-	AAA_AVP_LIST group;	
144
-	peer *p;
145
-	routing_realm *rr;
146
-	int app_id=0,vendor_id=0;
147
-	
148
-	LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s);
149
-
150
-	app_id = m->applicationId;	
151
-	avp = AAAFindMatchingAVP(m,0,AVP_Vendor_Specific_Application_Id,0,AAA_FORWARD_SEARCH);
152
-	if (avp){
153
-		group = AAAUngroupAVPS(avp->data);
154
-		avp_vendor = AAAFindMatchingAVPList(group,group.head,AVP_Vendor_Id,0,0);				
155
-		avp2 = AAAFindMatchingAVPList(group,group.head,AVP_Auth_Application_Id,0,0);				
156
-		if (avp_vendor&&avp2){
157
-			vendor_id = get_4bytes(avp_vendor->data.s);
158
-			app_id = get_4bytes(avp2->data.s);
159
-		}
160
-		avp2 = AAAFindMatchingAVPList(group,group.head,AVP_Acct_Application_Id,0,0);				
161
-		if (avp_vendor&&avp2){
162
-			vendor_id = get_4bytes(avp_vendor->data.s);
163
-			app_id = get_4bytes(avp2->data.s);
164
-		}
165
-		AAAFreeAVPList(&group);
166
-	}
167
-
168
-	avp_vendor = AAAFindMatchingAVP(m,0,AVP_Vendor_Id,0,AAA_FORWARD_SEARCH);				
169
-	avp = AAAFindMatchingAVP(m,0,AVP_Auth_Application_Id,0,AAA_FORWARD_SEARCH);
170
-	if (avp){
171
-		if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
172
-		else vendor_id = 0;
173
-		app_id = get_4bytes(avp->data.s);		
174
-	}
175
-	
176
-	avp = AAAFindMatchingAVP(m,0,AVP_Acct_Application_Id,0,AAA_FORWARD_SEARCH);
177
-	if (avp){
178
-		if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
179
-		else vendor_id = 0;
180
-		app_id = get_4bytes(avp->data.s);		
181
-	}
182
-	
183
-	avp = AAAFindMatchingAVP(m,0,AVP_Destination_Host,0,AAA_FORWARD_SEARCH);
184
-	if (avp) destination_host = avp->data;
185
-	
186
-	if (destination_host.len){
187
-		/* There is a destination host present in the message try and route directly there */
188
-		p = get_peer_by_fqdn(&destination_host);
189
-		if (p && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) {
190
-			p->last_selected = time(NULL);
191
-			return p;
192
-		}
193
-		/* the destination host peer is not connected at the moment, try a normal route then */
194
-	}
195
-	
196
-	avp = AAAFindMatchingAVP(m,0,AVP_Destination_Realm,0,AAA_FORWARD_SEARCH);
197
-	if (avp) destination_realm = avp->data;
198
-	
199
-	if (!config->r_table) {
200
-		LM_ERR("get_routing_peer(): Empty routing table.\n");
201
-		return 0;
202
-	}
203
-	
204
-	if (destination_realm.len){
205
-		/* first search for the destination realm */
206
-		for(rr=config->r_table->realms;rr;rr=rr->next)
207
-			if (rr->realm.len == destination_realm.len &&
208
-				strncasecmp(rr->realm.s,destination_realm.s,destination_realm.len)==0)
209
-					break;
210
-		if (rr) {
211
-			p = get_first_connected_route(rr->routes,app_id,vendor_id);
212
-			if (p) return p;
213
-			else LM_ERR("get_routing_peer(): No connected Route peer found for Realm <%.*s>. Trying DefaultRoutes next...\n",
214
-					destination_realm.len,destination_realm.s);
215
-		}	 
216
-	}
217
-	/* if not found in the realms or no destination_realm, 
218
-	 * get the first connected host in default routes */
219
-	LM_DBG("no routing peer found, trying default route\n");
220
-	p = get_first_connected_route(config->r_table->routes,app_id,vendor_id);
221
-	if (!p){
222
-		LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n",
223
-				app_id,vendor_id);
224
-	}
225
-	return p;
138
+ */
139
+peer* get_routing_peer(AAAMessage *m) {
140
+    str destination_realm = {0, 0}, destination_host = {0, 0}, session_id;
141
+    AAA_AVP *avp, *avp_vendor, *avp2;
142
+    AAA_AVP_LIST group;
143
+    peer *p;
144
+    routing_realm *rr;
145
+    int app_id = 0, vendor_id = 0;
146
+
147
+    LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s);
148
+
149
+    app_id = m->applicationId;
150
+    session_id = m->sessionId->data;
151
+    avp = AAAFindMatchingAVP(m, 0, AVP_Vendor_Specific_Application_Id, 0, AAA_FORWARD_SEARCH);
152
+    if (avp) {
153
+        group = AAAUngroupAVPS(avp->data);
154
+        avp_vendor = AAAFindMatchingAVPList(group, group.head, AVP_Vendor_Id, 0, 0);
155
+        avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Auth_Application_Id, 0, 0);
156
+        if (avp_vendor && avp2) {
157
+            vendor_id = get_4bytes(avp_vendor->data.s);
158
+            app_id = get_4bytes(avp2->data.s);
159
+        }
160
+        avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Acct_Application_Id, 0, 0);
161
+        if (avp_vendor && avp2) {
162
+            vendor_id = get_4bytes(avp_vendor->data.s);
163
+            app_id = get_4bytes(avp2->data.s);
164
+        }
165
+        AAAFreeAVPList(&group);
166
+    }
167
+
168
+    avp_vendor = AAAFindMatchingAVP(m, 0, AVP_Vendor_Id, 0, AAA_FORWARD_SEARCH);
169
+    avp = AAAFindMatchingAVP(m, 0, AVP_Auth_Application_Id, 0, AAA_FORWARD_SEARCH);
170
+    if (avp) {
171
+        if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
172
+        else vendor_id = 0;
173
+        app_id = get_4bytes(avp->data.s);
174
+    }
175
+
176
+    avp = AAAFindMatchingAVP(m, 0, AVP_Acct_Application_Id, 0, AAA_FORWARD_SEARCH);
177
+    if (avp) {
178
+        if (avp_vendor) vendor_id = get_4bytes(avp_vendor->data.s);
179
+        else vendor_id = 0;
180
+        app_id = get_4bytes(avp->data.s);
181
+    }
182
+
183
+    avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Host, 0, AAA_FORWARD_SEARCH);
184
+    if (avp) destination_host = avp->data;
185
+
186
+    if (destination_host.len) {
187
+        /* There is a destination host present in the message try and route directly there */
188
+        p = get_peer_by_fqdn(&destination_host);
189
+        if (p && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
190
+            p->last_selected = time(NULL);
191
+            return p;
192
+        }
193
+        /* the destination host peer is not connected at the moment, try a normal route then */
194
+    }
195
+
196
+    avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH);
197
+    if (avp) destination_realm = avp->data;
198
+
199
+    if (!config->r_table) {
200
+        LM_ERR("get_routing_peer(): Empty routing table.\n");
201
+        return 0;
202
+    }
203
+
204
+    if (destination_realm.len) {
205
+        /* first search for the destination realm */
206
+        for (rr = config->r_table->realms; rr; rr = rr->next)
207
+            if (rr->realm.len == destination_realm.len &&
208
+                    strncasecmp(rr->realm.s, destination_realm.s, destination_realm.len) == 0)
209
+                break;
210
+        if (rr) {
211
+            p = get_first_connected_route(&session_id, rr->routes, app_id, vendor_id);
212
+            if (p) return p;
213
+            else LM_ERR("get_routing_peer(): No connected Route peer found for Realm <%.*s>. Trying DefaultRoutes next...\n",
214
+                    destination_realm.len, destination_realm.s);
215
+        }
216
+    }
217
+    /* if not found in the realms or no destination_realm, 
218
+     * get the first connected host in default routes */
219
+    LM_DBG("no routing peer found, trying default route\n");
220
+    p = get_first_connected_route(&session_id, config->r_table->routes, app_id, vendor_id);
221
+    if (!p) {
222
+        LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n",
223
+                app_id, vendor_id);
224
+    }
225
+    return p;
226 226
 }
... ...
@@ -123,7 +123,9 @@ void free_session(cdp_session_t *x)
123 123
 
124 124
 		if(x->dest_host.s) shm_free(x->dest_host.s);
125 125
 		if(x->dest_realm.s) shm_free(x->dest_realm.s);
126
-
126
+                if (x->sticky_peer_fqdn_buflen && x->sticky_peer_fqdn.s) {
127
+                    shm_free(x->sticky_peer_fqdn.s);
128
+                }
127 129
 		shm_free(x);
128 130
 	}
129 131
 }
... ...
@@ -230,6 +230,8 @@ typedef struct _cdp_session_t {
230 230
 	unsigned int vendor_id;				/**< specific vendor id for this session */
231 231
 	cdp_session_type_t type;
232 232
 	str dest_host, dest_realm; 			/*the destination host and realm, used only for auth, for the moment*/
233
+        str sticky_peer_fqdn;                           /*peer that we would like to stick for for this session*/
234
+        int sticky_peer_fqdn_buflen;                     /*length of buffer available for sticky peer*/
233 235
 	union {
234 236
 		cdp_auth_session_t auth;
235 237
 		cdp_acc_session_t acc;