Browse code

- dispatcher has failover support - to enable failover, set bit 2 of 'flags' parameter - when failover is enabled, selected destination is added as dst_uri or domain part of r-uri and the rest of addresses in destination set are added in AVP list - ds_next_{dst,domain} can be used in failure_route to get the next address in dst_uri or r-uri domain - dev version increased - doc updated

git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@475 689a6050-402a-0410-94f2-e92a70836424

Daniel-Constantin Mierla authored on 18/12/2005 09:56:28
Showing 6 changed files
... ...
@@ -10,6 +10,8 @@ Edited by
10 10
 Daniel-Constantin Mierla
11 11
 
12 12
    Copyright � 2004 FhG FOKUS
13
+
14
+   Copyright � 2005 Voice-System.RO
13 15
      _________________________________________________________
14 16
 
15 17
    Table of Contents
... ...
@@ -26,11 +28,18 @@ Daniel-Constantin Mierla
26 28
               1.3.1. list_file (string)
27 29
               1.3.2. force_dst (int)
28 30
               1.3.3. flags (int)
31
+              1.3.4. use_default (int)
32
+              1.3.5. dst_avp_id (int)
33
+              1.3.6. grp_avp_id (int)
34
+              1.3.7. cnt_avp_id (int)
29 35
 
30 36
         1.4. Exported Functions
31 37
 
32 38
               1.4.1. ds_select_dst(set, alg)
33 39
               1.4.2. ds_select_domain(set, alg)
40
+              1.4.3. ds_next_dst()
41
+              1.4.4. ds_next_domain()
42
+              1.4.5. ds_mark_dst()
34 43
 
35 44
         1.5. Installation & Running
36 45
 
... ...
@@ -44,9 +53,13 @@ Daniel-Constantin Mierla
44 53
    1-1. Set the "list_file" parameter
45 54
    1-2. Set the "force_dst" parameter
46 55
    1-3. Set the "flags" parameter
47
-   1-4. ds_select_dst usage
48
-   1-5. dispatcher list file
49
-   1-6. OpenSER config script - sample dispatcher usage
56
+   1-4. Set the "use_default" parameter
57
+   1-5. Set the "dst_avp_id" parameter
58
+   1-6. Set the "grp_avp_id" parameter
59
+   1-7. Set the "cnt_avp_id" parameter
60
+   1-8. ds_select_dst usage
61
+   1-9. dispatcher list file
62
+   1-10. OpenSER config script - sample dispatcher usage
50 63
      _________________________________________________________
51 64
 
52 65
 Chapter 1. User's Guide
... ...
@@ -109,18 +122,86 @@ modparam("dispatcher", "force_dst", 1)
109 122
 
110 123
 1.3.3. flags (int)
111 124
 
112
-   Various flags that affect the hashing behaviour. For now only
113
-   the flag 1 is defined. If flag 1 is set only the username part
114
-   of the uri will be used when computing an uri based hash. If
115
-   no flags are set the username, hostname and port will be used
116
-   The port is used only if different from 5060 (normal sip uri)
117
-   or 5061 (in the sips case).
125
+   Various flags that affect dispatcher's behaviour. The flags
126
+   are defined as a bitmask on an integer value. If flag 1 is set
127
+   only the username part of the uri will be used when computing
128
+   an uri based hash. If no flags are set the username, hostname
129
+   and port will be used The port is used only if different from
130
+   5060 (normal sip uri) or 5061 (in the sips case).
131
+
132
+   If flag 2 is set, then the failover support is enabled. The
133
+   functions exported by the module will store the rest of
134
+   addresses from the destination set in AVP, and use these AVPs
135
+   to contact next address when the current-tried fails.
118 136
 
119 137
    Default value is "0". 
120 138
 
121 139
    Example 1-3. Set the "flags" parameter
122 140
  ...
123
- modparam("dispatcher", "flags", 1)
141
+ modparam("dispatcher", "flags", 3)
142
+ ...
143
+
144
+     _________________________________________________________
145
+
146
+1.3.4. use_default (int)
147
+
148
+   If the parameter is set to 1, the last address in destination
149
+   set is used as last option to send the message. For example,
150
+   it is good when wanting to send the call to an anouncement
151
+   server saying: "the gateways are full, try later".
152
+
153
+   Default value is "0". 
154
+
155
+   Example 1-4. Set the "use_default" parameter
156
+ ...
157
+ modparam("dispatcher", "use_default", 1)
158
+ ...
159
+
160
+     _________________________________________________________
161
+
162
+1.3.5. dst_avp_id (int)
163
+
164
+   The id of the avp which will hold the list with addresses, in
165
+   the order they have been selected by the chosen algorithm. If
166
+   use_default is 1, the value of last dst_avp_id is the last
167
+   address in destination set. The first dst_avp_id is the
168
+   selected destinations. All the other addresses from the
169
+   destination set will be added in the avp list to be able to
170
+   implement serial forking.
171
+
172
+   Default value is "271". 
173
+
174
+   Example 1-5. Set the "dst_avp_id" parameter
175
+ ...
176
+ modparam("dispatcher", "dst_avp_id", 100)
177
+ ...
178
+
179
+     _________________________________________________________
180
+
181
+1.3.6. grp_avp_id (int)
182
+
183
+   The id of the avp storing the group id of the destination set.
184
+   Good to have it for later usage or checks.
185
+
186
+   Default value is "272". 
187
+
188
+   Example 1-6. Set the "grp_avp_id" parameter
189
+ ...
190
+ modparam("dispatcher", "grp_avp_id", 101)
191
+ ...
192
+
193
+     _________________________________________________________
194
+
195
+1.3.7. cnt_avp_id (int)
196
+
197
+   The id of the avp storing the number of destination addresses
198
+   kept in dst_avp_id avps.
199
+
200
+   Default value is "273". 
201
+
202
+   Example 1-7. Set the "cnt_avp_id" parameter
203
+ ...
204
+ modparam("dispatcher", "cnt_avp_id", 103)
124 205
  ...
125 206
 
126 207
      _________________________________________________________
... ...
@@ -145,9 +226,14 @@ modparam("dispatcher", "force_dst", 1)
145 226
           + "X" - if the algorithm is not implemented, the first
146 227
             entry in set is chosen.
147 228
 
229
+   If the bit 2 in 'flags' is set, the rest of the addresses from
230
+   the destination set is stored in AVP list. You can use
231
+   'ds_next_dst()' to use next address to achieve serial forking
232
+   to all possible destinations.
233
+
148 234
    This function can be used from REQUEST_ROUTE.
149 235
 
150
-   Example 1-4. ds_select_dst usage
236
+   Example 1-8. ds_select_dst usage
151 237
 ...
152 238
 ds_select_dst("1", "0");
153 239
 ...
... ...
@@ -159,9 +245,41 @@ ds_select_dst("1", "0");
159 245
    rewrites the host and port from R-URI. The parameters have
160 246
    same meaning as for ds_select_dst().
161 247
 
248
+   If the bit 2 in 'flags' is set, the rest of the addresses from
249
+   the destination set is stored in AVP list. You can use
250
+   'ds_next_domain()' to use next address to achieve serial
251
+   forking to all possible destinations.
252
+
162 253
    This function can be used from REQUEST_ROUTE.
