From 1e6ff26541ad7560559b211d84dc2d5ab6796fd0 Mon Sep 17 00:00:00 2001
From: fcabiddu <federico.cabiddu@gmail.com>
Date: Sun, 2 Nov 2014 09:22:04 +0100
Subject: [PATCH] modules/dispatcher: added per gateway and global send socket
 configuration

---
 modules/dispatcher/README                   | 244 ++++++++++++++++------------
 modules/dispatcher/dispatch.c               | 100 +++++++++++-
 modules/dispatcher/dispatch.h               |   6 +
 modules/dispatcher/dispatcher.c             |  56 ++++++-
 modules/dispatcher/doc/dispatcher_admin.xml |  63 ++++++-
 5 files changed, 354 insertions(+), 115 deletions(-)

diff --git a/modules/dispatcher/README b/modules/dispatcher/README
index 1987823..abab4f4 100644
--- a/modules/dispatcher/README
+++ b/modules/dispatcher/README
@@ -50,20 +50,22 @@ Carsten Bock
               3.13. cnt_avp (str)
               3.14. dstid_avp (str)
               3.15. attrs_avp (str)
-              3.16. hash_pvar (str)
-              3.17. setid_pvname (str)
-              3.18. attrs_pvname (str)
-              3.19. ds_ping_method (string)
-              3.20. ds_ping_from (string)
-              3.21. ds_ping_interval (int)
-              3.22. ds_probing_threshold (int)
-              3.23. ds_ping_reply_codes (string)
-              3.24. ds_probing_mode (int)
-              3.25. ds_hash_size (int)
-              3.26. ds_hash_expire (int)
-              3.27. ds_hash_initexpire (int)
-              3.28. ds_hash_check_interval (int)
-              3.29. outbound_proxy (str)
+              3.16. sock_avp (str)
+              3.17. hash_pvar (str)
+              3.18. setid_pvname (str)
+              3.19. attrs_pvname (str)
+              3.20. ds_ping_method (string)
+              3.21. ds_ping_from (string)
+              3.22. ds_ping_interval (int)
+              3.23. ds_probing_threshold (int)
+              3.24. ds_ping_reply_codes (string)
+              3.25. ds_probing_mode (int)
+              3.26. ds_hash_size (int)
+              3.27. ds_hash_expire (int)
+              3.28. ds_hash_initexpire (int)
+              3.29. ds_hash_check_interval (int)
+              3.30. outbound_proxy (str)
+              3.31. ds_default_socket (str)
 
         4. Functions
 
@@ -117,27 +119,29 @@ Carsten Bock
    1.13. Set the "cnt_avp" parameter
    1.14. Set the "dstid_avp" parameter
    1.15. Set the "attrs_avp" parameter
-   1.16. Use $avp(i:273) for hashing:
-   1.17. Use combination of PVs for hashing:
-   1.18. Set the "setid_pvname" parameter
-   1.19. Set the "attrs_pvname" parameter
-   1.20. Set the "ds_ping_method" parameter
-   1.21. Set the "ds_ping_from" parameter
-   1.22. Set the "ds_ping_interval" parameter
-   1.23. Set the "ds_probing_threshhold" parameter
-   1.24. Set the "ds_ping_reply_codes" parameter
-   1.25. Set the "ds_probing_mode" parameter
-   1.26. Set the "ds_hash_size" parameter
-   1.27. Set the "ds_hash_expire" parameter
-   1.28. Set the "ds_hash_initexpire" parameter
-   1.29. Set the "ds_hash_check_interval" parameter
-   1.30. Set the "outbound_proxy" parameter
-   1.31. ds_select_dst usage
-   1.32. ds_mark_dst usage
-   1.33. ds_mark_dst usage
-   1.34. ds_load_unset usage
-   1.35. dispatcher list file
-   1.36. Kamailio config script - sample dispatcher usage
+   1.16. Set the "sock_avp" parameter
+   1.17. Use $avp(i:273) for hashing:
+   1.18. Use combination of PVs for hashing:
+   1.19. Set the "setid_pvname" parameter
+   1.20. Set the "attrs_pvname" parameter
+   1.21. Set the "ds_ping_method" parameter
+   1.22. Set the "ds_ping_from" parameter
+   1.23. Set the "ds_ping_interval" parameter
+   1.24. Set the "ds_probing_threshhold" parameter
+   1.25. Set the "ds_ping_reply_codes" parameter
+   1.26. Set the "ds_probing_mode" parameter
+   1.27. Set the "ds_hash_size" parameter
+   1.28. Set the "ds_hash_expire" parameter
+   1.29. Set the "ds_hash_initexpire" parameter
+   1.30. Set the "ds_hash_check_interval" parameter
+   1.31. Set the "outbound_proxy" parameter
+   1.32. Set the "ds_default_socket" parameter
+   1.33. ds_select_dst usage
+   1.34. ds_mark_dst usage
+   1.35. ds_mark_dst usage
+   1.36. ds_load_unset usage
+   1.37. dispatcher list file
+   1.38. Kamailio config script - sample dispatcher usage
 
 Chapter 1. Admin Guide
 
