Browse code

lcr: improve the search for GW when both IP address and src_port are used

It has been noticed, that after a list of contributions into lcr:
* 14e6fc80b3d2389567c73c4a2196bf8e6d92d8d2
* d8583d6ce1748c1ac8494616fced507b01dd4375
* 470fd5b8bedca56efcc5e6aa0225089fe3857ac9
* aa8d3ed4fe20efbd22db3b0b01a655789afa8818
the gateways matching when a search is being done based on
the IP address and the src_port (through the list of GWs with the same IP)
works, but not fully correct.

It is only related to the usage with the third parameter 'src_port',
when calling from_gw() and from_any_gw(), and, only when the search
is done through the gateways list, which have the same IP address
(but different ports). If gateways have different IPs, the issue
is not catchable.

The problem is in the algorithm used for matching based on
two objects (ip_addr and src_port) - the binary search.
It's not fully suitable for a search based on two (or more) objects.

The binary works completely fine, when only one object is used for searching,
but works not fully correct when the search is based on a comparison
of two (or more) objects. A division happening based on the value of
the first object, gives a chance that the second object will never
be found in the current half being looked up.

This commit concerns switching to a full cycling through the list of
gateways of the same lcr_id, and gives a proper work of the do_from_gw().

The slight drawback of the new method is that we have to do a cycling
through the whole list of GWs of the same lcr_id, but on the other hand
it is much more efficient than trying to build up a matching using binary
based on two objects (ip_addr and src_port) being used for comparison.

Donat Zenichev authored on 08/10/2021 13:18:47 • Victor Seva committed on 08/10/2021 17:01:07
Showing 1 changed files
... ...
@@ -922,28 +922,24 @@ static int comp_gws(const void *_g1, const void *_g2)
922 922
 }
923 923
 
924 924
 /*
925
- * Compare gateways based on their IP address and port
925
+ * Compare a gateway using IP address and the src port
926 926
  */
927
-static int comp_gws_include_port(const void *_g1, const void *_g2)
928
-{
929
-	struct gw_info *g1 = (struct gw_info *)_g1;
930
-	struct gw_info *g2 = (struct gw_info *)_g2;
931
-
932
-	/* first address family comparison */
933
-	if(g1->ip_addr.af < g2->ip_addr.af)
934
-		return -1;
935
-	if(g1->ip_addr.af > g2->ip_addr.af)
936
-		return 1;
937
-	if(g1->ip_addr.len < g2->ip_addr.len)
938
-		return -1;
939
-	if(g1->ip_addr.len > g2->ip_addr.len)
940
-		return 1;
941
-
942
-	/* secondly ports comparison */
943
-	if(g1->port != g2->port)
944
-		return -1;
927
+static struct gw_info * find_gateway_by_ip_and_port(struct gw_info * gw, struct gw_info * gws) {
928
+	int tmp = 0, gw_index = 0;
929
+
930
+	for (int i = 1; i <= gws[0].ip_addr.u.addr32[0]; i++) {
931
+		tmp = memcmp(gws[i].ip_addr.u.addr, gw->ip_addr.u.addr, gws[i].ip_addr.len);
932
+		if (gws[i].ip_addr.af == gw->ip_addr.af &&
933
+			gws[i].ip_addr.len == gw->ip_addr.len &&
934
+			tmp == 0 &&	/* a comparison of the IP address value */
935
+			gws[i].port == gw->port) {
936
+				gw_index = i;
937
+				break;
938
+		}
939
+	}
940
+	if (gw_index != 0) return &(gws[gw_index]);
945 941
 
946
-	return memcmp(g1->ip_addr.u.addr, g2->ip_addr.u.addr, g1->ip_addr.len);
942
+	return NULL;
947 943
 }
948 944
 
949 945
 /* 
... ...
@@ -3025,8 +3021,7 @@ static int do_from_gw(struct sip_msg *_m, unsigned int lcr_id,
3025 3021
 	if (src_port != 0) {
3026 3022
 		/* Search for gw based on its ip address and port */
3027 3023
 		gw.port = src_port;
3028
-		res = (struct gw_info *)bsearch(&gw, &(gws[1]), gws[0].ip_addr.u.addr32[0],
3029
-				sizeof(struct gw_info), comp_gws_include_port);
3024
+		res = find_gateway_by_ip_and_port(&gw, gws);
3030 3025
 	} else {
3031 3026
 		/* Search for gw based on its ip address */
3032 3027
 		res = (struct gw_info *)bsearch(&gw, &(gws[1]), gws[0].ip_addr.u.addr32[0],