163 254
      _________________________________________________________
164 255
 
256
+1.4.3. ds_next_dst()
257
+
258
+   Takes the next destination address from the AVPs with id
259
+   'dst_avp_id' and sets the dst_uri (outbound proxy address).
260
+
261
+   This function can be used from FAILURE_ROUTE.
262
+     _________________________________________________________
263
+
264
+1.4.4. ds_next_domain()
265
+
266
+   Takes the next destination address from the AVPs with id
267
+   'dst_avp_id' and sets the domain part of the request uri.
268
+
269
+   This function can be used from FAILURE_ROUTE.
270
+     _________________________________________________________
271
+
272
+1.4.5. ds_mark_dst()
273
+
274
+   Mark the last used address from destination set as inactive,
275
+   in order to be ingnored in the future. In this way it can be
276
+   implemented an automatic detection of failed gateways. When an
277
+   address is marked as inactive, it will be ignored by
278
+   'ds_select_dst' and 'ds_select_domain'.
279
+
280
+   This function can be used from FAILURE_ROUTE.
281
+     _________________________________________________________
282
+
165 283
 1.5. Installation & Running
166 284
 
167 285
 1.5.1. Destination List File
... ...
@@ -171,7 +289,7 @@ ds_select_dst("1", "0");
171 289
    integer value. Destination address must be a valid SIP URI.
172 290
    Empty lines or lines starting with "#" are ignored.
173 291
 
174
-   Example 1-5. dispatcher list file
292
+   Example 1-9. dispatcher list file
175 293
 ...
176 294
 # $Id$
177 295
 # dispatcher destination sets
... ...
@@ -192,7 +310,7 @@ ds_select_dst("1", "0");
192 310
 
193 311
    Next picture displays a sample usage of dispatcher.
194 312
 
195
-   Example 1-6. OpenSER config script - sample dispatcher usage
313
+   Example 1-10. OpenSER config script - sample dispatcher usage
196 314
 ...
197 315
 # $Id$
198 316
 # sample config file for dispatcher module
... ...
@@ -4,6 +4,7 @@
4 4
  * dispatcher module
5 5
  *
6 6
  * Copyright (C) 2004-2006 FhG Fokus
7
+ * Copyright (C) 2005 Voice-System.ro
7 8
  *
8 9
  * This file is part of openser, a free SIP server.
9 10
  *
... ...
@@ -25,6 +26,7 @@
25 26
  * -------
26 27
  * 2004-07-31  first version, by daniel
27 28
  * 2005-04-22  added ruri  & to_uri hashing (andrei)
29
+ * 2005-12-10  added failover support via avp (daniel)
28 30
  * 
29 31
  */
30 32
 
... ...
@@ -35,9 +37,12 @@
35 37
 #include "../../trim.h"
36 38
 #include "../../dprint.h"
37 39
 #include "../../action.h"
38
-#include "../../mem/mem.h"
40
+#include "../../route.h"
41
+#include "../../dset.h"
42
+#include "../../mem/shm_mem.h"
39 43
 #include "../../parser/parse_uri.h"
40 44
 #include "../../parser/parse_from.h"
45
+#include "../../usr_avp.h"
41 46
 
42 47
 #include "dispatch.h"
43 48
 
... ...
@@ -52,6 +57,7 @@ typedef struct _ds_setidx
52 57
 typedef struct _ds_dest
53 58
 {
54 59
 	str uri;
60
+	int flags;
55 61
 	struct _ds_dest *next;
56 62
 } ds_dest_t, *ds_dest_p;
57 63
 
... ...
@@ -65,10 +71,11 @@ typedef struct _ds_set
65 71
 	struct _ds_set *next;
66 72
 } ds_set_t, *ds_set_p;
67 73
 
68
-extern int force_dst;
74
+extern int ds_force_dst;
69 75
 
70 76
 ds_setidx_p _ds_index = NULL;
71 77
 ds_set_p _ds_list = NULL;
78
+int _ds_list_nr = 0;
72 79
 
73 80
 /**
74 81
  *
... ...
@@ -151,7 +158,7 @@ int ds_load_list(char *lfile)
151 158
 
152 159
 		if(si==NULL)
153 160
 		{
154
-			si = (ds_setidx_p)pkg_malloc(sizeof(ds_setidx_t));
161
+			si = (ds_setidx_p)shm_malloc(sizeof(ds_setidx_t));
155 162
 			if(si==NULL)
156 163
 			{
157 164
 				LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory\n");
... ...
@@ -177,7 +184,7 @@ int ds_load_list(char *lfile)
177 184
 
178 185
 		if(sp==NULL)
179 186
 		{
180
-			sp = (ds_set_p)pkg_malloc(sizeof(ds_set_t));
187
+			sp = (ds_set_p)shm_malloc(sizeof(ds_set_t));
181 188
 			if(sp==NULL)
182 189
 			{
183 190
 				LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory.\n");
... ...
@@ -193,7 +200,7 @@ int ds_load_list(char *lfile)
193 200
 		sp->index = si->index;
194 201
 
195 202
 		/* store uri */
196
-		dp = (ds_dest_p)pkg_malloc(sizeof(ds_dest_t));
203
+		dp = (ds_dest_p)shm_malloc(sizeof(ds_dest_t));
197 204
 		if(dp==NULL)
198 205
 		{
199 206
 			LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory!\n");
... ...
@@ -201,11 +208,11 @@ int ds_load_list(char *lfile)
201 208
 		}
202 209
 		memset(dp, 0, sizeof(ds_dest_t));
203 210
 
204
-		dp->uri.s = (char*)pkg_malloc(uri.len+1);
211
+		dp->uri.s = (char*)shm_malloc(uri.len+1);
205 212
 		if(dp->uri.s==NULL)
206 213
 		{
207 214
 			LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory!!\n");
208
-			pkg_free(dp);
215
+			shm_free(dp);
209 216
 			goto error;
210 217
 		}
211 218
 		strncpy(dp->uri.s, uri.s, uri.len);
... ...
@@ -227,7 +234,7 @@ next_line:
227 234
 	DBG("DISPATCHER:ds_load_list: found [%d] dest sets\n", setn);
228 235
 	
229 236
 	/* re-index destination sets for fast access */
230
-	sp0 = (ds_set_p)pkg_malloc(setn*sizeof(ds_set_t));
237
+	sp0 = (ds_set_p)shm_malloc(setn*sizeof(ds_set_t));
231 238
 	if(sp0==NULL)