@@ -166,20 +170,22 @@ Chapter 1. Admin Guide
         3.13. cnt_avp (str)
         3.14. dstid_avp (str)
         3.15. attrs_avp (str)
-        3.16. hash_pvar (str)
-        3.17. setid_pvname (str)
-        3.18. attrs_pvname (str)
-        3.19. ds_ping_method (string)
-        3.20. ds_ping_from (string)
-        3.21. ds_ping_interval (int)
-        3.22. ds_probing_threshold (int)
-        3.23. ds_ping_reply_codes (string)
-        3.24. ds_probing_mode (int)
-        3.25. ds_hash_size (int)
-        3.26. ds_hash_expire (int)
-        3.27. ds_hash_initexpire (int)
-        3.28. ds_hash_check_interval (int)
-        3.29. outbound_proxy (str)
+        3.16. sock_avp (str)
+        3.17. hash_pvar (str)
+        3.18. setid_pvname (str)
+        3.19. attrs_pvname (str)
+        3.20. ds_ping_method (string)
+        3.21. ds_ping_from (string)
+        3.22. ds_ping_interval (int)
+        3.23. ds_probing_threshold (int)
+        3.24. ds_ping_reply_codes (string)
+        3.25. ds_probing_mode (int)
+        3.26. ds_hash_size (int)
+        3.27. ds_hash_expire (int)
+        3.28. ds_hash_initexpire (int)
+        3.29. ds_hash_check_interval (int)
+        3.30. outbound_proxy (str)
+        3.31. ds_default_socket (str)
 
    4. Functions
 
@@ -265,20 +271,22 @@ Chapter 1. Admin Guide
    3.13. cnt_avp (str)
    3.14. dstid_avp (str)
    3.15. attrs_avp (str)
-   3.16. hash_pvar (str)
-   3.17. setid_pvname (str)
-   3.18. attrs_pvname (str)
-   3.19. ds_ping_method (string)
-   3.20. ds_ping_from (string)
-   3.21. ds_ping_interval (int)
-   3.22. ds_probing_threshold (int)
-   3.23. ds_ping_reply_codes (string)
-   3.24. ds_probing_mode (int)
-   3.25. ds_hash_size (int)
-   3.26. ds_hash_expire (int)
-   3.27. ds_hash_initexpire (int)
-   3.28. ds_hash_check_interval (int)
-   3.29. outbound_proxy (str)
+   3.16. sock_avp (str)
+   3.17. hash_pvar (str)
+   3.18. setid_pvname (str)
+   3.19. attrs_pvname (str)
+   3.20. ds_ping_method (string)
+   3.21. ds_ping_from (string)
+   3.22. ds_ping_interval (int)
+   3.23. ds_probing_threshold (int)
+   3.24. ds_ping_reply_codes (string)
+   3.25. ds_probing_mode (int)
+   3.26. ds_hash_size (int)
+   3.27. ds_hash_expire (int)
+   3.28. ds_hash_initexpire (int)
+   3.29. ds_hash_check_interval (int)
+   3.30. outbound_proxy (str)
+   3.31. ds_default_socket (str)
 
 3.1. list_file (string)
 
@@ -492,7 +500,24 @@ Note
  modparam("dispatcher", "attrs_avp", "$avp(dsattrs)")
  ...
 
-3.16. hash_pvar (str)
+3.16. sock_avp (str)
+
+   The name of the avp which will hold the list with the sockets
+   associated to the addresses stored in dst_avp avp.
+
+Note
+
+   If you want to do load balancing fail over, you have to set this
+   parameter to use the correct socket for each gateway.
+
+   Default value is "null" - don't add AVPs.
+
+   Example 1.16. Set the "sock_avp" parameter
+ ...
+ modparam("dispatcher", "sock_avp", "$avp(dssocket)")
+ ...
+
+3.17. hash_pvar (str)
 
    String with PVs used for the hashing algorithm 7.
 
@@ -503,41 +528,41 @@ Note
 
    Default value is "null" - disabled.
 
-   Example 1.16. Use $avp(i:273) for hashing:
+   Example 1.17. Use $avp(i:273) for hashing:
  ...
  modparam("dispatcher", "hash_pvar", "$avp(i:273)")
  ...
 
-   Example 1.17. Use combination of PVs for hashing:
+   Example 1.18. Use combination of PVs for hashing:
  ...
  modparam("dispatcher", "hash_pvar", "hash the $fU@$ci")
  ...
 
-3.17. setid_pvname (str)
+3.18. setid_pvname (str)
 
    The name of the PV where to store the set ID (group ID) when calling
    ds_is_from_list() with no parameter.
 
    Default value is "null" - don't set PV.
 
-   Example 1.18. Set the "setid_pvname" parameter
+   Example 1.19. Set the "setid_pvname" parameter
  ...
  modparam("dispatcher", "setid_pvname", "$var(setid)")
  ...
 
