Browse code

dispatcher: new function ds_select_routing(rules, mode, [limit])

- select target addresses from a combination of groups and algorithms,
control where the first destination is pushed and optionally set a limit

Daniel-Constantin Mierla authored on 19/06/2018 16:34:00
Showing 3 changed files
... ...
@@ -1935,19 +1935,60 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
1935 1935
 
1936 1936
 /**
1937 1937
  * Set destination address from group 'set' selected with alogorithm 'alg'
1938
- * - the rest of addresses in group are added as next destination in avps,
1938
+ * - the rest of addresses in group are added as next destination in xavps,
1939 1939
  *   up to the 'limit'
1940 1940
  * - mode specify to set address in R-URI or outboud proxy
1941 1941
  *
1942 1942
  */
1943
-int ds_select_dst_limit(
1944
-		sip_msg_t *msg, int set, int alg, unsigned int limit, int mode)
1943
+int ds_select_dst_limit(sip_msg_t *msg, int set, int alg, uint32_t limit,
1944
+		int mode)
1945 1945
 {
1946
-	int i, cnt;
1946
+	int ret;
1947
+	sr_xval_t nxval;
1948
+	ds_select_state_t vstate;
1949
+
1950
+	memset(&vstate, 0, sizeof(ds_select_state_t));
1951
+	vstate.setid = set;
1952
+	vstate.alg = alg;
1953
+	vstate.umode = mode;
1954
+	vstate.limit = limit;
1955
+
1956
+	if(vstate.limit == 0) {
1957
+		LM_DBG("Limit set to 0 - forcing to unlimited\n");
1958
+		vstate.limit = 0xffffffff;
1959
+	}
1960
+
1961
+	ret = ds_manage_routes(msg, &vstate);
1962
+	if(ret<0) {
1963
+		return ret;
1964
+	}
1965
+
1966
+	/* add cnt value to xavp */
1967
+	if(((ds_xavp_ctx_mode & DS_XAVP_CTX_SKIP_CNT)==0)
1968
+			&& (ds_xavp_ctx.len >= 0)) {
1969
+		/* add to xavp the number of selected dst records */
1970
+		memset(&nxval, 0, sizeof(sr_xval_t));
1971
+		nxval.type = SR_XTYPE_INT;
1972
+		nxval.v.i = vstate.cnt;
1973
+		if(xavp_add_xavp_value(&ds_xavp_ctx, &ds_xavp_ctx_cnt, &nxval, NULL)==NULL) {
1974
+			LM_ERR("failed to add cnt value to xavp\n");
1975
+			return -1;
1976
+		}
1977
+	}
1978
+
1979
+	LM_DBG("selected target destinations: %d\n", vstate.cnt);
1980
+
1981
+	return ret;
1982
+}
1983
+
1984
+/**
1985
+ *
1986
+ */
1987
+int ds_manage_routes(sip_msg_t *msg, ds_select_state_t *rstate)
1988
+{
1989
+	int i;
1947 1990
 	unsigned int hash;
1948 1991
 	ds_set_t *idx = NULL;
1949
-	sr_xval_t nxval;
1950
-	sr_xavp_t *pxavp = NULL;
1951 1992
 
1952 1993
 	if(msg == NULL) {
1953 1994
 		LM_ERR("bad parameters\n");
... ...
@@ -1959,13 +2000,7 @@ int ds_select_dst_limit(
1959 2000
 		return -1;
1960 2001
 	}
1961 2002
 
1962
-	if(limit == 0) {
1963
-		LM_DBG("Limit set to 0 - forcing to unlimited\n");
1964
-		limit = 0xffffffff;
1965
-	}
1966
-	--limit; /* reserving 1 slot for selected dst */
1967
-
1968
-	if((mode == DS_SETOP_DSTURI) && (ds_force_dst == 0)
2003
+	if((rstate->umode == DS_SETOP_DSTURI) && (ds_force_dst == 0)
1969 2004
 			&& (msg->dst_uri.s != NULL || msg->dst_uri.len > 0)) {
1970 2005
 		LM_ERR("destination already set [%.*s]\n", msg->dst_uri.len,
1971 2006
 				msg->dst_uri.s);
... ...
@@ -1974,15 +2009,15 @@ int ds_select_dst_limit(
1974 2009
 
1975 2010
 
1976 2011
 	/* get the index of the set */
1977
-	if(ds_get_index(set, *crt_idx, &idx) != 0) {
1978
-		LM_ERR("destination set [%d] not found\n", set);
2012
+	if(ds_get_index(rstate->setid, *crt_idx, &idx) != 0) {
2013
+		LM_ERR("destination set [%d] not found\n", rstate->setid);
1979 2014
 		return -1;
1980 2015
 	}
1981 2016
 
1982
-	LM_DBG("set [%d]\n", set);
2017
+	LM_DBG("set [%d]\n", rstate->setid);
1983 2018
 
1984 2019
 	hash = 0;
1985
-	switch(alg) {
2020
+	switch(rstate->alg) {
1986 2021
 		case DS_ALG_HASHCALLID: /* 0 - hash call-id */
1987 2022
 			if(ds_hash_callid(msg, &hash) != 0) {
1988 2023
 				LM_ERR("can't get callid hash\n");
... ...
@@ -2048,14 +2083,14 @@ int ds_select_dst_limit(
2048 2083
 			if(msg->first_line.u.request.method_value != METHOD_INVITE) {
2049 2084
 				/* use first entry */
2050 2085
 				hash = 0;
2051
-				alg = 0;
2086
+				rstate->alg = 0;
2052 2087
 				break;
2053 2088
 			}
2054 2089
 			if(ds_xavp_dst.len <= 0) {
2055 2090
 				LM_ERR("no dst xavp for load distribution"
2056 2091
 					   " - using first entry...\n");
2057 2092
 				hash = 0;
2058
-				alg = 0;
2093
+				rstate->alg = 0;
2059 2094
 			} else {
2060 2095
 				i = ds_get_leastloaded(idx);
2061 2096
 				if(i < 0) {
... ...
@@ -2063,10 +2098,10 @@ int ds_select_dst_limit(
2063 2098
 					return -1;
2064 2099
 				}
2065 2100
 				hash = i;
2066
-				if(ds_load_add(msg, idx, set, hash) < 0) {
2101
+				if(ds_load_add(msg, idx, rstate->setid, hash) < 0) {
2067 2102
 					LM_ERR("unable to update destination load"
2068 2103
 						   " - classic dispatching\n");
2069
-					alg = 0;
2104
+					rstate->alg = 0;
2070 2105
 				}
2071 2106
 			}
2072 2107
 			break;
... ...
@@ -2078,12 +2113,12 @@ int ds_select_dst_limit(
2078 2113
 			hash = 0;
2079 2114
 			break;
2080 2115
 		default:
2081
-			LM_WARN("algo %d not implemented - using first entry...\n", alg);
2116
+			LM_WARN("algo %d not implemented - using first entry...\n",
2117
+					rstate->alg);
2082 2118
 			hash = 0;
2083 2119
 	}
2084 2120
 
2085
-	LM_DBG("alg hash [%u]\n", hash);
2086
-	cnt = 0;
2121
+	LM_DBG("using alg [%d] hash [%u]\n", rstate->alg, hash);
2087 2122
 
2088 2123
 	if(ds_use_default != 0 && idx->nr != 1)
2089 2124
 		hash = hash % (idx->nr - 1);
... ...
@@ -2112,23 +2147,21 @@ int ds_select_dst_limit(
2112 2147
 
2113 2148
 	hash = i;
2114 2149
 
2115
-	if(mode!=DS_SETOP_XAVP) {
2150
+	if(rstate->umode!=DS_SETOP_XAVP) {
2116 2151
 		if(ds_push_dst(msg, &idx->dlist[hash].uri, idx->dlist[hash].sock,
2117
-					mode) != 0) {
2152
+					rstate->umode) != 0) {
2118 2153
 			LM_ERR("cannot set next hop address with: %.*s\n",
2119 2154
 					idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
2120 2155
 			return -1;
2121 2156
 		}
2122 2157
 	}
2123
-	/* if alg is round-robin then update the shortcut to next to be used */
2124
-	if(alg == DS_ALG_ROUNDROBIN)
2125
-		idx->last = (hash + 1) % idx->nr;
2126 2158
 
2127
-	LM_DBG("selected [%d-%d/%d] <%.*s>\n", alg, set, hash,
2159
+	LM_DBG("selected [%d-%d-%d/%d] <%.*s>\n", rstate->alg, rstate->setid,
2160
+			rstate->umode, hash,
2128 2161
 			idx->dlist[hash].uri.len, idx->dlist[hash].uri.s);
2129 2162
 
2130
-	if(alg == DS_ALG_PARALLEL) {
2131
-		if(ds_add_branches(msg, idx, hash, mode)<0) {
2163
+	if(rstate->alg == DS_ALG_PARALLEL) {
2164
+		if(ds_add_branches(msg, idx, hash, rstate->umode)<0) {
2132 2165
 			LM_ERR("failed to add additional branches\n");
2133 2166
 			/* one destination was already set - return success anyhow */
2134 2167
 			return 2;
... ...
@@ -2144,70 +2177,67 @@ int ds_select_dst_limit(
2144 2177
 		return 1;
2145 2178
 	}
2146 2179
 
2147
-	LM_DBG("using first entry [%d/%d]\n", set, hash);
2148
-	if(ds_add_xavp_record(idx, hash, set, alg, &pxavp)<0) {
2149
-		LM_ERR("failed to add destination in the xavp (%d/%d)\n", hash, set);
2180
+	LM_DBG("using first entry [%d/%d]\n", rstate->setid, hash);
2181
+	if(ds_add_xavp_record(idx, hash, rstate->setid, rstate->alg,
2182
+				&rstate->lxavp)<0) {
2183
+		LM_ERR("failed to add destination in the xavp (%d/%d)\n",
2184
+				hash, rstate->setid);
2150 2185
 		return -1;
2151 2186
 	}
2187
+	rstate->cnt++;
2152 2188
 
2153 2189
 	/* add to xavp the destinations after the selected one */
2154
-	for(i = hash + 1; i < idx->nr && cnt < limit; i++) {
2190
+	for(i = hash + 1; i < idx->nr && rstate->cnt < rstate->limit; i++) {
2155 2191
 		if(ds_skip_dst(idx->dlist[i].flags)
2156 2192
 				|| (ds_use_default != 0 && i == (idx->nr - 1))) {
2157 2193
 			continue;
2158 2194
 		}
2159 2195
 		/* max load exceeded per destination */
2160
-		if(alg == DS_ALG_CALLLOAD
2196
+		if(rstate->alg == DS_ALG_CALLLOAD
2161 2197
 				&& idx->dlist[i].dload >= idx->dlist[i].attrs.maxload) {
2162 2198
 			continue;
2163 2199
 		}
2164
-		LM_DBG("using entry [%d/%d]\n", set, i);
2165
-		if(ds_add_xavp_record(idx, i, set, alg, &pxavp)<0) {
2166
-			LM_ERR("failed to add destination in the xavp (%d/%d)\n", i, set);
2200
+		LM_DBG("using entry [%d/%d]\n", rstate->setid, i);
2201
+		if(ds_add_xavp_record(idx, i, rstate->setid, rstate->alg,
2202
+					&rstate->lxavp)<0) {
2203
+			LM_ERR("failed to add destination in the xavp (%d/%d)\n",
2204
+					i, rstate->setid);
2167 2205
 			return -1;
2168 2206
 		}
2169
-		cnt++;
2207
+		rstate->cnt++;
2170 2208
 	}
2171 2209
 
2172 2210
 	/* add to xavp the destinations before the selected one */
2173
-	for(i = 0; i < hash && cnt < limit; i++) {
2211
+	for(i = 0; i < hash && rstate->cnt < rstate->limit; i++) {
2174 2212
 		if(ds_skip_dst(idx->dlist[i].flags)
2175 2213
 				|| (ds_use_default != 0 && i == (idx->nr - 1))) {
2176 2214
 			continue;
2177 2215
 		}
2178 2216
 		/* max load exceeded per destination */
2179
-		if(alg == DS_ALG_CALLLOAD
2217
+		if(rstate->alg == DS_ALG_CALLLOAD
2180 2218
 				&& idx->dlist[i].dload >= idx->dlist[i].attrs.maxload) {
2181 2219
 			continue;
2182 2220
 		}
2183
-		LM_DBG("using entry [%d/%d]\n", set, i);
2184
-		if(ds_add_xavp_record(idx, i, set, alg, &pxavp)<0) {
2185
-			LM_ERR("failed to add destination in the xavp (%d/%d)\n", i, set);
2221
+		LM_DBG("using entry [%d/%d]\n", rstate->setid, i);
2222
+		if(ds_add_xavp_record(idx, i, rstate->setid, rstate->alg,
2223
+					&rstate->lxavp)<0) {
2224
+			LM_ERR("failed to add destination in the xavp (%d/%d)\n",
2225
+					i, rstate->setid);
2186 2226
 			return -1;
2187 2227
 		}
2188
-		cnt++;
2228
+		rstate->cnt++;
2189 2229
 	}
2190 2230
 
2191 2231
 	/* add default dst to last position in XAVP list */
2192
-	if(ds_use_default != 0 && hash != idx->nr - 1 && cnt < limit) {
2193
-		LM_DBG("using default entry [%d/%d]\n", set, idx->nr - 1);
2194
-		if(ds_add_xavp_record(idx, idx->nr - 1, set, alg, &pxavp)<0) {
2232
+	if(ds_use_default != 0 && hash != idx->nr - 1
2233
+				&& rstate->cnt < rstate->limit) {
2234
+		LM_DBG("using default entry [%d/%d]\n", rstate->setid, idx->nr - 1);
2235
+		if(ds_add_xavp_record(idx, idx->nr - 1, rstate->setid, rstate->alg,
2236
+					&rstate->lxavp)<0) {
2195 2237
 			LM_ERR("failed to add default destination in the xavp\n");
2196 2238
 			return -1;
2197 2239
 		}
2198
-		cnt++;
2199
-	}
2200
-
2201
-	if(((ds_xavp_ctx_mode & DS_XAVP_CTX_SKIP_CNT)==0)
2202
-			&& (ds_xavp_ctx.len >= 0)) {
2203
-		/* add to xavp the number of selected dst records */
2204
-		memset(&nxval, 0, sizeof(sr_xval_t));
2205
-		nxval.type = SR_XTYPE_INT;
2206
-		nxval.v.i = cnt;
2207
-		if(xavp_add_xavp_value(&ds_xavp_ctx, &ds_xavp_ctx_cnt, &nxval, NULL)==NULL) {
2208
-			LM_ERR("failed to add cnt value to xavp\n");
2209
-			return -1;
2210
-		}
2240
+		rstate->cnt++;
2211 2241
 	}
2212 2242
 
2213 2243
 	return 1;
... ...
@@ -30,6 +30,7 @@
30 30
 
31 31
 #include <stdio.h>
32 32
 #include "../../core/pvar.h"
33
+#include "../../core/xavp.h"
33 34
 #include "../../core/parser/msg_parser.h"
34 35
 #include "../../core/rand/kam_rand.h"
35 36
 #include "../../modules/tm/tm_load.h"
... ...
@@ -121,8 +122,8 @@ void ds_disconnect_db(void);
121 122
 int ds_load_db(void);
122 123
 int ds_reload_db(void);
123 124
 int ds_destroy_list(void);
124
-int ds_select_dst_limit(
125
-		struct sip_msg *msg, int set, int alg, unsigned int limit, int mode);
125
+int ds_select_dst_limit(sip_msg_t *msg, int set, int alg, uint32_t limit,
126
+		int mode);
126 127
 int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode);
127 128
 int ds_update_dst(struct sip_msg *msg, int upos, int mode);
128 129
 int ds_update_state(sip_msg_t *msg, int group, str *address, int state);
... ...
@@ -210,6 +211,16 @@ typedef struct _ds_set {
210 211
 	int longer;
211 212
 	gen_lock_t lock;
212 213
 } ds_set_t;
214
+
215
+typedef struct _ds_select_state {
216
+	int setid;
217
+	int alg;
218
+	int umode;
219
+	uint32_t limit;
220
+	int cnt;
221
+	sr_xavp_t *lxavp;
222
+} ds_select_state_t;
223
+
213 224
 /* clang-format on */
214 225
 
215 226
 #define AVL_LEFT 0
... ...
@@ -229,4 +240,6 @@ ds_set_t *ds_avl_insert(ds_set_t **root, int id, int *setn);
229 240
 ds_set_t *ds_avl_find(ds_set_t *node, int id);
230 241
 void ds_avl_destroy(ds_set_t **node);
231 242
 
243
+int ds_manage_routes(sip_msg_t *msg, ds_select_state_t *rstate);
244
+
232 245
 #endif
... ...
@@ -153,6 +153,8 @@ static int w_ds_select_dst(struct sip_msg*, char*, char*);
153 153
 static int w_ds_select_dst_limit(struct sip_msg*, char*, char*, char*);
154 154
 static int w_ds_select_domain(struct sip_msg*, char*, char*);
155 155
 static int w_ds_select_domain_limit(struct sip_msg*, char*, char*, char*);
156
+static int w_ds_select_routes(sip_msg_t*, char*, char*);
157
+static int w_ds_select_routes_limit(sip_msg_t*, char*, char*, char*);
156 158
 static int w_ds_next_dst(struct sip_msg*, char*, char*);
157 159
 static int w_ds_next_domain(struct sip_msg*, char*, char*);
158 160
 static int w_ds_set_dst(struct sip_msg*, char*, char*);
... ...
@@ -189,6 +191,10 @@ static cmd_export_t cmds[]={
189 191
 		fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
190 192
 	{"ds_select_domain", (cmd_function)w_ds_select_domain_limit, 3,
191 193
 		fixup_igp_all, 0, REQUEST_ROUTE|FAILURE_ROUTE},
194
+	{"ds_select_routes", (cmd_function)w_ds_select_routes, 2,
195
+		fixup_spve_spve, 0, REQUEST_ROUTE|FAILURE_ROUTE},
196
+	{"ds_select_routes", (cmd_function)w_ds_select_routes_limit, 3,
197
+		fixup_spve_spve_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
192 198
 	{"ds_next_dst",      (cmd_function)w_ds_next_dst,      0,
193 199
 		ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
194 200
 	{"ds_next_domain",   (cmd_function)w_ds_next_domain,   0,
... ...
@@ -619,6 +625,167 @@ static int w_ds_select_domain_limit(
619 625
 			DS_SETOP_RURI /*set host port*/);
620 626
 }
621 627
 
628
+/**
629
+ *
630
+ */
631
+static int ki_ds_select_routes_limit(sip_msg_t *msg, str *srules, str *smode,
632
+		int rlimit)
633
+{
634
+	int i;
635
+	int vret;
636
+	int gret;
637
+	int vfirst;
638
+	sr_xval_t nxval;
639
+	ds_select_state_t vstate;
640
+
641
+	memset(&vstate, 0, sizeof(ds_select_state_t));
642
+	vstate.limit = (uint32_t)rlimit;
643
+	if(vstate.limit == 0) {
644
+		LM_DBG("Limit set to 0 - forcing to unlimited\n");
645
+		vstate.limit = 0xffffffff;
646
+	}
647
+	vret = -1;
648
+	gret = -1;
649
+	vfirst = 0;
650
+	i = 0;
651
+	while(i<srules->len) {
652
+		vstate.setid = 0;
653
+		for(; i<srules->len; i++) {
654
+			if(srules->s[i]<'0' || srules->s[i]>'9') {
655
+				if(srules->s[i]=='=') {
656
+					i++;
657
+					break;
658
+				} else {
659
+					LM_ERR("invalid character in [%.*s] at [%d]\n",
660
+							srules->len, srules->s, i);
661
+					return -1;
662
+				}
663
+			}
664
+			vstate.setid = (vstate.setid * 10) + (srules->s[i] - '0');
665
+		}
666
+		vstate.alg = 0;
667
+		for(; i<srules->len; i++) {
668
+			if(srules->s[i]<'0' || srules->s[i]>'9') {
669
+				if(srules->s[i]==';') {
670
+					i++;
671
+					break;
672
+				} else {
673
+					LM_ERR("invalid character in [%.*s] at [%d]\n",
674
+							srules->len, srules->s, i);
675
+					return -1;
676
+				}
677
+			}
678
+			vstate.alg = (vstate.alg * 10) + (srules->s[i] - '0');
679
+		}
680
+		LM_DBG("routing with setid=%d alg=%d cnt=%d limit=0x%x (%u)\n",
681
+			vstate.setid, vstate.alg, vstate.cnt, vstate.limit, vstate.limit);
682
+		
683
+		vstate.umode = DS_SETOP_XAVP;
684
+		if(vfirst==0) {
685
+			switch(smode->s[0]) {
686
+				case '0':
687
+				case 'd':
688
+				case 'D':
689
+					vstate.umode = DS_SETOP_DSTURI;
690
+				break;
691
+				case '1':
692
+				case 'r':
693
+				case 'R':
694
+					vstate.umode = DS_SETOP_RURI;
695
+				break;
696
+				case '2':
697
+				case 'x':
698
+				case 'X':
699
+				break;
700
+				default:
701
+					LM_ERR("invalid routing mode parameter: %.*s\n",
702
+							smode->len, smode->s);
703
+					return -1;
704
+			}
705
+			vfirst = 1;
706
+		}
707
+		vret = ds_manage_routes(msg, &vstate);
708
+		if(vret<0) {
709
+			LM_DBG("failed to select target destinations from %d=%d [%.*s]\n",
710
+					vstate.setid, vstate.alg, srules->len, srules->s);
711
+			/* continue to try other target groups */
712
+		} else {
713
+			if(vret>0) {
714
+				gret = vret;
715
+			}
716
+		}
717
+	}
718
+
719
+	if(gret<0) {
720
+		/* no selection of a target address */
721
+		LM_DBG("failed to select any target destinations from [%.*s]\n",
722
+					srules->len, srules->s);
723
+		/* return last failure code when trying to select target addresses */
724
+		return vret;
725
+	}
726
+
727
+	/* add cnt value to xavp */
728
+	if(((ds_xavp_ctx_mode & DS_XAVP_CTX_SKIP_CNT)==0)
729
+			&& (ds_xavp_ctx.len >= 0)) {
730
+		/* add to xavp the number of selected dst records */
731
+		memset(&nxval, 0, sizeof(sr_xval_t));
732
+		nxval.type = SR_XTYPE_INT;
733
+		nxval.v.i = vstate.cnt;
734
+		if(xavp_add_xavp_value(&ds_xavp_ctx, &ds_xavp_ctx_cnt, &nxval, NULL)==NULL) {
735
+			LM_ERR("failed to add cnt value to xavp\n");
736
+			return -1;
737
+		}
738
+	}
739
+
740
+	LM_DBG("selected target destinations: %d\n", vstate.cnt);
741
+	return gret;
742
+}
743
+
744
+/**
745
+ *
746
+ */
747
+static int ki_ds_select_routes(sip_msg_t *msg, str *srules, str *smode)
748
+{
749
+	return ki_ds_select_routes_limit(msg, srules, smode, 0);
750
+}
751
+
752
+/**
753
+ *
754
+ */
755
+static int w_ds_select_routes(sip_msg_t *msg, char *lrules, char *umode)
756
+{
757
+	return w_ds_select_routes_limit(msg, lrules, umode, 0);
758
+}
759
+
760
+/**
761
+ *
762
+ */
763
+static int w_ds_select_routes_limit(sip_msg_t *msg, char *lrules, char *umode,
764
+		char *rlimit)
765
+{
766
+	str vrules;
767
+	str vmode;
768
+	int vlimit;
769
+
770
+	if(fixup_get_svalue(msg, (gparam_t*)lrules, &vrules)<0) {
771
+		LM_ERR("failed to get routing rules parameter\n");
772
+		return -1;
773
+	}
774
+	if(fixup_get_svalue(msg, (gparam_t*)umode, &vmode)<0) {
775
+		LM_ERR("failed to get update mode parameter\n");
776
+		return -1;
777
+	}
778
+	if(rlimit!=NULL) {
779
+		if(fixup_get_ivalue(msg, (gparam_t*)rlimit, &vlimit)<0) {
780
+			LM_ERR("failed to get limit parameter\n");
781
+			return -1;
782
+		}
783
+	} else {
784
+		vlimit = 0;
785
+	}
786
+	return ki_ds_select_routes_limit(msg, &vrules, &vmode, vlimit);
787
+}
788
+
622 789
 /**
623 790
  *
624 791
  */
... ...
@@ -1112,6 +1279,16 @@ static sr_kemi_t sr_kemi_dispatcher_exports[] = {
1112 1279
 		{ SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_INT,
1113 1280
 			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1114 1281
 	},
1282
+	{ str_init("dispatcher"), str_init("ds_select_routes"),
1283
+		SR_KEMIP_INT, ki_ds_select_routes,
1284
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
1285
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1286
+	},
1287
+	{ str_init("dispatcher"), str_init("ds_select_routes_limit"),
1288
+		SR_KEMIP_INT, ki_ds_select_routes_limit,
1289
+		{ SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
1290
+			SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1291
+	},
1115 1292
 	{ str_init("dispatcher"), str_init("ds_next_dst"),
1116 1293
 		SR_KEMIP_INT, ki_ds_next_dst,
1117 1294
 		{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,