232 239
 	{
233 240
 		LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory!!\n");
... ...
@@ -242,7 +249,7 @@ next_line:
242 249
 			sp0[i].next = NULL;
243 250
 		else
244 251
 			sp0[i].next = &sp0[i+1];
245
-		dp0 = (ds_dest_p)pkg_malloc(sp0[i].nr*sizeof(ds_dest_t));
252
+		dp0 = (ds_dest_p)shm_malloc(sp0[i].nr*sizeof(ds_dest_t));
246 253
 		if(dp0==NULL)
247 254
 		{
248 255
 			LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory!\n");
... ...
@@ -273,12 +280,14 @@ next_line:
273 280
 		{
274 281
 			dp0 = dp;
275 282
 			dp = dp->next;
276
-			pkg_free(dp0);
283
+			shm_free(dp0);
277 284
 		}
278 285
 		sp0 = sp;
279 286
 		sp = sp->next;
280
-		pkg_free(sp0);
287
+		shm_free(sp0);
281 288
 	}
289
+
290
+	_ds_list_nr = setn;
282 291
 	
283 292
 	return 0;
284 293
 
... ...
@@ -291,7 +300,7 @@ error:
291 300
 	{
292 301
 		si0 = si;
293 302
 		si = si->next;
294
-		pkg_free(si0);
303
+		shm_free(si0);
295 304
 	}
296 305
 	_ds_index = NULL;
297 306
 
... ...
@@ -302,15 +311,15 @@ error:
302 311
 		while(dp)
303 312
 		{
304 313
 			if(dp->uri.s!=NULL)
305
-				pkg_free(dp->uri.s);
314
+				shm_free(dp->uri.s);
306 315
 			dp->uri.s=NULL;
307 316
 			dp0 = dp;
308 317
 			dp = dp->next;
309
-			pkg_free(dp0);
318
+			shm_free(dp0);
310 319
 		}
311 320
 		sp0 = sp;
312 321
 		sp = sp->next;
313
-		pkg_free(sp0);
322
+		shm_free(sp0);
314 323
 	}
315 324
 	return -1;
316 325
 }
... ...
@@ -331,21 +340,21 @@ int ds_destroy_list()
331 340
 		{
332 341
 			if(sp->dlist[i].uri.s!=NULL)
333 342
 			{
334
-				pkg_free(sp->dlist[i].uri.s);
343
+				shm_free(sp->dlist[i].uri.s);
335 344
 				sp->dlist[i].uri.s = NULL;
336 345
 			}
337 346
 		}
338
-		pkg_free(sp->dlist);
347
+		shm_free(sp->dlist);
339 348
 		sp = sp->next;
340 349
 	}
341
-	if (_ds_list) pkg_free(_ds_list);
350
+	if (_ds_list) shm_free(_ds_list);
342 351
 	
343 352
 	si = _ds_index;
344 353
 	while(si)
345 354
 	{
346 355
 		si0 = si;
347 356
 		si = si->next;
348
-		pkg_free(si0);
357
+		shm_free(si0);
349 358
 	}
350 359
 	_ds_index = NULL;
351 360
 
... ...
@@ -599,16 +608,96 @@ int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
599 608
 }
600 609
 
601 610
 
611
+static inline int ds_get_index(int group, int *index)
612
+{
613
+	ds_setidx_p si = NULL;
614
+	
615
+	if(index==NULL || group<0 || _ds_index==NULL)
616
+		return -1;
617
+	
618
+	/* get the index of the set */
619
+	si = _ds_index;
620
+	while(si)
621
+	{
622
+		if(si->id == group)
623
+		{
624
+			*index = si->index;
625
+			break;
626
+		}
627
+		si = si->next;
628
+	}
629
+
630
+	if(si==NULL)
631
+	{
632
+		LOG(L_ERR,
633
+			"DISPATCHER:ds_get_index: destination set [%d] not found\n",group);
634
+		return -1;
635
+	}
636
+
637
+	return 0;
638
+}
639
+
640
+static inline int ds_update_dst(struct sip_msg *msg, str *uri, int mode)
641
+{
642
+	struct action act;
643
+	switch(mode)
644
+	{
645
+		case 1:
646
+			act.type = SET_HOSTPORT_T;
647
+			act.p1_type = STRING_ST;
648
+			if(uri->len>4 
649
+					&& strncasecmp(uri->s,"sip:",4)==0)
650
+				act.p1.string = uri->s+4;
651
+			else
652
+				act.p1.string = uri->s;
653
+			act.next = 0;
654
+	
655
+			if (do_action(&act, msg) < 0) {
656
+				LOG(L_ERR,
657
+					"DISPATCHER:dst_update_dst: Error while setting host\n");
658
+				return -1;
659
+			}
660
+			if(route_type==FAILURE_ROUTE)
661
+			{
662
+				if (append_branch(msg, 0, 0, Q_UNSPECIFIED, 0, 0)!=1 )
663
+				{
664
+					LOG(L_ERR,
665
+						"DISPATCHER:dst_update_dst: append_branch action"
666
+						" failed\n");
667
+					return -1;
668
+				}
669
+			}
670
+		break;
671
+		default:
672
+			if(route_type==FAILURE_ROUTE)
673
+			{
674
+				if (append_branch(msg, 0, uri, Q_UNSPECIFIED, 0, 0)!=1 )
675
+				{
676
+					LOG(L_ERR,
677
+						"DISPATCHER:dst_update_dst: append_branch action"
678
+						" failed\n");
679
+					return -1;
680
+				}
681
+			} else {
682
+				if (set_dst_uri(msg, uri) < 0) {
683
+					LOG(L_ERR,
684
+					"DISPATCHER:dst_update_dst: Error while setting dst_uri\n");
685
+					return -1;
686
+				}	
687
+			}
688
+		break;
689
+	}
690
+	return 0;
691
+}
602 692
 
603 693
 /**
604 694
  *
605 695
  */