-3.18. attrs_pvname (str)
+3.19. attrs_pvname (str)
 
    The name of the PV where to store the attributes of matching address
    when calling ds_is_from_list().
 
    Default value is "null" - don't set PV.
 
-   Example 1.19. Set the "attrs_pvname" parameter
+   Example 1.20. Set the "attrs_pvname" parameter
  ...
  modparam("dispatcher", "attrs_pvname", "$var(attrs)")
  ...
 
-3.19. ds_ping_method (string)
+3.20. ds_ping_method (string)
 
    With this method you can define, with which method you want to probe
    the gateways. Pinging gateways feature depends on ds_ping_interval
@@ -545,12 +570,12 @@ Note
 
    Default value is "OPTIONS".
 
-   Example 1.20. Set the "ds_ping_method" parameter
+   Example 1.21. Set the "ds_ping_method" parameter
  ...
  modparam("dispatcher", "ds_ping_method", "INFO")
  ...
 
-3.20. ds_ping_from (string)
+3.21. ds_ping_from (string)
 
    With this Method you can define the "From:"-Line for the request, sent
    to the failed gateways. This method is only available, if compiled with
@@ -558,12 +583,12 @@ Note
 
    Default value is "sip:dispatcher@localhost".
 
-   Example 1.21. Set the "ds_ping_from" parameter
+   Example 1.22. Set the "ds_ping_from" parameter
  ...
  modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com")
  ...
 
-3.21. ds_ping_interval (int)
+3.22. ds_ping_interval (int)
 
    With this parameter you can define the interval for sending a request
    to a gateway marked as inactive upon a failed request routing to it.
@@ -572,12 +597,12 @@ Note
 
    Default value is "0".
 
-   Example 1.22. Set the "ds_ping_interval" parameter
+   Example 1.23. Set the "ds_ping_interval" parameter
  ...
  modparam("dispatcher", "ds_ping_interval", 30)
  ...
 
-3.22. ds_probing_threshold (int)
+3.23. ds_probing_threshold (int)
 
    If you want to set a gateway into inactive mode, there can be a
    specific number of failed requests until it will change from "active"
@@ -588,12 +613,12 @@ Note
 
    Default value is "1" (set inactive with first failure).
 
-   Example 1.23. Set the "ds_probing_threshhold" parameter
+   Example 1.24. Set the "ds_probing_threshhold" parameter
  ...
  modparam("dispatcher", "ds_probing_threshhold", 10)
  ...
 
-3.23. ds_ping_reply_codes (string)
+3.24. ds_ping_reply_codes (string)
 
    This parameter defines the valid response codes, which are accepted as
    a valid reply to the PING-Method. It is a list separated by colons,
@@ -605,13 +630,13 @@ Note
 
    Default value is "" (only 200 OK is accepted).
 