606
-int ds_select_dst(struct sip_msg *msg, char *set, char *alg, int mode)
696
+int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
607 697
 {
608
-	int a, s, idx;
609
-	ds_setidx_p si = NULL;
698
+	int idx, i, cnt;
610 699
 	unsigned int hash;
611
-	struct action act;
700
+	int_str avp_name, avp_val;
612 701
 
613 702
 	if(msg==NULL)
614 703
 	{
... ...
@@ -622,7 +711,7 @@ int ds_select_dst(struct sip_msg *msg, char *set, char *alg, int mode)
622 711
 		return -1;
623 712
 	}
624 713
 
625
-	if((mode==0) && (force_dst==0)
714
+	if((mode==0) && (ds_force_dst==0)
626 715
 			&& (msg->dst_uri.s!=NULL || msg->dst_uri.len>0))
627 716
 	{
628 717
 		LOG(L_ERR,
... ...
@@ -631,32 +720,19 @@ int ds_select_dst(struct sip_msg *msg, char *set, char *alg, int mode)
631 720
 		return -1;
632 721
 	}
633 722
 	
634
-	s = (int)(long)set;
635
-	a = (int)(long)alg;
636 723
 
637 724
 	/* get the index of the set */
638
-	si = _ds_index;
639
-	while(si)
640
-	{
641
-		if(si->id == s)
642
-		{
643
-			idx = si->index;
644
-			break;
645
-		}
646
-		si = si->next;
647
-	}
648
-
649
-	if(si==NULL)
725
+	if(ds_get_index(set, &idx)!=0)
650 726
 	{
651 727
 		LOG(L_ERR,
652
-			"DISPATCHER:ds_select_dst: destination set [%d] not found\n",s);
728
+			"DISPATCHER:ds_select_dst: destination set [%d] not found\n",set);
653 729
 		return -1;
654 730
 	}
655
-
656
-	DBG("DISPATCHER:ds_select_dst: set index [%d]\n", idx);
731
+	
732
+	DBG("DISPATCHER:ds_select_dst: set index [%d->%d]\n", set, idx);
657 733
 
658 734
 	hash = 0;
659
-	switch(a)
735
+	switch(alg)
660 736
 	{
661 737
 		case 0:
662 738
 			if(ds_hash_callid(msg, &hash)!=0)
... ...
@@ -697,43 +773,240 @@ int ds_select_dst(struct sip_msg *msg, char *set, char *alg, int mode)
697 773
 		default:
698 774
 			LOG(L_WARN,
699 775
 					"WARNING: ds_select_dst: algo %d not implemented"
700
-					" - using first entry...\n", a);
776
+					" - using first entry...\n", alg);
701 777
 			hash = 0;
702 778
 	}
703 779
 
704 780
 	DBG("DISPATCHER:ds_select_dst: alg hash [%u]\n", hash);
705
-
706
-	hash = hash%_ds_list[idx].nr;
707
-	switch(mode)
781
+	cnt = 0;
782
+	if(ds_use_default!=0)
783
+		hash = hash%(_ds_list[idx].nr-1);
784
+	else
785
+		hash = hash%_ds_list[idx].nr;
786
+	i=hash;
787
+	while(_ds_list[idx].dlist[i].flags & DS_INACTIVE_DST)
708 788
 	{
709
-		case 1:
710
-			act.type = SET_HOSTPORT_T;
711
-			act.p1_type = STRING_ST;
712
-			if(_ds_list[idx].dlist[hash].uri.len>4 
713
-					&& strncasecmp(_ds_list[idx].dlist[hash].uri.s,"sip:",4)==0)
714
-				act.p1.string = _ds_list[idx].dlist[hash].uri.s+4;
715
-			else
716
-				act.p1.string = _ds_list[idx].dlist[hash].uri.s;
717
-			act.next = 0;
718
-	
719
-			if (do_action(&act, msg) < 0) {
720
-				LOG(L_ERR,
721
-					"DISPATCHER:dst_select_dst: Error while setting host\n");
722
-				return -1;
723
-			}
724
-		break;
725
-		default:
726
-			if (set_dst_uri(msg, &_ds_list[idx].dlist[hash].uri) < 0) {
727
-				LOG(L_ERR,
728
-					"DISPATCHER:dst_select_dst: Error while setting dst_uri\n");
789
+		if(ds_use_default!=0)
790
+			i = (i+1)%(_ds_list[idx].nr-1);
791
+		else
792
+			i = (i+1)%_ds_list[idx].nr;
793
+		if(i==hash)
794
+		{
795
+			if(ds_use_default!=0)
796
+			{
797
+				i = _ds_list[idx].nr-1;
798
+			} else {
729 799
 				return -1;
730 800
 			}
731
-			DBG("DISPATCHER:ds_select_dst: selected [%d-%d/%d/%d] <%.*s>\n",
732
-				a, s, idx, hash, msg->dst_uri.len, msg->dst_uri.s);
801
+		}
802
+	}
803
+
804
+	hash = i;
805
+
806
+	if(ds_update_dst(msg, &_ds_list[idx].dlist[hash].uri, mode)!=0)
807
+	{
808
+		LOG(L_ERR, "DISPATCHER:ds_select_dst: cannot set dst addr\n");
809
+		return -1;
810
+	}
733 811
 	
734
-		break;
812
+	DBG("DISPATCHER:ds_select_dst: selected [%d-%d/%d/%d] <%.*s>\n",
813
+		alg, set, idx, hash, _ds_list[idx].dlist[hash].uri.len,
814
+		_ds_list[idx].dlist[hash].uri.s);
815
+
816
+	if(!(ds_flags&DS_FAILOVER_ON))
817
+		return 1;
818
+
819
+	if(ds_use_default!=0 && hash!=_ds_list[idx].nr-1)
820
+	{
821
+		avp_val.s = &_ds_list[idx].dlist[_ds_list[idx].nr-1].uri;
822
+		avp_name.n = dst_avp_id;
823
+		if(add_avp(AVP_VAL_STR, avp_name, avp_val)!=0)
824
+			return -1;
825
+		cnt++;
826
+	}
827
+	
828
+	/* add to avp */
829
+
830
+	for(i=hash-1; i>=0; i--)
831
+	{	
832
+		if((_ds_list[idx].dlist[i].flags & DS_INACTIVE_DST)
833
+				|| (ds_use_default!=0 && i==(_ds_list[idx].nr-1)))
834
+			continue;
835
+		DBG("DISPATCHER:ds_select_dst: using entry [%d/%d]\n",
836
+				set, i);
837
+		avp_val.s = &_ds_list[idx].dlist[i].uri;
838
+		avp_name.n = dst_avp_id;
839
+		if(add_avp(AVP_VAL_STR, avp_name, avp_val)!=0)
840
+			return -1;
841
+		cnt++;
842
+	}
843
+
844
+	for(i=_ds_list[idx].nr-1; i>hash; i--)
845
+	{	
846
+		if((_ds_list[idx].dlist[i].flags & DS_INACTIVE_DST)
847
+				|| (ds_use_default!=0 && i==(_ds_list[idx].nr-1)))
848
+			continue;
849
+		DBG("DISPATCHER:ds_select_dst: using entry [%d/%d]\n",
850
+				set, i);
851
+		avp_val.s = &_ds_list[idx].dlist[i].uri;
852
+		avp_name.n = dst_avp_id;
853
+		if(add_avp(AVP_VAL_STR, avp_name, avp_val)!=0)
854
+			return -1;
855
+		cnt++;
856
+	}
857
+
858
+	/* add to avp the first used dst */
859
+	avp_val.s = &_ds_list[idx].dlist[hash].uri;
860
+	avp_name.n = dst_avp_id;
861
+	if(add_avp(AVP_VAL_STR, avp_name, avp_val)!=0)
862
+		return -1;
863
+	cnt++;
864
+	
865
+	/* add to avp the group id */
866
+	avp_val.n = set;
867
+	avp_name.n = grp_avp_id;
868
+	if(add_avp(0, avp_name, avp_val)!=0)
869
+		return -1;
870
+	
871
+	/* add to avp the number of dst */
872
+	avp_val.n = cnt;
873
+	avp_name.n = cnt_avp_id;
874
+	if(add_avp(0, avp_name, avp_val)!=0)
875
+		return -1;
876
+	
877
+	return 1;
878
+}
879
+
880
+int ds_next_dst(struct sip_msg *msg, int mode)
881
+{
882
+	struct usr_avp *avp;
883
+	struct usr_avp *prev_avp;
884
+	int_str avp_name;
885
+	int_str avp_value;
886
+	
887
+	if(!(ds_flags&DS_FAILOVER_ON))
888
+	{
889
+		LOG(L_WARN, "DISPATCHER:ds_next_dst: failover support disabled\n");
890
+		return -1;
735 891
 	}
736 892
 
893
+	avp_name.n = dst_avp_id;
894
+
895
+	prev_avp = search_first_avp(0, avp_name, &avp_value);
896
+	if(prev_avp==NULL)
897
+		return -1; /* used avp deleted -- strange */
898
+
899
+	avp = search_next_avp(prev_avp, &avp_value);
900
+	destroy_avp(prev_avp);
901
+	if(avp==NULL || !(avp->flags&AVP_VAL_STR))
902
+		return -1; /* no more avps or value is int */
903
+	
904
+	if(ds_update_dst(msg, avp_value.s, mode)!=0)
905
+	{
906
+		LOG(L_ERR, "DISPATCHER:ds_next_dst: cannot set dst addr\n");
907
+		return -1;
908
+	}
909
+	DBG("DISPATCHER:ds_next_dst: using [%.*s]\n",
910
+			avp_value.s->len, avp_value.s->s);
911
+	
737 912
 	return 1;
738 913
 }
739 914
 
915
+int ds_mark_dst(struct sip_msg *msg, int mode)
916
+{
917
+	int group, ret;
918
+	struct usr_avp *prev_avp;
919
+	int_str avp_name;
920
+	int_str avp_value;
921
+	
922
+	if(!(ds_flags&DS_FAILOVER_ON))
923
+	{
924
+		LOG(L_WARN, "DISPATCHER:ds_mark_dst: failover support disabled\n");
925
+		return -1;
926
+	}
927
+
928
+	avp_name.n = grp_avp_id;
929
+	prev_avp = search_first_avp(0, avp_name, &avp_value);
930
+	
931
+	if(prev_avp==NULL || prev_avp->flags&AVP_VAL_STR)
932
+		return -1; /* grp avp deleted -- strange */
933
+	group = avp_value.n;
934
+	
935
+	avp_name.n = dst_avp_id;
936
+	prev_avp = search_first_avp(0, avp_name, &avp_value);
937
+	
938
+	if(prev_avp==NULL || !(prev_avp->flags&AVP_VAL_STR))
939
+		return -1; /* dst avp deleted -- strange */
940
+	
941
+	if(mode==1)
942
+		ret = ds_set_state(group, avp_value.s, DS_INACTIVE_DST, 0);
943
+	else
944
+		ret = ds_set_state(group, avp_value.s, DS_INACTIVE_DST, 1);
945
+
946
+	DBG("DISPATCHER:ds_mark_dst: mode [%d] grp [%d] dst [%.*s]\n",
947
+			mode, group, avp_value.s->len, avp_value.s->s);
948
+	
949
+	return (ret==0)?1:-1;
950
+}
951
+
952
+int ds_set_state(int group, str *address, int state, int type)
953
+{
954
+	int i=0, idx=0;
955
+
956
+	if(_ds_list==NULL)
957
+	{
958
+		LOG(L_ERR, "DISPATCHER:ds_set_state: the list is null\n");
959
+		return -1;
960
+	}
961
+	
962
+	/* get the index of the set */
963
+	if(ds_get_index(group, &idx)!=0)
964
+	{
965
+		LOG(L_ERR,
966
+			"DISPATCHER:ds_set_state: destination set [%d] not found\n",group);
967
+		return -1;
968
+	}
969
+
970
+	while(i<_ds_list[idx].nr)
971
+	{
972
+		if(_ds_list[idx].dlist[i].uri.len==address->len 
973
+				&& strncasecmp(_ds_list[idx].dlist[i].uri.s, address->s,
974
+					address->len)==0)
975
+		{
976
+			if(type)
977
+				_ds_list[idx].dlist[i].flags |= state;
978
+			else
979
+				_ds_list[idx].dlist[i].flags &= ~state;
980
+				
981
+			return 0;
982
+		}
983
+		i++;
984
+	}
985
+
986
+	return -1;
987
+}
988
+
989
+int ds_print_list(FILE *fout)
990
+{
991
+	int i, j;
992
+	
993
+	if(_ds_list==NULL || _ds_list_nr<=0)
994
+	{
995
+		LOG(L_ERR, "DISPATCHER:ds_print_list: the list is null\n");
996
+		return -1;
997
+	}
998
+	
999
+	fprintf(fout, "\nnumber of destination sets: %d\n", _ds_list_nr);
1000
+	for(i=0; i<_ds_list_nr; i++)
1001
+	{
1002
+		fprintf(fout, "\n set #%d\n", _ds_list[i].id);
1003
+		for(j=0; j<_ds_list[i].nr; j++)
1004
+		{
1005
+			fprintf(fout, "    %c   %.*s\n",
1006
+				(_ds_list[i].dlist[j].flags&DS_INACTIVE_DST)?'I':'A',
1007
+				_ds_list[i].dlist[j].uri.len, _ds_list[i].dlist[j].uri.s);
1008
+		}
1009
+	}
1010
+	return 0;
1011
+}
1012
+
... ...
@@ -29,15 +29,37 @@
29 29
 #ifndef _DISPATCH_H_
30 30
 #define _DISPATCH_H_
31 31
 
32
+#include <stdio.h>
33
+#include "../../items.h"
32 34
 #include "../../parser/msg_parser.h"
33 35
 
34 36
 
35
-#define DS_HASH_USER_ONLY 1  /* use only the uri user part for hashing */
37
+#define DS_HASH_USER_ONLY	1  /* use only the uri user part for hashing */
38
+#define DS_FAILOVER_ON		2  /* store the other dest in avps */
39
+#define DS_INACTIVE_DST		1  /* inactive destination */
40
+
41
+typedef struct _ds_param
42
+{
43
+	int type;
44
+	union {
45
+		int id;
46
+		xl_spec_t sp;
47
+	} v;
48
+} ds_param_t, *ds_param_p;
49
+
36 50
 extern int ds_flags; 
51
+extern int ds_use_default; 
52
+extern int dst_avp_id;
53
+extern int grp_avp_id;
54
+extern int cnt_avp_id;
37 55
 
38 56
 int ds_load_list(char *lfile);
39 57
 int ds_destroy_list();
40
-int ds_select_dst(struct sip_msg *msg, char *set, char *alg, int mode);
58
+int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode);
59
+int ds_next_dst(struct sip_msg *msg, int mode);
60
+int ds_set_state(int group, str *address, int state, int type);
61
+int ds_mark_dst(struct sip_msg *msg, int mode);
62
+int ds_print_list(FILE *fout);
41 63
 