-   Example 1.24. Set the "ds_ping_reply_codes" parameter
+   Example 1.25. Set the "ds_ping_reply_codes" parameter
  ...
  modparam("dispatcher", "ds_ping_reply_codes", "class=2;code=403;code=488;class=
 3")
  ...
 
-3.24. ds_probing_mode (int)
+3.25. ds_probing_mode (int)
 
    Controls what gateways are tested to see if they are reachable. If set
    to 0, only the gateways with state PROBING are tested; if set to 1, all
@@ -621,12 +646,12 @@ Note
 
    Default value is "0".
 
-   Example 1.25. Set the "ds_probing_mode" parameter
+   Example 1.26. Set the "ds_probing_mode" parameter
  ...
  modparam("dispatcher", "ds_probing_mode", 1)
  ...
 
-3.25. ds_hash_size (int)
+3.26. ds_hash_size (int)
 
    The value to be used as power of two to set the number of slots to hash
    table storing data for call load dispatching (e.g., value 8 will create
@@ -635,24 +660,24 @@ Note
 
    Default value is "0".
 
-   Example 1.26. Set the "ds_hash_size" parameter
+   Example 1.27. Set the "ds_hash_size" parameter
  ...
  modparam("dispatcher", "ds_hash_size", 9)
  ...
 
-3.26. ds_hash_expire (int)
+3.27. ds_hash_expire (int)
 
    Expiration time in seconds to remove the load on a destination if no
    BYE was received meanwhile.
 
    Default value is "7200".
 
-   Example 1.27. Set the "ds_hash_expire" parameter
+   Example 1.28. Set the "ds_hash_expire" parameter
  ...
  modparam("dispatcher", "ds_hash_expire", 3600)
  ...
 
-3.27. ds_hash_initexpire (int)
+3.28. ds_hash_initexpire (int)
 
    Expiration time in seconds to remove the load on a destination if no
    200 for INVITE was received meanwhile and state updated with
@@ -660,34 +685,47 @@ Note
 
    Default value is "7200".
 
-   Example 1.28. Set the "ds_hash_initexpire" parameter
+   Example 1.29. Set the "ds_hash_initexpire" parameter
  ...
  modparam("dispatcher", "ds_hash_initexpire", 60)
  ...
 
-3.28. ds_hash_check_interval (int)
+3.29. ds_hash_check_interval (int)
 
    Time interval in seconds to scan internal hash table with call load
    dispatching data for expired items.
 
    Default value is "30".
 
-   Example 1.29. Set the "ds_hash_check_interval" parameter
+   Example 1.30. Set the "ds_hash_check_interval" parameter
  ...
  modparam("dispatcher", "ds_hash_check_interval", 60)
  ...
 
-3.29. outbound_proxy (str)
+3.30. outbound_proxy (str)
 
    SIP URI of outbound proxy to be used when sending pings.
 
    By default no outbound proxy is defined.
 
-   Example 1.30. Set the "outbound_proxy" parameter
+   Example 1.31. Set the "outbound_proxy" parameter
  ...
  modparam("dispatcher", "outbound_proxy", "sip:outbound.example.com")
  ...
 
+3.31. ds_default_socket (str)
+
+   Default socket to be used for sending pings and dispatching requests
+   when a gateway has no send socket configured.
+
+   By default no default socket is defined, the first configuration script
+   listen directive is used.
+
+   Example 1.32. Set the "ds_default_socket" parameter
+ ...
+ modparam("dispatcher", "ds_default_socket", "udp:192.168.0.125:5060")
+ ...
+
 4. Functions
 
    4.1. ds_select_dst(set, alg[, limit])
@@ -760,7 +798,7 @@ Note
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.31. ds_select_dst usage
+   Example 1.33. ds_select_dst usage
 ...
 ds_select_dst("1", "0");
 ...
@@ -824,7 +862,7 @@ ds_select_dst("1", "4", "3");
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
-   Example 1.32. ds_mark_dst usage
+   Example 1.34. ds_mark_dst usage
 ...
 failure_route[tryagain] {
 ...
@@ -870,7 +908,7 @@ failure_route[tryagain] {
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.33. ds_mark_dst usage
+   Example 1.35. ds_mark_dst usage
 ...
 if(ds_is_from_list()) {
     ...
@@ -901,7 +939,7 @@ if(ds_is_from_list("10", "sip:127.0.0.1:5080", "3")) {
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE and ONREPLY_ROUTE.
 
-   Example 1.34. ds_load_unset usage
+   Example 1.36. ds_load_unset usage
 ...
 route {
     ...
@@ -1056,9 +1094,11 @@ kamcmd dispatcher.set_state ip 2 sip:127.0.0.1:5080
        probing destination (sending keep alives);
      * priority: sets the priority in destination list (based on it is
        done the initial ordering inside the set)
-     * attributes: extra filed in form of name1=value1;...;nameN=valueN.
-       There are some predefined names that are used of weight and call
-       load dispatching.
+     * attributes: extra field in form of name1=value1;...;nameN=valueN.
+       There are some predefined names:
+          + 'duid' - used for call load dispatching
+          + 'weight' - used for weight based load distribution
+          + 'socket' - used to set the sending socket for the gateway
 
    Line format is:
 ...
@@ -1067,13 +1107,13 @@ setid(int) destination(sip uri) flags(int,opt) priority(int,opt) attrs(str,opt)
 
    Full line example:
 ...
-1 sip:127.0.0.1:5080 0 0 duid=abc;my=xyz
+1 sip:127.0.0.1:5080 0 0 duid=abc;socket=udp:192.168.0.125:5060;my=xyz
 ...
 
    For database, each element of a line resides in a different column.
    Next is a dispatcher.list file example:
 
-   Example 1.35. dispatcher list file
+   Example 1.37. dispatcher list file
 ...
 # $Id$
 # dispatcher destination sets
@@ -1098,7 +1138,7 @@ r,opt)
 
    Next picture shows a sample usage of the dispatcher module.
 
-   Example 1.36. Kamailio config script - sample dispatcher usage
+   Example 1.38. Kamailio config script - sample dispatcher usage
 ...
 #!KAMAILIO
 #
diff --git a/modules/dispatcher/dispatch.c b/modules/dispatcher/dispatch.c
index aa8194f..71e0463 100644
--- a/modules/dispatcher/dispatch.c
+++ b/modules/dispatcher/dispatch.c
@@ -232,6 +232,9 @@ int ds_set_attrs(ds_dest_t *dest, str *attrs)
 		} else if(pit->name.len==7
 				&& strncasecmp(pit->name.s, "maxload", 7)==0) {
 			str2sint(&pit->body, &dest->attrs.maxload);
+		} else if(pit->name.len==6
+				&& strncasecmp(pit->name.s, "socket", 6)==0) {
+			dest->attrs.socket = pit->body;
 		}
 	}
 	return 0;
@@ -253,6 +256,8 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 	struct hostent* he;
 	struct sip_uri puri;
 	int orig_id = 0, orig_nr = 0;
+	str host;
+	int port, proto;
 	ds_set_t *orig_ds_lists = ds_lists[list_idx];
 
 	/* check uri */
@@ -308,6 +313,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 	strncpy(dp->uri.s, uri.s, uri.len);
 	dp->uri.s[uri.len]='\0';
 	dp->uri.len = uri.len;
+
 	dp->flags = flags;
 	dp->priority = priority;
 
@@ -317,6 +323,22 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 		goto err;
 	}
 
+	/* check socket attribute */
+	if (dp->attrs.socket.s && dp->attrs.socket.len > 0) {
+		if (parse_phostport(dp->attrs.socket.s, &host.s, &host.len,
+				&port, &proto)!=0) {
+			LM_ERR("bad socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);
+			goto err;
+		}
+		dp->sock = grep_sock_info( &host, (unsigned short)port, proto);
+		if (dp->sock==0) {
+			LM_ERR("non-local socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);
+			goto err;
+		}
+	} else if (ds_default_sockinfo) {
+		dp->sock = ds_default_sockinfo;
+	}
+
 	/* The Hostname needs to be \0 terminated for resolvehost, so we
 	 * make a copy here. */
 	strncpy(hn, puri.host.s, puri.host.len);
@@ -573,11 +595,11 @@ int ds_load_list(char *lfile)
 		while(*p && (*p==' ' || *p=='\t' || *p=='\r' || *p=='\n'))
 			p++;
 		if(*p=='\0' || *p=='#')
-			goto add_destination; /* no priority given */
+			goto add_destination; /* no attrs given */
 
 		/* get attributes */
 		attrs.s = p;
-		while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n' && *p!='#')
+		while(*p && *p!=' ' && *p!='\t' && *p!='\r' && *p!='\n')
 			p++;
 		attrs.len = p-attrs.s;
 
@@ -1549,7 +1571,8 @@ int ds_load_unset(struct sip_msg *msg)
 /**
  *
  */
-static inline int ds_update_dst(struct sip_msg *msg, str *uri, int mode)
+static inline int ds_update_dst(struct sip_msg *msg, str *uri,
+								struct socket_info *sock, int mode)
 {
 	struct action act;
 	struct run_act_ctx ra_ctx;
@@ -1580,6 +1603,8 @@ static inline int ds_update_dst(struct sip_msg *msg, str *uri, int mode)
 			ruri_mark_new(); /* re-use uri for serial forking */
 			break;
 	}
+	if (sock)
+		msg->force_send_socket = sock;
 	return 0;
 }
 
@@ -1597,6 +1622,7 @@ int ds_select_dst_limit(struct sip_msg *msg, int set, int alg, unsigned int limi
 	unsigned int hash;
 	int_str avp_val;
 	ds_set_t *idx = NULL;
+	char buf[2+16+1];
 
 	if(msg==NULL)
 	{
@@ -1766,7 +1792,7 @@ int ds_select_dst_limit(struct sip_msg *msg, int set, int alg, unsigned int limi
 
 	hash = i;
 
-	if(ds_update_dst(msg, &idx->dlist[hash].uri, mode)!=0)
+	if(ds_update_dst(msg, &idx->dlist[hash].uri, idx->dlist[hash].sock, mode)!=0)
 	{
 		LM_ERR("cannot set dst addr\n");
 		return -1;
@@ -1797,6 +1823,17 @@ int ds_select_dst_limit(struct sip_msg *msg, int set, int alg, unsigned int limi
 							avp_val)!=0)
 					return -1;
 			}
+
+			/* only add sock_avp if dst_avp is set */
+			if(sock_avp_name.n!=0 && idx->dlist[idx->nr-1].sock)
+			{
+				avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[idx->nr-1].sock);
+				avp_val.s.s = buf;
+				if(add_avp(AVP_VAL_STR|sock_avp_type, sock_avp_name,
+							avp_val)!=0)
+					return -1;
+			}
+
 			if(alg==DS_ALG_LOAD)
 			{
 				if(idx->dlist[idx->nr-1].attrs.duid.len<=0)
@@ -1833,6 +1870,16 @@ int ds_select_dst_limit(struct sip_msg *msg, int set, int alg, unsigned int limi
 							avp_val)!=0)
 					return -1;
 			}
+
+			if(sock_avp_name.n!=0 && idx->dlist[i].sock)
+			{
+				avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[i].sock);
+				avp_val.s.s = buf;
+				if(add_avp(AVP_VAL_STR|sock_avp_type, sock_avp_name,
+							avp_val)!=0)
+					return -1;
+			}
+
 			if(alg==DS_ALG_LOAD)
 			{
 				if(idx->dlist[i].attrs.duid.len<=0)
@@ -1867,6 +1914,16 @@ int ds_select_dst_limit(struct sip_msg *msg, int set, int alg, unsigned int limi
 							avp_val)!=0)
 					return -1;
 			}
+
+			if(sock_avp_name.n!=0 && idx->dlist[i].sock)
+			{
+				avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[i].sock);
+				avp_val.s.s = buf;
+				if(add_avp(AVP_VAL_STR|sock_avp_type, sock_avp_name,
+							avp_val)!=0)
+					return -1;
+			}
+
 			if(alg==DS_ALG_LOAD)
 			{
 				if(idx->dlist[i].attrs.duid.len<=0)
@@ -1896,6 +1953,15 @@ int ds_select_dst_limit(struct sip_msg *msg, int set, int alg, unsigned int limi
 						avp_val)!=0)
 				return -1;
 		}
+		if(sock_avp_name.n!=0 && idx->dlist[hash].sock)
+		{
+			avp_val.s.len = 1 + sprintf(buf, "%p", idx->dlist[hash].sock);
+			avp_val.s.s = buf;
+			if(add_avp(AVP_VAL_STR|sock_avp_type, sock_avp_name,
+						avp_val)!=0)
+				return -1;
+		}
+
 		if(alg==DS_ALG_LOAD)
 		{
 			if(idx->dlist[hash].attrs.duid.len<=0)
@@ -1937,7 +2003,9 @@ int ds_next_dst(struct sip_msg *msg, int mode)
 	struct search_state st;
 	struct usr_avp *avp;
 	struct usr_avp *prev_avp;
+	struct socket_info *sock;
 	int_str avp_value;
+	int_str sock_avp_value;
 	int alg = 0;
 
 	if(!(ds_flags&DS_FAILOVER_ON) || dst_avp_name.n==0)
@@ -1969,6 +2037,20 @@ int ds_next_dst(struct sip_msg *msg, int mode)
 		}
 	}
 
+	if(sock_avp_name.n!=0)
+	{
+		prev_avp = search_first_avp(sock_avp_type,
+				attrs_avp_name, &avp_value, &st);
+		if(prev_avp!=NULL)
+		{
+			if (sscanf( sock_avp_value.s.s, "%p", (void**)&sock ) != 1)
+				sock = NULL;
+			destroy_avp(prev_avp);
+		} else {
+			sock = NULL;
+		}
+	}
+
 	prev_avp = search_first_avp(dst_avp_type, dst_avp_name, &avp_value, &st);
 	if(prev_avp==NULL)
 		return -1; /* used avp deleted -- strange */