42 64
 #endif
43 65
 
... ...
@@ -37,7 +37,9 @@
37 37
 #include "../../dprint.h"
38 38
 #include "../../error.h"
39 39
 #include "../../ut.h"
40
+#include "../../route.h"
40 41
 #include "../../mem/mem.h"
42
+#include "../../fifo_server.h"
41 43
 
42 44
 #include "dispatch.h"
43 45
 
... ...
@@ -45,8 +47,12 @@ MODULE_VERSION
45 47
 
46 48
 /** parameters */
47 49
 char *dslistfile = CFG_DIR"dispatcher.list";
48
-int  force_dst = 0;
49
-int  ds_flags   = 0; 
50
+int  ds_force_dst   = 0;
51
+int  ds_flags       = 0; 
52
+int  ds_use_default = 0; 
53
+int  dst_avp_id     = 271;
54
+int  grp_avp_id     = 272;
55
+int  cnt_avp_id     = 273;
50 56
 
51 57
 /** module functions */
52 58
 static int mod_init(void);
... ...
@@ -54,22 +60,37 @@ static int child_init(int);
54 60
 
55 61
 static int w_ds_select_dst(struct sip_msg*, char*, char*);
56 62
 static int w_ds_select_domain(struct sip_msg*, char*, char*);
63
+static int w_ds_next_dst(struct sip_msg*, char*, char*);
64
+static int w_ds_next_domain(struct sip_msg*, char*, char*);
65
+static int w_ds_mark_dst0(struct sip_msg*, char*, char*);
66
+static int w_ds_mark_dst1(struct sip_msg*, char*, char*);
57 67
 