@@ -1978,7 +2060,7 @@ int ds_next_dst(struct sip_msg *msg, int mode)
 	if(avp==NULL || !(avp->flags&AVP_VAL_STR))
 		return -1; /* no more avps or value is int */
 
-	if(ds_update_dst(msg, &avp_value.s, mode)!=0)
+	if(ds_update_dst(msg, &avp_value.s, sock, mode)!=0)
 	{
 		LM_ERR("cannot set dst addr\n");
 		return -1;
@@ -2374,6 +2456,9 @@ int ds_print_mi_list(struct mi_node* rpl)
 			if(node == NULL)
 				return -1;
 
+			if(attr == 0)
+				return -1;
+
 			memset(&c, 0, sizeof(c));
 			if (list->dlist[j].flags & DS_INACTIVE_DST)
 				c[0] = 'I';
@@ -2512,6 +2597,11 @@ void ds_check_timer(unsigned int ticks, void* param)
 				set_uac_req(&uac_r, &ds_ping_method, 0, 0, 0,
 						TMCB_LOCAL_COMPLETED, ds_options_callback,
 						(void*)(long)list->id);
+				if (list->dlist[j].attrs.socket.s != NULL && list->dlist[j].attrs.socket.len > 0) {
+					uac_r.ssock = &list->dlist[j].attrs.socket;
+				} else if (ds_default_socket.s != NULL && ds_default_socket.len > 0) {
+					uac_r.ssock = &ds_default_socket;
+				}
 				if (tmb.t_request(&uac_r,
 							&list->dlist[j].uri,
 							&list->dlist[j].uri,
diff --git a/modules/dispatcher/dispatch.h b/modules/dispatcher/dispatch.h
index e0466d8..f6a950a 100644
--- a/modules/dispatcher/dispatch.h
+++ b/modules/dispatcher/dispatch.h
@@ -86,6 +86,8 @@ extern int_str dstid_avp_name;
 extern unsigned short dstid_avp_type;
 extern int_str attrs_avp_name;
 extern unsigned short attrs_avp_type;
+extern int_str sock_avp_name;
+extern unsigned short sock_avp_type;
 
 extern pv_elem_t * hash_param_model;
 
@@ -102,6 +104,8 @@ extern int probing_threshhold; /*!< number of failed requests,
 								 before a destination is taken into probing */ 
 extern int ds_probing_mode;
 extern str ds_outbound_proxy;
+extern str ds_default_socket;
+extern struct socket_info * ds_default_sockinfo;
 
 int init_data(void);
 int init_ds_db(void);
@@ -150,6 +154,7 @@ typedef struct _ds_attrs
 {
 	str body;
 	str duid;
+	str socket;
 	int maxload;
 	int weight;
 } ds_attrs_t;
@@ -161,6 +166,7 @@ typedef struct _ds_dest
 	int priority;
 	int dload;
 	ds_attrs_t attrs;
+	struct socket_info * sock;
 	struct ip_addr ip_address; 	/*!< IP-Address of the entry */
 	unsigned short int port; 	/*!< Port of the URI */
 	unsigned short int proto; 	/*!< Protocol of the URI */
diff --git a/modules/dispatcher/dispatcher.c b/modules/dispatcher/dispatcher.c
index 13f3f56..769a415 100644
--- a/modules/dispatcher/dispatcher.c
+++ b/modules/dispatcher/dispatcher.c
@@ -89,6 +89,7 @@ static str grp_avp_param = {NULL, 0};
 static str cnt_avp_param = {NULL, 0};
 static str dstid_avp_param = {NULL, 0};
 static str attrs_avp_param = {NULL, 0};
+static str sock_avp_param = {NULL, 0};
 str hash_pvar_param = {NULL, 0};
 
 int_str dst_avp_name;
@@ -101,6 +102,8 @@ int_str dstid_avp_name;
 unsigned short dstid_avp_type;
 int_str attrs_avp_name;
 unsigned short attrs_avp_type;
+int_str sock_avp_name;
+unsigned short sock_avp_type;
 
 pv_elem_t * hash_param_model = NULL;
 
@@ -115,6 +118,9 @@ static str ds_ping_reply_codes_str= {NULL, 0};
 static int** ds_ping_reply_codes = NULL;
 static int* ds_ping_reply_codes_cnt;
 
+str ds_default_socket       = {NULL, 0};
+struct socket_info * ds_default_sockinfo = NULL;
+
 int ds_hash_size = 0;
 int ds_hash_expire = 7200;
 int ds_hash_initexpire = 7200;
@@ -224,6 +230,7 @@ static param_export_t params[]={
 	{"cnt_avp",         PARAM_STR, &cnt_avp_param},
 	{"dstid_avp",       PARAM_STR, &dstid_avp_param},
 	{"attrs_avp",       PARAM_STR, &attrs_avp_param},
+	{"sock_avp",        PARAM_STR, &sock_avp_param},
 	{"hash_pvar",       PARAM_STR, &hash_pvar_param},
 	{"setid_pvname",    PARAM_STR, &ds_setid_pvname},
 	{"attrs_pvname",    PARAM_STR, &ds_attrs_pvname},
@@ -238,6 +245,7 @@ static param_export_t params[]={
 	{"ds_hash_initexpire", INT_PARAM, &ds_hash_initexpire},
 	{"ds_hash_check_interval", INT_PARAM, &ds_hash_check_interval},
 	{"outbound_proxy",  PARAM_STR, &ds_outbound_proxy},
+	{"ds_default_socket",  PARAM_STR, &ds_default_socket},
 	{0,0,0}
 };
 
@@ -272,6 +280,8 @@ struct module_exports exports= {
 static int mod_init(void)
 {
 	pv_spec_t avp_spec;
+	str host;
+	int port, proto;
 
 	if(register_mi_mod(exports.name, mi_cmds)!=0)
 	{
@@ -432,6 +442,28 @@ static int mod_init(void)
 		attrs_avp_type = 0;
 	}
 
+	if (sock_avp_param.s && sock_avp_param.len > 0)
+	{
+		if (pv_parse_spec(&sock_avp_param, &avp_spec)==0
+				|| avp_spec.type!=PVT_AVP)
+		{
+			LM_ERR("malformed or non AVP %.*s AVP definition\n",
+					sock_avp_param.len, sock_avp_param.s);
+			return -1;
+		}
+
+		if(pv_get_avp_name(0, &(avp_spec.pvp), &sock_avp_name,
+					&sock_avp_type)!=0)
+		{
+			LM_ERR("[%.*s]- invalid AVP definition\n", sock_avp_param.len,
+					sock_avp_param.s);
+			return -1;
+		}
+	} else {
+		sock_avp_name.n = 0;
+		sock_avp_type = 0;
+	}
+
 	if (hash_pvar_param.s && *hash_pvar_param.s) {
 		if(pv_parse_format(&hash_pvar_param, &hash_param_model) < 0
 				|| hash_param_model==NULL) {
@@ -493,11 +525,25 @@ static int mod_init(void)
 		register_timer(ds_check_timer, NULL, ds_ping_interval);
 	}
 
+	if (ds_default_socket.s && ds_default_socket.len > 0) {
+ 		if (parse_phostport( ds_default_socket.s, &host.s, &host.len,
+				&port, &proto)!=0) {
+			LM_ERR("bad socket <%.*s>\n", ds_default_socket.len, ds_default_socket.s);
+			return -1;
+		}
+		ds_default_sockinfo = grep_sock_info( &host, (unsigned short)port, proto);
+		if (ds_default_sockinfo==0) {
+			LM_WARN("non-local socket <%.*s>\n", ds_default_socket.len, ds_default_socket.s);
+			return -1;
+		}
+		LM_INFO("default dispatcher socket set to <%.*s>\n", ds_default_socket.len, ds_default_socket.s);
+	}
+
 	return 0;
 }
 
 /*! \brief
- * Initialize children
+ * Initialize childreng
  */
 static int child_init(int rank)
 {
@@ -720,7 +766,7 @@ static int w_ds_load_update(struct sip_msg *msg, char *str1, char *str2)
  */
 static int ds_warn_fixup(void** param, int param_no)
 {
-	if(!dst_avp_param.s || !grp_avp_param.s || !cnt_avp_param.s)
+	if(!dst_avp_param.s || !grp_avp_param.s || !cnt_avp_param.s || !sock_avp_param.s)
 	{
 		LM_ERR("failover functions used, but AVPs paraamters required"
 				" are NULL -- feature disabled\n");
@@ -1149,12 +1195,14 @@ static void dispatcher_rpc_list(rpc_t* rpc, void* ctx)
 					rpc->fault(ctx, 500, "Internal error creating dest struct");
 					return;
 				}
-				if(rpc->struct_add(wh, "SSdd",
+				if(rpc->struct_add(wh, "SSddS",
 							"BODY", &(list->dlist[j].attrs.body),
 							"DUID", (list->dlist[j].attrs.duid.s)?
 							&(list->dlist[j].attrs.duid):&data,
 							"MAXLOAD", list->dlist[j].attrs.maxload,
-							"WEIGHT", list->dlist[j].attrs.weight)<0)
+							"WEIGHT", list->dlist[j].attrs.weight,
+							"SOCKET", (list->dlist[j].attrs.socket.s)?
+							&(list->dlist[j].attrs.socket):&data)<0)
 				{
 					rpc->fault(ctx, 500, "Internal error creating attrs struct");
 					return;
diff --git a/modules/dispatcher/doc/dispatcher_admin.xml b/modules/dispatcher/doc/dispatcher_admin.xml
index f4e0eaf..a7575f4 100644
--- a/modules/dispatcher/doc/dispatcher_admin.xml
+++ b/modules/dispatcher/doc/dispatcher_admin.xml
@@ -421,6 +421,30 @@ modparam("dispatcher", "force_dst", 1)
  </programlisting>
  		</example>
  	</section>
+ 	<section id="dispatcher.p.sock_avp">
+ 		<title><varname>sock_avp</varname> (str)</title>
+ 		<para>
+			The name of the avp which will hold the list with the sockets associated to the addresses stored in dst_avp avp.
+ 		</para>
+		<note>
+ 		<para>
+			If you want to do load balancing fail over, you have to set this parameter to use the correct socket for each gateway.
+ 		</para>
+		</note>
+ 		<para>
+ 		<emphasis>
+ 			Default value is <quote>null</quote> - don't add AVPs.
+ 		</emphasis>
+ 		</para>
+ 		<example>
+ 		<title>Set the <quote>sock_avp</quote> parameter</title>
+ <programlisting format="linespecific">
+ ...
+ modparam("dispatcher", "sock_avp", "$avp(dssocket)")
+ ...
+ </programlisting>
+ 		</example>
+ 	</section>
  	<section id="dispatcher.p.hash_pvar">
  		<title><varname>hash_pvar</varname> (str)</title>
  		<para>
@@ -731,6 +755,26 @@ modparam("dispatcher", "force_dst", 1)
  		</example>
 	</section>
 
+ 	<section id="dispatcher.p.ds_default_socket">
+ 		<title><varname>ds_default_socket</varname> (str)</title>
+ 		<para>
+		Default socket to be used for sending pings and dispatching requests when a gateway has no send socket configured.
+ 		</para>
+ 		<para>
+ 		<emphasis>
+			By default no default socket is defined, the first configuration script <emphasis>listen</emphasis> directive is used.
+ 		</emphasis>
+ 		</para>
+ 		<example>
+ 		<title>Set the <quote>ds_default_socket</quote> parameter</title>
+ <programlisting format="linespecific">
+ ...
+ modparam("dispatcher", "ds_default_socket", "udp:192.168.0.125:5060")
+ ...
+ </programlisting>
+ 		</example>
+	</section>
+
 	</section>
 
 	<section>
@@ -1321,9 +1365,20 @@ onreply_route {
 			is done the initial ordering inside the set)</para>
 			</listitem>
 			<listitem>
-			<para>attributes: extra filed in form of
-			name1=value1;...;nameN=valueN. There are some predefined names
-			that are used of weight and call load dispatching.</para>
+			<para>attributes: extra fields in form of
+				name1=value1;...;nameN=valueN. There are some predefined names:
+				<itemizedlist>
+					<listitem>
+						'duid' - used for call load dispatching
+					</listitem>
+					<listitem>
+						'weight' - used for weight based load distribution
+					</listitem>
+					<listitem>
+						'socket' - used to set the sending socket for the gateway
+					</listitem>
+				</itemizedlist>
+			</para>
 			</listitem>
 		</itemizedlist>
 		<para>
@@ -1340,7 +1395,7 @@ setid(int) destination(sip uri) flags(int,opt) priority(int,opt) attrs(str,opt)
 		</para>
 		<programlisting format="linespecific">
 ...
-1 sip:127.0.0.1:5080 0 0 duid=abc;my=xyz
+1 sip:127.0.0.1:5080 0 0 duid=abc;socket=udp:192.168.0.125:5060;my=xyz
 ...
 </programlisting>
 
-- 
2.1.0