58 68
 void destroy(void);
59 69
 
60 70
 static int ds_fixup(void** param, int param_no);
61 71
 
72
+static int ds_fifo_set(FILE *stream, char *response_file);
73
+static int ds_fifo_list(FILE *stream, char *response_file);
74
+
62 75
 static cmd_export_t cmds[]={
63 76
 	{"ds_select_dst",    w_ds_select_dst,    2, ds_fixup, REQUEST_ROUTE},
64 77
 	{"ds_select_domain", w_ds_select_domain, 2, ds_fixup, REQUEST_ROUTE},
78
+	{"ds_next_dst",      w_ds_next_dst,      0,        0, FAILURE_ROUTE},
79
+	{"ds_next_domain",   w_ds_next_domain,   0,        0, FAILURE_ROUTE},
80
+	{"ds_mark_dst",      w_ds_mark_dst0,     0,        0, FAILURE_ROUTE},
81
+	{"ds_mark_dst",      w_ds_mark_dst1,     1,        0, FAILURE_ROUTE},
65 82
 	{0,0,0,0,0}
66 83
 };
67 84
 
68 85
 
69 86
 static param_export_t params[]={
70 87
 	{"list_file",      STR_PARAM, &dslistfile},
71
-	{"force_dst",      INT_PARAM, &force_dst},
88
+	{"force_dst",      INT_PARAM, &ds_force_dst},
72 89
 	{"flags",          INT_PARAM, &ds_flags},
90
+	{"use_default",    INT_PARAM, &ds_use_default},
91
+	{"dst_avp_id",     INT_PARAM, &dst_avp_id},
92
+	{"grp_avp_id",     INT_PARAM, &grp_avp_id},
93
+	{"cnt_avp_id",     INT_PARAM, &cnt_avp_id},
73 94
 	{0,0,0}
74 95
 };
75 96
 
... ...
@@ -94,6 +115,20 @@ static int mod_init(void)
94 115
 {
95 116
 	DBG("DISPATCHER: initializing ...\n");
96 117
 
118
+	if(register_fifo_cmd(ds_fifo_set, "ds_set_state", 0)<0)
119
+	{
120
+		LOG(L_ERR,
121
+			"DISPATCHER:mod_init:ERROR: cannot register fifo command!\n");
122
+		return -1;
123
+	}
124
+	
125
+	if(register_fifo_cmd(ds_fifo_list, "ds_list", 0)<0)
126
+	{
127
+		LOG(L_ERR,
128
+			"DISPATCHER:mod_init:ERROR: cannot register fifo command!!\n");
129
+		return -1;
130
+	}
131
+	
97 132
 	if(ds_load_list(dslistfile)!=0)
98 133
 	{
99 134
 		LOG(L_ERR, "DISPATCHER:mod_init:ERROR -- couldn't load list file\n");
... ...
@@ -112,15 +147,50 @@ static int child_init(int rank)
112 147
 	return 0;
113 148
 }
114 149
 
150
+static inline int ds_get_ivalue(struct sip_msg* msg, ds_param_p dp, int *val)
151
+{
152
+	xl_value_t value;
153
+	if(dp->type==0) {
154
+		*val = dp->v.id;
155
+		return 0;
156
+	}
157
+	
158
+	DBG("DISPATCHER:ds_get_ivalue: searching %d %d %d\n", dp->v.sp.type,
159
+			dp->v.sp.p.hindex, dp->v.sp.p.hparam.len);
160
+	if(xl_get_spec_value(msg, &dp->v.sp, &value)!=0 
161
+			|| value.flags&XL_VAL_NULL || !(value.flags&XL_VAL_INT))
162
+	{
163
+		LOG(L_ERR,
164
+			"DISPATCHER:ds_get_ivalue: no AVP found (error in scripts)\n");
165
+		return -1;
166
+	}
167
+	*val = value.ri;
168
+	return 0;
169
+}
170
+
115 171
 /**
116 172
  *
117 173
  */
118 174
 static int w_ds_select_dst(struct sip_msg* msg, char* set, char* alg)
119 175
 {
176
+	int a, s;
177
+	
120 178
 	if(msg==NULL)
121 179
 		return -1;
180
+	if(ds_get_ivalue(msg, (ds_param_p)set, &s)!=0)
181
+	{
182
+		LOG(L_ERR,
183
+			"DISPATCHER:w_ds_select_dst: no set value\n");
184
+		return -1;
185
+	}
186
+	if(ds_get_ivalue(msg, (ds_param_p)alg, &a)!=0)
187
+	{
188
+		LOG(L_ERR,
189
+			"DISPATCHER:ds_get_ivalue: no alg value\n");
190
+		return -1;
191
+	}
122 192
 
123
-	return ds_select_dst(msg, set, alg, 0);
193
+	return ds_select_dst(msg, s, a, 0 /*set dst uri*/);
124 194
 }
125 195
 
126 196
 /**
... ...
@@ -128,10 +198,59 @@ static int w_ds_select_dst(struct sip_msg* msg, char* set, char* alg)
128 198
  */
129 199
 static int w_ds_select_domain(struct sip_msg* msg, char* set, char* alg)
130 200
 {
201
+	int a, s;
131 202
 	if(msg==NULL)
132 203
 		return -1;
133 204
 
134
-	return ds_select_dst(msg, set, alg, 1);
205
+	if(ds_get_ivalue(msg, (ds_param_p)set, &s)!=0)
206
+	{
207
+		LOG(L_ERR,
208
+			"DISPATCHER:w_ds_select_dst: no set value\n");
209
+		return -1;
210
+	}
211
+	if(ds_get_ivalue(msg, (ds_param_p)alg, &a)!=0)
212
+	{
213
+		LOG(L_ERR,
214
+			"DISPATCHER:ds_get_ivalue: no alg value\n");
215
+		return -1;
216
+	}
217
+
218
+	return ds_select_dst(msg, s, a, 1/*set host port*/);
219
+}
220
+
221
+/**
222
+ *
223
+ */
224
+static int w_ds_next_dst(struct sip_msg *msg, char *str1, char *str2)
225
+{
226
+	return ds_next_dst(msg, 0/*set dst uri*/);
227
+}
228
+
229
+/**
230
+ *
231
+ */
232
+static int w_ds_next_domain(struct sip_msg *msg, char *str1, char *str2)
233
+{
234
+	return ds_next_dst(msg, 1/*set host port*/);
235
+}
236
+
237
+/**
238
+ *
239
+ */
240
+static int w_ds_mark_dst0(struct sip_msg *msg, char *str1, char *str2)
241
+{
242
+	return ds_mark_dst(msg, 0);
243
+}
244
+
245
+/**
246
+ *
247
+ */
248
+static int w_ds_mark_dst1(struct sip_msg *msg, char *str1, char *str2)
249
+{
250
+	if(str1 && (str1[0]=='i' || str1[0]=='I' || str1[0]=='0'))
251
+		return ds_mark_dst(msg, 0);
252
+	else
253
+		return ds_mark_dst(msg, 1);
135 254
 }
136 255
 
137 256
 /**
... ...
@@ -143,23 +262,134 @@ void destroy(void)
143 262
 	ds_destroy_list();
144 263
 }
145 264
 
265
+/**
266
+ *
267
+ */
146 268
 static int ds_fixup(void** param, int param_no)
147 269
 {
148
-	long n;
149 270
 	int err;
271
+	ds_param_p dsp;
272
+	
150 273
 	if(param_no==1 || param_no==2)
151 274
 	{
152
-		n = str2s(*param, strlen(*param), &err);
153
-		if (err == 0)
275
+		dsp = (ds_param_p)pkg_malloc(sizeof(ds_param_t));
276
+		if(dsp == NULL)
154 277
 		{
155
-			pkg_free(*param);
156
-			*param=(void*)n;
157
-		} else {
158
-			LOG(L_ERR, "DISPATCHER:ds_fixup: Bad number <%s>\n",
159
-			    (char*)(*param));
278
+			LOG(L_ERR, "DISPATCHER:ds_fixup: no more memory\n");
160 279
 			return E_UNSPEC;
161 280
 		}
281
+		memset(dsp, 0, sizeof(ds_param_t));
282
+		if(((char*)(*param))[0]=='$')
283
+		{
284
+			dsp->type = 1;
285
+			if(xl_parse_spec((char*)*param, &dsp->v.sp,
286
+					XL_THROW_ERROR|XL_DISABLE_MULTI|XL_DISABLE_COLORS)==NULL
287
+				|| dsp->v.sp.type!=XL_AVP)
288
+			{
289
+				LOG(L_ERR,
290
+					"DISPATCHER:ds_fixup: Unsupported User Field identifier\n");
291
+				return E_UNSPEC;
292
+			}
293
+		} else {
294
+			dsp->type = 0;
295
+			dsp->v.id = str2s(*param, strlen(*param), &err);
296
+			if (err == 0)
297
+			{
298
+				pkg_free(*param);
299
+			} else {
300
+				LOG(L_ERR, "DISPATCHER:ds_fixup: Bad number <%s>\n",
301
+				    (char*)(*param));
302
+				return E_UNSPEC;
303
+			}
304
+		}
305
+		*param = (void*)dsp;
162 306
 	}
163 307
 	return 0;
164 308
 }
165 309
 
310
+/**
311
+ *
312
+ */
313
+static int ds_fifo_set(FILE *stream, char *response_file)
314
+{
315
+	static char tbuf[256];
316
+	str sp;
317
+	int ret;
318
+	unsigned int group, state;
319
+	
320
+	sp.s = tbuf;
321
+	
322
+	if(!read_line(sp.s, 255, stream, &sp.len) || sp.len==0)	
323
+	{
324
+		LOG(L_ERR, "DISPATCHER:ds_fifo_set: could not read state\n");
325
+		fifo_reply(response_file, "500 ds_fifo_set - state not found\n");
326
+		return 1;
327
+	}
328
+
329
+	if(sp.len<=0)
330
+	{
331
+		LOG(L_ERR, "DISPATCHER:ds_fifo_set: bad state value\n");
332
+		fifo_reply(response_file, "500 ds_fifo_set - bad state value\n");
333
+		return 1;
334
+	}
335
+
336
+	state = 1;
337
+	if(sp.s[0]=='0' || sp.s[0]=='I' || sp.s[0]=='i')
338
+		state = 0;
339
+
340
+	if(!read_line(sp.s, 255, stream, &sp.len) || sp.len==0)	
341
+	{
342
+		LOG(L_ERR, "DISPATCHER:ds_fifo_set: could not read group\n");
343
+		fifo_reply(response_file, "500 ds_fifo_set - group not found\n");
344
+		return 1;
345
+	}
346
+
347
+	if(str2int(&sp, &group))
348
+	{
349
+		LOG(L_ERR, "DISPATCHER:ds_fifo_set: bad group value\n");
350
+		fifo_reply(response_file, "500 ds_fifo_set - bad group value\n");
351
+		return 1;
352
+	}
353
+
354
+	if(!read_line(sp.s, 255, stream, &sp.len) || sp.len==0)	
355
+	{
356
+		LOG(L_ERR, "DISPATCHER:ds_fifo_set: could not read dst address\n");
357
+		fifo_reply(response_file, "500 ds_fifo_set - address not found\n");
358
+		return 1;
359
+	}
360
+
361
+	if(state==1)
362
+		ret = ds_set_state(group, &sp, DS_INACTIVE_DST, 0);
363
+	else
364
+		ret = ds_set_state(group, &sp, DS_INACTIVE_DST, 1);
365
+
366
+	if(ret!=0)
367
+	{
368
+		fifo_reply(response_file, "404 ds_fifo_set - destination not found\n");
369
+		return 1;
370
+	}
371
+	
372
+	fifo_reply(response_file, "200 ds_fifo_set - state updated\n");
373
+	return 0;
374
+}
375
+
376
+/**
377
+ *
378
+ */
379
+static int ds_fifo_list(FILE *stream, char *response_file)
380
+{
381
+	FILE *freply=NULL;
382
+	
383
+	freply = open_reply_pipe(response_file);
384
+	if(freply == NULL)
385
+	{
386
+		LOG(L_ERR, "DISPATCHER:ds_fifo_list: can't open reply fifo\n");
387
+		return -1;
388
+	}
389
+	
390
+	ds_print_list(freply);
391
+
392
+	fclose(freply);
393
+	return 0;
394
+}
395
+
... ...
@@ -38,6 +38,10 @@
38 38
 	    <year>2004</year>
39 39
 	    <holder>&fhg;</holder>
40 40
 	</copyright>
41
+	<copyright>
42
+	    <year>2005</year>
43
+	    <holder>Voice-System.RO</holder>
44
+	</copyright>
41 45
 	<revhistory>
42 46
 	    <revision>
43 47
 		<revnumber>$Revision$</revnumber>
... ...
@@ -100,14 +100,21 @@ modparam("dispatcher", "force_dst", 1)
100 100
  	<section>
101 101
  		<title><varname>flags</varname> (int)</title>
102 102
  		<para>
103
- 		Various flags that affect the hashing behaviour.
104
- 		For now only the flag 1 is defined. If flag 1 is set only the username
103
+ 		Various flags that affect dispatcher's behaviour. The flags are defined
104
+		as a bitmask on an integer value.
105
+ 		If flag 1 is set only the username
105 106
  		part of the uri will be used when computing an uri based hash.
106 107
  		If no flags are set the username, hostname and port will be used
107 108
  		The port is used only if different from 5060 (normal sip uri) or 5061
108 109
  		(in the sips case).
109 110
  		</para>
110 111
  		<para>
112
+		If flag 2 is set, then the failover support is enabled. The functions
113
+		exported by the module will store the rest of addresses from the
114
+		destination set in AVP, and use these AVPs to contact next address when
115
+		the current-tried fails.
116
+ 		</para>
117
+ 		<para>
111 118
  		<emphasis>
112 119
  			Default value is <quote>0</quote>.
113 120
  		</emphasis>
... ...
@@ -116,7 +123,93 @@ modparam("dispatcher", "force_dst", 1)
116 123
  		<title>Set the <quote>flags</quote> parameter</title>
117 124
  <programlisting format="linespecific">
118 125
  ...
119
- modparam("dispatcher", "flags", 1)
126
+ modparam("dispatcher", "flags", 3)
127
+ ...
128
+ </programlisting>
129
+ 		</example>
130
+ 	</section>
131
+ 	<section>
132
+ 		<title><varname>use_default</varname> (int)</title>
133
+ 		<para>
134
+ 		If the parameter is set to 1, the last address in destination set
135
+		is used as last option to send the message. For example, it is good
136
+		when wanting to send the call to an anouncement server saying:
137
+		"the gateways are full, try later".
138
+ 		</para>
139
+ 		<para>
140
+ 		<emphasis>
141
+ 			Default value is <quote>0</quote>.
142
+ 		</emphasis>
143
+ 		</para>
144
+ 		<example>
145
+ 		<title>Set the <quote>use_default</quote> parameter</title>
146
+ <programlisting format="linespecific">
147
+ ...
148
+ modparam("dispatcher", "use_default", 1)
149
+ ...
150
+ </programlisting>
151
+ 		</example>
152
+	</section>
153
+ 	<section>
154
+ 		<title><varname>dst_avp_id</varname> (int)</title>
155
+ 		<para>
156
+ 		The id of the avp which will hold the list with addresses, in the order
157
+		they have been selected by the chosen algorithm. If use_default is 1,
158
+		the value of last dst_avp_id is the last address in destination set. The
159
+		first dst_avp_id is the selected destinations. All the other addresses
160
+		from the destination set will be added in the avp list to be able to
161
+		implement serial forking.
162
+ 		</para>
163
+ 		<para>
164
+ 		<emphasis>
165
+ 			Default value is <quote>271</quote>.
166
+ 		</emphasis>
167
+ 		</para>
168
+ 		<example>
169
+ 		<title>Set the <quote>dst_avp_id</quote> parameter</title>
170
+ <programlisting format="linespecific">
171
+ ...
172
+ modparam("dispatcher", "dst_avp_id", 100)
173
+ ...
174
+ </programlisting>
175
+ 		</example>
176
+ 	</section>
177
+ 	<section>
178
+ 		<title><varname>grp_avp_id</varname> (int)</title>
179
+ 		<para>
180
+ 		The id of the avp storing the group id of the destination set. Good
181
+		to have it for later usage or checks.
182
+ 		</para>
183
+ 		<para>
184
+ 		<emphasis>
185
+ 			Default value is <quote>272</quote>.
186
+ 		</emphasis>
187
+ 		</para>
188
+ 		<example>
189
+ 		<title>Set the <quote>grp_avp_id</quote> parameter</title>
190
+ <programlisting format="linespecific">
191
+ ...
192
+ modparam("dispatcher", "grp_avp_id", 101)
193
+ ...
194
+ </programlisting>
195
+ 		</example>
196
+ 	</section>
197
+ 	<section>
198
+ 		<title><varname>cnt_avp_id</varname> (int)</title>
199
+ 		<para>
200
+ 		The id of the avp storing the number of destination addresses kept in
201
+		dst_avp_id avps.
202
+ 		</para>
203
+ 		<para>
204
+ 		<emphasis>
205
+ 			Default value is <quote>273</quote>.
206
+ 		</emphasis>
207
+ 		</para>
208
+ 		<example>
209
+ 		<title>Set the <quote>cnt_avp_id</quote> parameter</title>
210
+ <programlisting format="linespecific">
211
+ ...
212
+ modparam("dispatcher", "cnt_avp_id", 103)
120 213
  ...
121 214
  </programlisting>
122 215
  		</example>
... ...
@@ -181,6 +274,12 @@ modparam("dispatcher", "force_dst", 1)
181 274
 		</listitem>
182 275
 		</itemizedlist>
183 276
 		<para>
277
+		If the bit 2 in 'flags' is set, the rest of the addresses from the
278
+		destination set is stored in AVP list. You can use 'ds_next_dst()' to
279
+		use next address to achieve serial forking to all possible
280
+		destinations.
281
+		</para>
282
+		<para>
184 283
 		This function can be used from REQUEST_ROUTE.
185 284
 		</para>
186 285
 		<example>
... ...
@@ -202,9 +301,53 @@ ds_select_dst("1", "0");
202 301
  		ds_select_dst().
203 302
  		<para>
204 303
 		<para>
304
+		If the bit 2 in 'flags' is set, the rest of the addresses from the
305
+		destination set is stored in AVP list. You can use 'ds_next_domain()'
306
+		to use next address to achieve serial forking to all possible
307
+		destinations.
308
+		</para>
309
+		<para>
205 310
 		This function can be used from REQUEST_ROUTE.
206 311
 		</para>
207 312
  	</section>
313
+  	<section>
314
+ 		<title>
315
+ 		<function moreinfo="none">ds_next_dst()</function>
316
+ 		</title>
317
+ 		<para>
318
+ 		Takes the next destination address from the AVPs with id 'dst_avp_id'
319
+		and sets the dst_uri (outbound proxy address).
320
+ 		</para>
321
+		<para>
322
+		This function can be used from FAILURE_ROUTE.
323
+		</para>
324
+ 	</section>
325
+  	<section>
326
+ 		<title>
327
+ 		<function moreinfo="none">ds_next_domain()</function>
328
+ 		</title>
329
+ 		<para>
330
+ 		Takes the next destination address from the AVPs with id 'dst_avp_id'
331
+		and sets the domain part of the request uri.
332
+ 		</para>
333
+		<para>
334
+		This function can be used from FAILURE_ROUTE.
335
+		</para>
336
+ 	</section>
337
+  	<section>
338
+ 		<title>
339
+ 		<function moreinfo="none">ds_mark_dst()</function>
340
+ 		</title>
341
+ 		<para>
342
+ 		Mark the last used address from destination set as inactive, in order
343
+		to be ingnored in the future. In this way it can be implemented an
344
+		automatic detection of failed gateways. When an address is marked as
345
+		inactive, it will be ignored by 'ds_select_dst' and 'ds_select_domain'.
346
+ 		</para>
347
+		<para>
348
+		This function can be used from FAILURE_ROUTE.
349
+		</para>
350
+ 	</section>
208 351
 	</section>
209 352
 	<section>
210 353
 	<title>Installation & Running</title>