Przeglądaj źródła

modules/lcr: added capability to inactive a faulty gateway and re-activate
it automatically by successful response to OPTIONS ping

Juha Heinanen 11 lat temu
rodzic
commit
85fdd590ca

+ 160 - 45
modules/lcr/README

@@ -10,7 +10,7 @@ Juha Heinanen
 
 
    <[email protected]>
    <[email protected]>
 
 
-   Copyright © 2005-2010 Juha Heinanen
+   Copyright (c) 2005-2014 Juha Heinanen
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
@@ -63,16 +63,22 @@ Juha Heinanen
               3.36. lcr_gw_count (integer)
               3.36. lcr_gw_count (integer)
               3.37. dont_strip_or_tag_flag (integer)
               3.37. dont_strip_or_tag_flag (integer)
               3.38. fetch_rows (integer)
               3.38. fetch_rows (integer)
+              3.39. ping_interval (integer)
+              3.40. ping_inactive_threshold (integer)
+              3.41. ping_valid_reply_codes (string)
+              3.42. ping_from (string)
+              3.43. ping_socket (string)
 
 
         4. Functions
         4. Functions
 
 
               4.1. load_gws(lcr_id[, uri_user[, caller_uri]])
               4.1. load_gws(lcr_id[, uri_user[, caller_uri]])
               4.2. next_gw()
               4.2. next_gw()
-              4.3. defunct_gw(period)
-              4.4. from_gw(lcr_id[, ip_addr, proto])
-              4.5. from_any_gw([ip_addr, proto])
-              4.6. to_gw(lcr_id[, ip_addr, proto])
-              4.7. to_any_gw([ip_addr, proto])
+              4.3. inactivate_gw()
+              4.4. defunct_gw(period)
+              4.5. from_gw(lcr_id[, ip_addr, proto])
+              4.6. from_any_gw([ip_addr, proto])
+              4.7. to_gw(lcr_id[, ip_addr, proto])
+              4.8. to_any_gw([ip_addr, proto])
 
 
         5. Exported RPC Commands
         5. Exported RPC Commands
 
 
@@ -123,18 +129,24 @@ Juha Heinanen
    1.36. Setting lcr_gw_count module parameter
    1.36. Setting lcr_gw_count module parameter
    1.37. Setting dont_strip_or_tag_flag module parameter
    1.37. Setting dont_strip_or_tag_flag module parameter
    1.38. Set fetch_rows parameter
    1.38. Set fetch_rows parameter
-   1.39. load_gws usage
-   1.40. next_gw usage from a route block
-   1.41. next_gw usage from a failure route block
-   1.42. defunct_gw usage
-   1.43. from_gw usage
-   1.44. from_gw usage
-   1.45. to_gw usage
-   1.46. to_gw usage
-   1.47. lcr.reload RPC example
-   1.48. lcr.dump_gws RPC example
-   1.49. lcr.dump_rules RPC example
-   1.50. lcr.defunct_gw RPC example
+   1.39. Set ping_interval parameter
+   1.40. Set ping_inactive_threshold parameter
+   1.41. Set ping_valid_reply_codes parameter
+   1.42. Set ping_from parameter
+   1.43. Set ping_socket parameter
+   1.44. load_gws usage
+   1.45. next_gw usage from a route block
+   1.46. next_gw usage from a failure route block
+   1.47. inactivate_gw usage
+   1.48. defunct_gw usage
+   1.49. from_gw usage
+   1.50. from_gw usage
+   1.51. to_gw usage
+   1.52. to_gw usage
+   1.53. lcr.reload RPC example
+   1.54. lcr.dump_gws RPC example
+   1.55. lcr.dump_rules RPC example
+   1.56. lcr.defunct_gw RPC example
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -186,16 +198,22 @@ Chapter 1. Admin Guide
         3.36. lcr_gw_count (integer)
         3.36. lcr_gw_count (integer)
         3.37. dont_strip_or_tag_flag (integer)
         3.37. dont_strip_or_tag_flag (integer)
         3.38. fetch_rows (integer)
         3.38. fetch_rows (integer)
+        3.39. ping_interval (integer)
+        3.40. ping_inactive_threshold (integer)
+        3.41. ping_valid_reply_codes (string)
+        3.42. ping_from (string)
+        3.43. ping_socket (string)
 
 
    4. Functions
    4. Functions
 
 
         4.1. load_gws(lcr_id[, uri_user[, caller_uri]])
         4.1. load_gws(lcr_id[, uri_user[, caller_uri]])
         4.2. next_gw()
         4.2. next_gw()
-        4.3. defunct_gw(period)
-        4.4. from_gw(lcr_id[, ip_addr, proto])
-        4.5. from_any_gw([ip_addr, proto])
-        4.6. to_gw(lcr_id[, ip_addr, proto])
-        4.7. to_any_gw([ip_addr, proto])
+        4.3. inactivate_gw()
+        4.4. defunct_gw(period)
+        4.5. from_gw(lcr_id[, ip_addr, proto])
+        4.6. from_any_gw([ip_addr, proto])
+        4.7. to_gw(lcr_id[, ip_addr, proto])
+        4.8. to_any_gw([ip_addr, proto])
 
 
    5. Exported RPC Commands
    5. Exported RPC Commands
 
 
@@ -332,6 +350,11 @@ Chapter 1. Admin Guide
    3.36. lcr_gw_count (integer)
    3.36. lcr_gw_count (integer)
    3.37. dont_strip_or_tag_flag (integer)
    3.37. dont_strip_or_tag_flag (integer)
    3.38. fetch_rows (integer)
    3.38. fetch_rows (integer)
+   3.39. ping_interval (integer)
+   3.40. ping_inactive_threshold (integer)
+   3.41. ping_valid_reply_codes (string)
+   3.42. ping_from (string)
+   3.43. ping_socket (string)
 
 
 3.1. db_url (string)
 3.1. db_url (string)
 
 
@@ -788,15 +811,86 @@ modparam("lcr", "dont_strip_or_tag_flag", 10)
 modparam("lcr", "fetch_rows", 3000)
 modparam("lcr", "fetch_rows", 3000)
 ...
 ...
 
 
+3.39. ping_interval (integer)
+
+   Interval in seconds for sending OPTIONS ping requests to gateways that,
+   due to failures, have been marked as inactive by inactivate_gw()
+   function call. If an inactive gateway later gives a valid response (see
+   ping_valid_reply_codes) to a ping request, it is marked again as
+   active.
+
+   If value of this parameter is greater than zero, tm module must have
+   been loaded and parameters lcr_id_avp and defunct_gw_avp must have been
+   defined. Value "0" disables sending of OPTIONS ping requests to failed
+   gateways.
+
+   Default value is "0".
+
+   Example 1.39.  Set ping_interval parameter
+...
+modparam("lcr", "ping_interval", 15)
+...
+
+3.40. ping_inactive_threshold (integer)
+
+   Tells after how many failures (= inactivate_gw() function calls) a
+   gateway is marked as inactive.
+
+   Default value is "1", i.e., gateway is marked inactive after first
+   failure.
+
+   Example 1.40.  Set ping_inactive_threshold parameter
+...
+modparam("lcr", "ping_inactive_threshold", 3)
+...
+
+3.41. ping_valid_reply_codes (string)
+
+   A comma separated list of SIP reply codes, which are accepted as valid
+   replies to OPTIONS ping requests. Reply codes 2xx are by default
+   accepted as valid replies and they don't need to be listed here.
+
+   Default value is "", i.e., only 2xx replies are considered as valid
+   replies.
+
+   Example 1.41.  Set ping_valid_reply_codes parameter
+...
+modparam("lcr", "ping_valid_reply_codes", "403,405,501")
+...
+
+3.42. ping_from (string)
+
+   From URI used in OPTIONS ping requests.
+
+   Default value is "sip:pinger@localhost".
+
+   Example 1.42.  Set ping_from parameter
+...
+modparam("lcr", "ping_from", "sip:proxy.operator.com")
+...
+
+3.43. ping_socket (string)
+
+   Socket to be used for sending OPTIONS ping request. If not set or set
+   to "", default socket is used.
+
+   Default value is "".
+
+   Example 1.43.  Set ping_socket parameter
+...
+modparam("lcr", "ping_socket", "192.98.102.10:5060")
+...
+
 4. Functions
 4. Functions
 
 
    4.1. load_gws(lcr_id[, uri_user[, caller_uri]])
    4.1. load_gws(lcr_id[, uri_user[, caller_uri]])
    4.2. next_gw()
    4.2. next_gw()
-   4.3. defunct_gw(period)
-   4.4. from_gw(lcr_id[, ip_addr, proto])
-   4.5. from_any_gw([ip_addr, proto])
-   4.6. to_gw(lcr_id[, ip_addr, proto])
-   4.7. to_any_gw([ip_addr, proto])
+   4.3. inactivate_gw()
+   4.4. defunct_gw(period)
+   4.5. from_gw(lcr_id[, ip_addr, proto])
+   4.6. from_any_gw([ip_addr, proto])
+   4.7. to_gw(lcr_id[, ip_addr, proto])
+   4.8. to_any_gw([ip_addr, proto])
 
 
 4.1.  load_gws(lcr_id[, uri_user[, caller_uri]])
 4.1.  load_gws(lcr_id[, uri_user[, caller_uri]])
 
 
@@ -818,7 +912,7 @@ modparam("lcr", "fetch_rows", 3000)
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.39. load_gws usage
+   Example 1.44. load_gws usage
 ...
 ...
 if (!load_gws(1, $rU, $var(caller_uri))) {
 if (!load_gws(1, $rU, $var(caller_uri))) {
         sl_send_reply("500", "Server Internal Error - Cannot load gateways");
         sl_send_reply("500", "Server Internal Error - Cannot load gateways");
@@ -848,7 +942,7 @@ if (!load_gws(1, $rU, $var(caller_uri))) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.40. next_gw usage from a route block
+   Example 1.45. next_gw usage from a route block
 ...
 ...
 if (!next_gw()) {
 if (!next_gw()) {
         sl_send_reply("503", "Service not available - No gateways");
         sl_send_reply("503", "Service not available - No gateways");
@@ -856,7 +950,7 @@ if (!next_gw()) {
 };
 };
 ...
 ...
 
 
-   Example 1.41. next_gw usage from a failure route block
+   Example 1.46. next_gw usage from a failure route block
 ...
 ...
 if (!next_gw()) {
 if (!next_gw()) {
         t_reply("503", "Service not available - No more gateways");
         t_reply("503", "Service not available - No more gateways");
@@ -864,7 +958,28 @@ if (!next_gw()) {
 };
 };
 ...
 ...
 
 
-4.3.  defunct_gw(period)
+4.3.  inactivate_gw()
+
+   Inactivates the gateway denoted by lcr_id_avp and defunct_gw_avp (which
+   were set by previous next_gw() call). Use of this function requires
+   that ping_interval module parameter has been set to a positive value
+   allowing an inactivated gateway to be automatically activated by a
+   positive response to OPTIONS ping request.
+
+   Returns 1 on success and -1 in case of error (see syslog).
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   Example 1.47. inactivate_gw usage
+...
+failure_route [GW_FAILURE] {
+...
+    if (t_check_status("408|503")) {
+        inactivate_gw();
+    };
+...
+
+4.4.  defunct_gw(period)
 
 
    Defuncts gateway denoted by lcr_id_avp and defunct_gw_avp (which were
    Defuncts gateway denoted by lcr_id_avp and defunct_gw_avp (which were
    set by previuos next_gw() call) for a period of seconds given as
    set by previuos next_gw() call) for a period of seconds given as
@@ -876,12 +991,12 @@ if (!next_gw()) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.42. defunct_gw usage
+   Example 1.48. defunct_gw usage
 ...
 ...
 defunct_gw(60);
 defunct_gw(60);
 ...
 ...
 
 
-4.4.  from_gw(lcr_id[, ip_addr, proto])
+4.5.  from_gw(lcr_id[, ip_addr, proto])
 
 
    Checks if request comes from IP address and transport protocol
    Checks if request comes from IP address and transport protocol
    specified for a gateway in LCR instance lcr_id. Fails if the LCR
    specified for a gateway in LCR instance lcr_id. Fails if the LCR
@@ -908,14 +1023,14 @@ defunct_gw(60);
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE.
    ONREPLY_ROUTE.
 
 
-   Example 1.43. from_gw usage
+   Example 1.49. from_gw usage
 ...
 ...
 if (from_gw(1, $avp(s:real_source_addr), 2) {
 if (from_gw(1, $avp(s:real_source_addr), 2) {
         ...
         ...
 };
 };
 ...
 ...
 
 
-4.5.  from_any_gw([ip_addr, proto])
+4.6.  from_any_gw([ip_addr, proto])
 
 
    Checks if request comes from IP address and transport protocol
    Checks if request comes from IP address and transport protocol
    specified for any gateway. Only LCR instances, where all gateways have
    specified for any gateway. Only LCR instances, where all gateways have
@@ -938,12 +1053,12 @@ if (from_gw(1, $avp(s:real_source_addr), 2) {
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE.
    ONREPLY_ROUTE.
 
 
-   Example 1.44. from_gw usage
+   Example 1.50. from_gw usage
 ...
 ...
 $var(lcr_id) = from_any_gw("192.168.1.1", 3);
 $var(lcr_id) = from_any_gw("192.168.1.1", 3);
 ...
 ...
 
 
-4.6.  to_gw(lcr_id[, ip_addr, proto])
+4.7.  to_gw(lcr_id[, ip_addr, proto])
 
 
    Checks if in-dialog request goes to IP address and transport protocol
    Checks if in-dialog request goes to IP address and transport protocol
    specified for a gateway in LCR instance lcr_id. Fails if LCR instance
    specified for a gateway in LCR instance lcr_id. Fails if LCR instance
@@ -959,7 +1074,7 @@ $var(lcr_id) = from_any_gw("192.168.1.1", 3);
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.45. to_gw usage
+   Example 1.51. to_gw usage
 ...
 ...
 if (to_gw("1")) {
 if (to_gw("1")) {
         ...
         ...
@@ -967,7 +1082,7 @@ if (to_gw("1")) {
 };
 };
 ...
 ...
 
 
-4.7.  to_any_gw([ip_addr, proto])
+4.8.  to_any_gw([ip_addr, proto])
 
 
    Checks if in-dialog request goes to IP address and transport protocol
    Checks if in-dialog request goes to IP address and transport protocol
    of any gateway. Only LCR instances, where all gateways have IP address,
    of any gateway. Only LCR instances, where all gateways have IP address,
@@ -985,7 +1100,7 @@ if (to_gw("1")) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.46. to_gw usage
+   Example 1.52. to_gw usage
 ...
 ...
 if (to_any_gw("192.55.66.2", 1)) {
 if (to_any_gw("192.55.66.2", 1)) {
         ...
         ...
@@ -1008,7 +1123,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
 
    Parameters: none
    Parameters: none
 
 
-   Example 1.47. lcr.reload RPC example
+   Example 1.53. lcr.reload RPC example
                 $ kamcmd lcr.reload
                 $ kamcmd lcr.reload
 
 
 5.2. lcr.dump_gws
 5.2. lcr.dump_gws
@@ -1017,7 +1132,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
 
    Parameters: none
    Parameters: none
 
 
-   Example 1.48. lcr.dump_gws RPC example
+   Example 1.54. lcr.dump_gws RPC example
                 $ kamcmd lcr.dump_gws
                 $ kamcmd lcr.dump_gws
 
 
 5.3. lcr.dump_rules
 5.3. lcr.dump_rules
@@ -1027,7 +1142,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
 
    Parameters: none
    Parameters: none
 
 
-   Example 1.49. lcr.dump_rules RPC example
+   Example 1.55. lcr.dump_rules RPC example
                 $ kamcmd lcr.dump_rules
                 $ kamcmd lcr.dump_rules
 
 
 5.4. lcr.defunct_gw
 5.4. lcr.defunct_gw
@@ -1040,7 +1155,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
 
    Parameters: lcr_id gw_id period
    Parameters: lcr_id gw_id period
 
 
-   Example 1.50. lcr.defunct_gw RPC example
+   Example 1.56. lcr.defunct_gw RPC example
                 $ kamcmd lcr.defunct_gw 1 4 120
                 $ kamcmd lcr.defunct_gw 1 4 120
 
 
 6. Known Limitations
 6. Known Limitations

+ 1 - 1
modules/lcr/doc/lcr.xml

@@ -24,7 +24,7 @@
 		</editor>
 		</editor>
 	</authorgroup>
 	</authorgroup>
 	<copyright>
 	<copyright>
-		<year>2005-2010</year>
+		<year>2005-2014</year>
 	 	<holder>Juha Heinanen</holder>
 	 	<holder>Juha Heinanen</holder>
 	</copyright>
 	</copyright>
 	</bookinfo>
 	</bookinfo>

+ 162 - 0
modules/lcr/doc/lcr_admin.xml

@@ -996,6 +996,136 @@ modparam("lcr", "fetch_rows", 3000)
 		</example>
 		</example>
 	</section>
 	</section>
 
 
+	<section>
+		<title><varname>ping_interval</varname> (integer)</title>
+		<para>
+		  Interval in seconds for sending OPTIONS ping requests
+		  to gateways that, due to failures, have been marked
+		  as inactive by inactivate_gw() function call.
+		  If an inactive gateway later gives a valid response (see
+		  <varname>ping_valid_reply_codes</varname>) to a ping
+		  request, it is marked again as active.
+		</para>
+		<para>
+		  If value of this parameter is greater
+		  than zero, tm module must have been loaded and
+		  parameters <varname>lcr_id_avp</varname> and
+		  <varname>defunct_gw_avp</varname> must have been defined.
+		  Value <quote>0</quote> disables sending of OPTIONS ping
+		  requests to failed gateways.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>0</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>
+		Set <varname>ping_interval</varname> parameter
+		</title>
+<programlisting format="linespecific">
+...
+modparam("lcr", "ping_interval", 15)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>ping_inactive_threshold</varname> (integer)</title>
+		<para>
+		  Tells after how many failures (= inactivate_gw() function
+		  calls) a gateway is marked as inactive.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>1</quote>, i.e., gateway is
+			marked inactive after first failure.
+		</emphasis>
+		</para>
+		<example>
+		<title>
+		Set <varname>ping_inactive_threshold</varname> parameter
+		</title>
+<programlisting format="linespecific">
+...
+modparam("lcr", "ping_inactive_threshold", 3)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>ping_valid_reply_codes</varname> (string)</title>
+		<para>
+		  A comma separated list of SIP reply codes, which are
+		  accepted as valid replies to OPTIONS ping requests.
+		  Reply codes 2xx are by default accepted as valid replies and
+		  they don't need to be listed here.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote></quote>, i.e., only 2xx
+			replies are considered as valid replies.
+		</emphasis>
+		</para>
+		<example>
+		<title>
+		Set <varname>ping_valid_reply_codes</varname> parameter
+		</title>
+<programlisting format="linespecific">
+...
+modparam("lcr", "ping_valid_reply_codes", "403,405,501")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>ping_from</varname> (string)</title>
+		<para>
+		  From URI used in OPTIONS ping requests.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>sip:pinger@localhost</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>
+		Set <varname>ping_from</varname> parameter
+		</title>
+<programlisting format="linespecific">
+...
+modparam("lcr", "ping_from", "sip:proxy.operator.com")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>ping_socket</varname> (string)</title>
+		<para>
+		  Socket to be used for sending OPTIONS ping request.  If not
+		  set or set to <quote></quote>, default socket is used.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote></quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>
+		Set <varname>ping_socket</varname> parameter
+		</title>
+<programlisting format="linespecific">
+...
+modparam("lcr", "ping_socket", "192.98.102.10:5060")
+...
+</programlisting>
+		</example>
+	</section>
+
 	</section>
 	</section>
 
 
 	<section>
 	<section>
@@ -1100,6 +1230,38 @@ if (!next_gw()) {
 		</example>
 		</example>
 	</section>
 	</section>
 
 
+	<section>
+		<title>
+		<function moreinfo="none">inactivate_gw()</function>
+		</title>	
+		<para>
+		Inactivates the gateway denoted by lcr_id_avp and defunct_gw_avp
+		(which were set by previous next_gw() call).  Use of this
+		function requires that <varname>ping_interval</varname> module
+		parameter has been set to a positive value allowing an
+		inactivated gateway to be automatically activated by a positive
+		response to OPTIONS ping request.
+		</para>
+		<para>
+		Returns 1 on success and -1 in case of error (see syslog).
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+		</para>
+		<example>
+		<title><function>inactivate_gw</function> usage</title>
+		<programlisting format="linespecific">
+...
+failure_route [GW_FAILURE] {
+...
+    if (t_check_status("408|503")) {
+        inactivate_gw();
+    };
+...
+		</programlisting>
+		</example>
+	</section>
+
 	<section>
 	<section>
 		<title>
 		<title>
 		<function moreinfo="none">defunct_gw(period)</function>
 		<function moreinfo="none">defunct_gw(period)</function>

+ 338 - 101
modules/lcr/lcr_mod.c

@@ -1,7 +1,7 @@
 /*
 /*
  * Least Cost Routing module
  * Least Cost Routing module
  *
  *
- * Copyright (C) 2005-2012 Juha Heinanen
+ * Copyright (C) 2005-2014 Juha Heinanen
  * Copyright (C) 2006 Voice Sistem SRL
  * Copyright (C) 2006 Voice Sistem SRL
  *
  *
  * This file is part of SIP Router, a free SIP server.
  * This file is part of SIP Router, a free SIP server.
@@ -83,6 +83,7 @@
 #include "hash.h"
 #include "hash.h"
 #include "lcr_rpc.h"
 #include "lcr_rpc.h"
 #include "../../rpc_lookup.h"
 #include "../../rpc_lookup.h"
+#include "../../modules/tm/tm_load.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
@@ -210,6 +211,12 @@ static unsigned int defunct_capability_param = 0;
 /* dont strip or tag param */
 /* dont strip or tag param */
 static int dont_strip_or_prefix_flag_param = -1;
 static int dont_strip_or_prefix_flag_param = -1;
 
 
+/* ping related params */
+unsigned int ping_interval_param = 0;
+unsigned int ping_inactivate_threshold_param = 1;
+str ping_valid_reply_codes_param = {"", 0};
+str ping_socket_param = {"", 0};
+str ping_from_param = {"sip:pinger@localhost", 20};
 
 
 /*
 /*
  * Other module types and variables
  * Other module types and variables
@@ -237,6 +244,13 @@ struct gw_info **gw_pt = (struct gw_info **)NULL;
 /* Pointer to rule_id info hash table */
 /* Pointer to rule_id info hash table */
 struct rule_id_info **rule_id_hash_table = (struct rule_id_info **)NULL;
 struct rule_id_info **rule_id_hash_table = (struct rule_id_info **)NULL;
 
 
+/* Pinging related vars */
+struct tm_binds tmb;
+void ping_timer(unsigned int ticks, void* param);
+unsigned int ping_valid_reply_codes[MAX_NO_OF_REPLY_CODES];
+str ping_method = {"OPTIONS", 7};
+unsigned int ping_rc_count = 0;
+
 /*
 /*
  * Functions that are defined later
  * Functions that are defined later
  */
  */
@@ -247,6 +261,7 @@ static void free_shared_memory(void);
 
 
 static int load_gws(struct sip_msg* _m, int argc, action_u_t argv[]);
 static int load_gws(struct sip_msg* _m, int argc, action_u_t argv[]);
 static int next_gw(struct sip_msg* _m, char* _s1, char* _s2);
 static int next_gw(struct sip_msg* _m, char* _s1, char* _s2);
+static int inactivate_gw(struct sip_msg* _m, char* _s1, char* _s2);
 static int defunct_gw(struct sip_msg* _m, char* _s1, char* _s2);
 static int defunct_gw(struct sip_msg* _m, char* _s1, char* _s2);
 static int from_gw_1(struct sip_msg* _m, char* _s1, char* _s2);
 static int from_gw_1(struct sip_msg* _m, char* _s1, char* _s2);
 static int from_gw_3(struct sip_msg* _m, char* _s1, char* _s2, char* _s3);
 static int from_gw_3(struct sip_msg* _m, char* _s1, char* _s2, char* _s3);
@@ -266,6 +281,8 @@ static cmd_export_t cmds[] = {
     {"next_gw", (cmd_function)next_gw, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
     {"next_gw", (cmd_function)next_gw, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
     {"defunct_gw", (cmd_function)defunct_gw, 1, 0, 0,
     {"defunct_gw", (cmd_function)defunct_gw, 1, 0, 0,
      REQUEST_ROUTE | FAILURE_ROUTE},
      REQUEST_ROUTE | FAILURE_ROUTE},
+    {"inactivate_gw", (cmd_function)inactivate_gw, 0, 0, 0,
+     REQUEST_ROUTE | FAILURE_ROUTE},
     {"from_gw", (cmd_function)from_gw_1, 1, 0, 0,
     {"from_gw", (cmd_function)from_gw_1, 1, 0, 0,
      REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
      REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
     {"from_gw", (cmd_function)from_gw_3, 3, 0, 0,
     {"from_gw", (cmd_function)from_gw_3, 3, 0, 0,
@@ -329,6 +346,11 @@ static param_export_t params[] = {
     {"lcr_gw_count",             INT_PARAM, &lcr_gw_count_param},
     {"lcr_gw_count",             INT_PARAM, &lcr_gw_count_param},
     {"dont_strip_or_prefix_flag",INT_PARAM, &dont_strip_or_prefix_flag_param},
     {"dont_strip_or_prefix_flag",INT_PARAM, &dont_strip_or_prefix_flag_param},
     {"fetch_rows",               INT_PARAM, &fetch_rows_param},
     {"fetch_rows",               INT_PARAM, &fetch_rows_param},
+    {"ping_interval",            INT_PARAM, &ping_interval_param},
+    {"ping_inactivate_threshold",  INT_PARAM, &ping_inactivate_threshold_param},
+    {"ping_valid_reply_codes",   STR_PARAM, &ping_valid_reply_codes_param.s},
+    {"ping_from",                STR_PARAM, &ping_from_param.s},
+    {"ping_socket",              STR_PARAM, &ping_socket_param.s},
     {0, 0, 0}
     {0, 0, 0}
 };
 };
 
 
@@ -406,6 +428,7 @@ static int mod_init(void)
     str s;
     str s;
     unsigned short avp_flags;
     unsigned short avp_flags;
     unsigned int i;
     unsigned int i;
+    char *at, *past, *sep;
 
 
     /* Register RPC commands */
     /* Register RPC commands */
     if (rpc_register_array(lcr_rpc)!=0) {
     if (rpc_register_array(lcr_rpc)!=0) {
@@ -441,6 +464,9 @@ static int mod_init(void)
     tag_col.len = strlen(tag_col.s);
     tag_col.len = strlen(tag_col.s);
     flags_col.len = strlen(flags_col.s);
     flags_col.len = strlen(flags_col.s);
     defunct_col.len = strlen(defunct_col.s);
     defunct_col.len = strlen(defunct_col.s);
+    ping_valid_reply_codes_param.len = strlen(ping_valid_reply_codes_param.s);
+    ping_socket_param.len = strlen(ping_socket_param.s);
+    ping_from_param.len = strlen(ping_from_param.s);
 
 
     /* Bind database */
     /* Bind database */
     if (lcr_db_bind(&db_url)) {
     if (lcr_db_bind(&db_url)) {
@@ -539,10 +565,15 @@ static int mod_init(void)
 	flags_avp_type = avp_flags;
 	flags_avp_type = avp_flags;
     }
     }
 
 
-    if (defunct_capability_param > 0) {
+    if ((ping_interval_param != 0) && (ping_interval_param < 10)) {
+	LM_ERR("invalid ping_interval value '%u'\n", ping_interval_param);
+	return -1;
+    }
+
+    if ((defunct_capability_param > 0) || (ping_interval_param > 0)) {
 	if (defunct_gw_avp_param && *defunct_gw_avp_param) {
 	if (defunct_gw_avp_param && *defunct_gw_avp_param) {
 	    s.s = defunct_gw_avp_param; s.len = strlen(s.s);
 	    s.s = defunct_gw_avp_param; s.len = strlen(s.s);
-        avp_spec = pv_cache_get(&s);
+	    avp_spec = pv_cache_get(&s);
 	    if (avp_spec==NULL || (avp_spec->type != PVT_AVP)) {
 	    if (avp_spec==NULL || (avp_spec->type != PVT_AVP)) {
 		LM_ERR("malformed or non AVP definition <%s>\n",
 		LM_ERR("malformed or non AVP definition <%s>\n",
 		       defunct_gw_avp_param);
 		       defunct_gw_avp_param);
@@ -560,7 +591,7 @@ static int mod_init(void)
 	}
 	}
 	if (lcr_id_avp_param && *lcr_id_avp_param) {
 	if (lcr_id_avp_param && *lcr_id_avp_param) {
 	    s.s = lcr_id_avp_param; s.len = strlen(s.s);
 	    s.s = lcr_id_avp_param; s.len = strlen(s.s);
-        avp_spec = pv_cache_get(&s);
+	    avp_spec = pv_cache_get(&s);
 	    if (avp_spec==NULL || (avp_spec->type != PVT_AVP)) {
 	    if (avp_spec==NULL || (avp_spec->type != PVT_AVP)) {
 		LM_ERR("malformed or non AVP definition <%s>\n",
 		LM_ERR("malformed or non AVP definition <%s>\n",
 		       lcr_id_avp_param);
 		       lcr_id_avp_param);
@@ -576,6 +607,48 @@ static int mod_init(void)
 	    LM_ERR("AVP lcr_id_avp has not been defined\n");
 	    LM_ERR("AVP lcr_id_avp has not been defined\n");
 	    return -1;
 	    return -1;
 	}
 	}
+	if (ping_interval_param > 0) {
+	    if (load_tm_api(&tmb) == -1) {
+		LM_ERR("could not bind tm api\n");
+		return -1;
+	    }
+	    if ((ping_inactivate_threshold_param < 1) ||
+		(ping_inactivate_threshold_param > 32)) {
+		LM_ERR("invalid ping_inactivate_threshold value '%u'\n",
+		       ping_inactivate_threshold_param);
+		return -1;
+	    }
+	    /* ping reply codes */
+	    at = ping_valid_reply_codes_param.s;
+	    past = ping_valid_reply_codes_param.s +
+		ping_valid_reply_codes_param.len;
+	    while (at < past) {
+		sep = index(at, ',');
+		s.s = at;
+		if (sep == NULL) {
+		    s.len = past - at;
+		    at = past;
+		} else {
+		    s.len = sep - at;
+		    at = sep + 1;
+		}
+		if (ping_rc_count > MAX_NO_OF_REPLY_CODES - 1) {
+		    LM_ERR("more than %u ping reply codes\n",
+			   MAX_NO_OF_REPLY_CODES);
+		    goto err;
+		}
+		if ((str2int(&s, &(ping_valid_reply_codes[ping_rc_count]))
+		     != 0) ||
+		    (ping_valid_reply_codes[ping_rc_count] < 100) ||
+		    (ping_valid_reply_codes[ping_rc_count] > 999)) {
+		    LM_ERR("invalid ping_valid_reply_codes code '%.*s'\n",
+			   s.len, s.s);
+		    return -1;
+		}
+		ping_rc_count++;
+	    }
+	    register_timer(ping_timer, NULL, ping_interval_param);
+	}
     }
     }
 
 
     if (fetch_rows_param < 1) {
     if (fetch_rows_param < 1) {
@@ -641,8 +714,8 @@ static int mod_init(void)
     /* gw tables themselves */
     /* gw tables themselves */
     /* ordered by ip_addr for from_gw/to_gw functions */
     /* ordered by ip_addr for from_gw/to_gw functions */
     /* in each table i, (gw_pt[i])[0].ip_addr contains number of
     /* in each table i, (gw_pt[i])[0].ip_addr contains number of
-       gateways in the table and (gw_pt[i])[0].port as value 1
-       if some gateways in the table have null ip addr */
+       gateways in the table and (gw_pt[i])[0].port has value 1
+       if some gateways in the table have null ip addr. */
     for (i = 0; i <= lcr_count_param; i++) {
     for (i = 0; i <= lcr_count_param; i++) {
 	gw_pt[i] = (struct gw_info *)shm_malloc(sizeof(struct gw_info) *
 	gw_pt[i] = (struct gw_info *)shm_malloc(sizeof(struct gw_info) *
 						(lcr_gw_count_param + 1));
 						(lcr_gw_count_param + 1));
@@ -701,6 +774,7 @@ static void destroy(void)
 static void free_shared_memory(void)
 static void free_shared_memory(void)
 {
 {
     int i;
     int i;
+
     for (i = 0; i <= lcr_count_param; i++) {
     for (i = 0; i <= lcr_count_param; i++) {
 	if (rule_pt && rule_pt[i]) {
 	if (rule_pt && rule_pt[i]) {
 	    rule_hash_table_contents_free(rule_pt[i]);
 	    rule_hash_table_contents_free(rule_pt[i]);
@@ -808,21 +882,29 @@ static int comp_gws(const void *_g1, const void *_g2)
  */
  */
 static int insert_gw(struct gw_info *gws, unsigned int i, unsigned int gw_id,
 static int insert_gw(struct gw_info *gws, unsigned int i, unsigned int gw_id,
 		     char *gw_name, unsigned int gw_name_len,
 		     char *gw_name, unsigned int gw_name_len,
-		     unsigned int scheme, struct ip_addr *ip_addr,
-		     unsigned int port, unsigned int transport,
+		     char *scheme, unsigned int scheme_len,
+		     struct ip_addr *ip_addr, unsigned int port,
+		     uri_transport transport_code, 
+		     char *transport, unsigned int transport_len,
 		     char *params, unsigned int params_len,
 		     char *params, unsigned int params_len,
 		     char *hostname, unsigned int hostname_len,
 		     char *hostname, unsigned int hostname_len,
 		     char *ip_string, unsigned int strip, char *prefix,
 		     char *ip_string, unsigned int strip, char *prefix,
 		     unsigned int prefix_len, char *tag, unsigned int tag_len,
 		     unsigned int prefix_len, char *tag, unsigned int tag_len,
 		     unsigned int flags, unsigned int defunct_until)
 		     unsigned int flags, unsigned int defunct_until)
 {
 {
+    char *at, *string;
+    int len;
+
     gws[i].gw_id = gw_id;
     gws[i].gw_id = gw_id;
     if (gw_name_len) memcpy(&(gws[i].gw_name[0]), gw_name, gw_name_len);
     if (gw_name_len) memcpy(&(gws[i].gw_name[0]), gw_name, gw_name_len);
     gws[i].gw_name_len = gw_name_len;
     gws[i].gw_name_len = gw_name_len;
-    gws[i].scheme = scheme;
+    memcpy(&(gws[i].scheme[0]), scheme, scheme_len);
+    gws[i].scheme_len = scheme_len;
     gws[i].ip_addr = *ip_addr;
     gws[i].ip_addr = *ip_addr;
     gws[i].port = port;
     gws[i].port = port;
-    gws[i].transport = transport;
+    gws[i].transport_code = transport_code;
+    if (transport_len) memcpy(&(gws[i].transport[0]), transport, transport_len);
+    gws[i].transport_len = transport_len;
     if (params_len) memcpy(&(gws[i].params[0]), params, params_len);
     if (params_len) memcpy(&(gws[i].params[0]), params, params_len);
     gws[i].params_len = params_len;
     gws[i].params_len = params_len;
     if (hostname_len) memcpy(&(gws[i].hostname[0]), hostname, hostname_len);
     if (hostname_len) memcpy(&(gws[i].hostname[0]), hostname, hostname_len);
@@ -834,8 +916,33 @@ static int insert_gw(struct gw_info *gws, unsigned int i, unsigned int gw_id,
     if (tag_len) memcpy(&(gws[i].tag[0]), tag, tag_len);
     if (tag_len) memcpy(&(gws[i].tag[0]), tag, tag_len);
     gws[i].flags = flags;
     gws[i].flags = flags;
     gws[i].defunct_until = defunct_until;
     gws[i].defunct_until = defunct_until;
-    LM_DBG("inserted gw <%u, %.*s, %s, %u, %.*s> at index %u\n", gw_id,
-	   gw_name_len, gw_name, ip_string, port, hostname_len, hostname, i);
+    gws[i].state = 0;
+    at = &(gws[i].uri[0]);
+    append_str(at, scheme, scheme_len);
+    if (ip_addr->af != 0) {
+	string = ip_addr2a(ip_addr);
+	len = strlen(string);
+	if (ip_addr->af == AF_INET6) {
+	    append_chr(at, '[');
+	    append_str(at, string, len);
+	    append_chr(at, ']');
+	} else {
+	    append_str(at, string, len);
+	}
+    } else {
+	append_str(at, &(hostname[0]), hostname_len);
+    }
+    if (port > 0) {
+	append_chr(at, ':');
+	string = int2str(port, &len);
+	append_str(at, string, len);
+    }
+    if (transport_len > 0) {
+	append_str(at, transport, transport_len);
+    }
+    gws[i].uri_len = at - &(gws[i].uri[0]);
+    LM_DBG("inserted gw <%u, %.*s, %.*s> at index %u\n", gw_id,
+	   gw_name_len, gw_name, gws[i].uri_len, gws[i].uri, i);
     return 1;
     return 1;
 }
 }
 
 
@@ -888,14 +995,14 @@ static int insert_gws(db1_res_t *res, struct gw_info *gws,
 		      unsigned int *gw_cnt)
 		      unsigned int *gw_cnt)
 {
 {
     unsigned int i, gw_id, defunct_until, gw_name_len, port, params_len,
     unsigned int i, gw_id, defunct_until, gw_name_len, port, params_len,
-	hostname_len, strip, prefix_len, tag_len, flags;
-    char *gw_name, *params, *hostname, *prefix, *tag;
+	hostname_len, strip, prefix_len, tag_len, flags, scheme_len,
+	transport_len;
+    char *gw_name, *params, *hostname, *prefix, *tag, *scheme, *transport;
+    uri_transport transport_code;
     db_row_t* row;
     db_row_t* row;
     struct in_addr in_addr;
     struct in_addr in_addr;
     struct ip_addr ip_addr, *ip_p;
     struct ip_addr ip_addr, *ip_p;
     str ip_string;
     str ip_string;
-    uri_type scheme;
-    uri_transport transport;
     
     
     for (i = 0; i < RES_ROW_N(res); i++) {
     for (i = 0; i < RES_ROW_N(res); i++) {
 	row = RES_ROWS(res) + i;
 	row = RES_ROWS(res) + i;
@@ -988,38 +1095,65 @@ static int insert_gws(db1_res_t *res, struct gw_info *gws,
 	    return 0;
 	    return 0;
 	}
 	}
 	if (VAL_NULL(ROW_VALUES(row) + 3)) {
 	if (VAL_NULL(ROW_VALUES(row) + 3)) {
-	    scheme = SIP_URI_T;
+	    scheme = "sip:";
+	    scheme_len = 4;
 	} else {
 	} else {
 	    if (VAL_TYPE(ROW_VALUES(row) + 3) != DB1_INT) {
 	    if (VAL_TYPE(ROW_VALUES(row) + 3) != DB1_INT) {
 		LM_ERR("lcr_gw uri scheme at row <%u> is not int\n", i);
 		LM_ERR("lcr_gw uri scheme at row <%u> is not int\n", i);
 		return 0;
 		return 0;
 	    }
 	    }
-	    scheme = (uri_type)VAL_INT(ROW_VALUES(row) + 3);
-	}
-	if ((scheme != SIP_URI_T) && (scheme != SIPS_URI_T)) {
-	    LM_ERR("lcr_gw has unknown or unsupported URI scheme <%u> at "
-		   "row <%u>\n", (unsigned int)scheme, i);
-	    return 0;
+	    switch (VAL_INT(ROW_VALUES(row) + 3)) {
+	    case SIP_URI_T:
+		scheme = "sip:";
+		scheme_len = 4;
+		break;
+	    case SIPS_URI_T:
+		scheme = "sips:";
+		scheme_len = 5;
+		break;
+	    default:
+		LM_ERR("lcr_gw has unknown or unsupported URI scheme <%u> at "
+		       "row <%u>\n", VAL_INT(ROW_VALUES(row) + 3), i);
+		return 0;
+	    }
 	}
 	}
 	if (VAL_NULL(ROW_VALUES(row) + 4)) {
 	if (VAL_NULL(ROW_VALUES(row) + 4)) {
-	    transport = PROTO_NONE;
+	    transport_code = PROTO_NONE;
+	    transport = "";
+	    transport_len = 0;
 	} else {
 	} else {
 	    if (VAL_TYPE(ROW_VALUES(row) + 4) != DB1_INT) {
 	    if (VAL_TYPE(ROW_VALUES(row) + 4) != DB1_INT) {
 		LM_ERR("lcr_gw transport at row <%u> is not int\n", i);
 		LM_ERR("lcr_gw transport at row <%u> is not int\n", i);
 		return 0;
 		return 0;
 	    }
 	    }
-	    transport = (uri_transport)VAL_INT(ROW_VALUES(row) + 4);
-	}
-	if ((transport != PROTO_UDP) && (transport != PROTO_TCP) &&
-	    (transport != PROTO_TLS) && (transport != PROTO_SCTP) &&
-	    (transport != PROTO_NONE)) {
-	    LM_ERR("lcr_gw has unknown or unsupported transport <%u> at "
-		   " row <%u>\n", (unsigned int)transport, i);
-	    return 0;
+	    transport_code = (uri_transport)VAL_INT(ROW_VALUES(row) + 4);
+	    switch (transport_code) {
+	    case PROTO_UDP:
+		transport = ";transport=udp";
+		transport_len = 14;
+		break;
+	    case PROTO_TCP:
+		transport = ";transport=tcp";
+		transport_len = 14;
+		break;
+	    case PROTO_TLS:
+		transport = ";transport=tls";
+		transport_len = 14;
+		break;
+	    case PROTO_SCTP:
+		transport = ";transport=sctp";
+		transport_len = 15;
+		break;
+	    default:
+		LM_ERR("lcr_gw has unknown or unsupported transport <%u> at "
+		       " row <%u>\n", transport_code, i);
+		return 0;
+	    }
 	}
 	}
-	if ((scheme == SIPS_URI_T) && (transport == PROTO_UDP)) {
+	if ((VAL_INT(ROW_VALUES(row) + 3) == SIPS_URI_T) &&
+	    (transport_code == PROTO_UDP)) {
 	    LM_ERR("lcr_gw has wrong transport <%u> for SIPS URI "
 	    LM_ERR("lcr_gw has wrong transport <%u> for SIPS URI "
-		   "scheme at row <%u>\n", transport, i);
+		   "scheme at row <%u>\n", transport_code, i);
 	    return 0;
 	    return 0;
 	}
 	}
 	if (VAL_NULL(ROW_VALUES(row) + 5)) {
 	if (VAL_NULL(ROW_VALUES(row) + 5)) {
@@ -1118,8 +1252,9 @@ static int insert_gws(db1_res_t *res, struct gw_info *gws,
 	}
 	}
 	(*gw_cnt)++;
 	(*gw_cnt)++;
 	if (!insert_gw(gws, *gw_cnt, gw_id, gw_name, gw_name_len,
 	if (!insert_gw(gws, *gw_cnt, gw_id, gw_name, gw_name_len,
-		       scheme, &ip_addr, port,
-		       transport, params, params_len, hostname,
+		       scheme, scheme_len, &ip_addr, port,
+		       transport_code, transport, transport_len,
+		       params, params_len, hostname,
 		       hostname_len, ip_string.s, strip, prefix, prefix_len,
 		       hostname_len, ip_string.s, strip, prefix, prefix_len,
 		       tag, tag_len, flags, defunct_until)) {
 		       tag, tag_len, flags, defunct_until)) {
 	    return 0;
 	    return 0;
@@ -1512,7 +1647,7 @@ int reload_tables()
 	lcr_dbf.free_result(dbh, res);
 	lcr_dbf.free_result(dbh, res);
 	res = NULL;
 	res = NULL;
 
 
-	/* swap tables */
+	/* Swap tables */
 	rule_pt_tmp = rule_pt[lcr_id];
 	rule_pt_tmp = rule_pt[lcr_id];
 	gw_pt_tmp = gw_pt[lcr_id];
 	gw_pt_tmp = gw_pt[lcr_id];
 	rule_pt[lcr_id] = rules;
 	rule_pt[lcr_id] = rules;
@@ -1535,14 +1670,16 @@ int reload_tables()
 }
 }
 
 
 
 
-inline int encode_avp_value(char *value, unsigned int gw_index, uri_type scheme,
+inline int encode_avp_value(char *value, unsigned int gw_index,
+			    char *scheme, unsigned int scheme_len,
 			    unsigned int strip,
 			    unsigned int strip,
 			    char *prefix, unsigned int prefix_len,
 			    char *prefix, unsigned int prefix_len,
 			    char *tag, unsigned int tag_len,
 			    char *tag, unsigned int tag_len,
 			    struct ip_addr *ip_addr, char *hostname, 
 			    struct ip_addr *ip_addr, char *hostname, 
 			    unsigned int hostname_len, unsigned int port,
 			    unsigned int hostname_len, unsigned int port,
 			    char *params, unsigned int params_len,
 			    char *params, unsigned int params_len,
-			    uri_transport transport, unsigned int flags)
+			    char *transport, unsigned int transport_len,
+			    unsigned int flags)
 {
 {
     char *at, *string;
     char *at, *string;
     int len;
     int len;
@@ -1554,8 +1691,7 @@ inline int encode_avp_value(char *value, unsigned int gw_index, uri_type scheme,
     append_str(at, string, len);
     append_str(at, string, len);
     append_chr(at, '|');
     append_chr(at, '|');
     /* scheme */
     /* scheme */
-    string = int2str(scheme, &len);
-    append_str(at, string, len);
+    append_str(at, scheme, scheme_len);
     append_chr(at, '|');
     append_chr(at, '|');
     /* strip */
     /* strip */
     string = int2str(strip, &len);
     string = int2str(strip, &len);
@@ -1591,8 +1727,7 @@ inline int encode_avp_value(char *value, unsigned int gw_index, uri_type scheme,
     append_str(at, params, params_len);
     append_str(at, params, params_len);
     append_chr(at, '|');
     append_chr(at, '|');
     /* transport */
     /* transport */
-    string = int2str(transport, &len);
-    append_str(at, string, len);
+    append_str(at, transport, transport_len);
     append_chr(at, '|');
     append_chr(at, '|');
     /* flags */
     /* flags */
     string = int2str(flags, &len);
     string = int2str(flags, &len);
@@ -1620,21 +1755,13 @@ inline int decode_avp_value(char *value, unsigned int *gw_index, str *scheme,
     s.len = sep - s.s;
     s.len = sep - s.s;
     str2int(&s, gw_index);
     str2int(&s, gw_index);
     /* scheme */
     /* scheme */
-    s.s = sep + 1;
-    sep = index(s.s, '|');
+    scheme->s = sep + 1;
+    sep = index(scheme->s, '|');
     if (sep == NULL) {
     if (sep == NULL) {
 	LM_ERR("scheme was not found in AVP value\n");
 	LM_ERR("scheme was not found in AVP value\n");
 	return 0;
 	return 0;
     }
     }
-    s.len = sep - s.s;
-    str2int(&s, &u);
-    if (u == SIP_URI_T) {
-	scheme->s = "sip:";
-	scheme->len = 4;
-    } else {
-	scheme->s = "sips:";
-	scheme->len = 5;
-    }
+    scheme->len = sep - scheme->s;
     /* strip */
     /* strip */
     s.s = sep + 1;
     s.s = sep + 1;
     sep = index(s.s, '|');
     sep = index(s.s, '|');
@@ -1694,7 +1821,7 @@ inline int decode_avp_value(char *value, unsigned int *gw_index, str *scheme,
     port->s = sep + 1;
     port->s = sep + 1;
     sep = index(port->s, '|');
     sep = index(port->s, '|');
     if (sep == NULL) {
     if (sep == NULL) {
-	LM_ERR("scheme was not found in AVP value\n");
+	LM_ERR("port was not found in AVP value\n");
 	return 0;
 	return 0;
     }
     }
     port->len = sep - port->s;
     port->len = sep - port->s;
@@ -1707,43 +1834,13 @@ inline int decode_avp_value(char *value, unsigned int *gw_index, str *scheme,
     }
     }
     params->len = sep - params->s;
     params->len = sep - params->s;
     /* transport */
     /* transport */
-    s.s = sep + 1;
-    sep = index(s.s, '|');
+    transport->s = sep + 1;
+    sep = index(transport->s, '|');
     if (sep == NULL) {
     if (sep == NULL) {
 	LM_ERR("transport was not found in AVP value\n");
 	LM_ERR("transport was not found in AVP value\n");
 	return 0;
 	return 0;
     }
     }
-    s.len = sep - s.s;
-    str2int(&s, &u);
-    switch (u) {
-    case PROTO_NONE:
-	transport->s = (char *)0;
-	transport->len = 0;
-	break;
-    case PROTO_UDP:
-	transport->s = ";transport=udp";
-	transport->len = 14;
-	break;
-    case PROTO_TCP:
-	transport->s = ";transport=tcp";
-	transport->len = 14;
-	break;
-    case PROTO_TLS:
-	transport->s = ";transport=tls";
-	transport->len = 14;
-	break;
-    case PROTO_SCTP:
-	transport->s = ";transport=sctp";
-	transport->len = 15;
-	break;
-    case PROTO_WS:
-    case PROTO_WSS:
-        LM_ERR("unsupported transport '%d'\n", u);
-	return 0;
-    default:
-	LM_ERR("unknown transport '%d'\n", u);
-	return 0;
-    }
+    transport->len = sep - transport->s;
     /* flags */
     /* flags */
     s.s = sep + 1;
     s.s = sep + 1;
     s.len = strlen(s.s);
     s.len = strlen(s.s);
@@ -1786,13 +1883,15 @@ void add_gws_into_avps(struct gw_info *gws, struct matched_gw_info *matched_gws,
 	    goto skip;
 	    goto skip;
 	}
 	}
 	value.len = 
 	value.len = 
-	    encode_avp_value(encoded_value, index, gws[index].scheme,
+	    encode_avp_value(encoded_value, index,
+			     gws[index].scheme, gws[index].scheme_len,
 			     strip, gws[index].prefix, prefix_len,
 			     strip, gws[index].prefix, prefix_len,
 			     gws[index].tag, tag_len,
 			     gws[index].tag, tag_len,
 			     &gws[index].ip_addr,
 			     &gws[index].ip_addr,
 			     gws[index].hostname, hostname_len,
 			     gws[index].hostname, hostname_len,
 			     gws[index].port, gws[index].params, params_len,
 			     gws[index].port, gws[index].params, params_len,
-			     gws[index].transport, gws[index].flags);
+			     gws[index].transport, gws[index].transport_len,
+			     gws[index].flags);
 	value.s = (char *)&(encoded_value[0]);
 	value.s = (char *)&(encoded_value[0]);
 	val.s = value;
 	val.s = value;
 	add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val);
 	add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val);
@@ -1913,8 +2012,10 @@ static int load_gws(struct sip_msg* _m, int argc, action_u_t argv[])
 	    /* Load gws associated with this rule */
 	    /* Load gws associated with this rule */
 	    t = rule->targets;
 	    t = rule->targets;
 	    while (t) {
 	    while (t) {
-		/* If this gw is defunct, skip it */
-		if (gws[t->gw_index].defunct_until > now) goto skip_gw;
+		/* If this gw is defunct or inactive, skip it */
+		if ((gws[t->gw_index].defunct_until > now) ||
+		    (gws[t->gw_index].state == GW_INACTIVE))
+		    goto skip_gw;
 		matched_gws[gw_index].gw_index = t->gw_index;
 		matched_gws[gw_index].gw_index = t->gw_index;
 		matched_gws[gw_index].prefix_len = pl->prefix_len;
 		matched_gws[gw_index].prefix_len = pl->prefix_len;
 		matched_gws[gw_index].priority = t->priority;
 		matched_gws[gw_index].priority = t->priority;
@@ -2104,19 +2205,17 @@ static int defunct_gw(struct sip_msg* _m, char *_defunct_period, char *_s2)
     /* Check defunct gw capability */
     /* Check defunct gw capability */
     if (defunct_capability_param == 0) {
     if (defunct_capability_param == 0) {
 	LM_ERR("no defunct gw capability, activate by setting "
 	LM_ERR("no defunct gw capability, activate by setting "
-	       "defunct_capability_param module param\n");
+	       "defunct_capability module param\n");
 	return -1;
 	return -1;
     }
     }
 
 
-    /* Get parameter value */
-
     /* Get and check parameter value */
     /* Get and check parameter value */
     defunct_period = strtol(_defunct_period, &tmp, 10);
     defunct_period = strtol(_defunct_period, &tmp, 10);
     if ((tmp == 0) || (*tmp) || (tmp == _defunct_period)) {
     if ((tmp == 0) || (*tmp) || (tmp == _defunct_period)) {
 	LM_ERR("invalid defunct_period parameter %s\n", _defunct_period);
 	LM_ERR("invalid defunct_period parameter %s\n", _defunct_period);
 	return -1;
 	return -1;
     }
     }
-    if (defunct_period < 1) {
+    if (defunct_period < 0) {
 	LM_ERR("invalid defunct_period param value %d\n", defunct_period);
 	LM_ERR("invalid defunct_period param value %d\n", defunct_period);
 	return -1;
 	return -1;
     }
     }
@@ -2130,7 +2229,7 @@ static int defunct_gw(struct sip_msg* _m, char *_defunct_period, char *_s2)
     gws = gw_pt[lcr_id_val.n];
     gws = gw_pt[lcr_id_val.n];
     if (search_first_avp(defunct_gw_avp_type, defunct_gw_avp,
     if (search_first_avp(defunct_gw_avp_type, defunct_gw_avp,
 			 &index_val, 0) == NULL) {
 			 &index_val, 0) == NULL) {
-	LM_ERR("defucnt_gw_avp was not found\n");
+	LM_ERR("defunct_gw_avp was not found\n");
 	return -1;
 	return -1;
     }
     }
     gw_index = index_val.n;
     gw_index = index_val.n;
@@ -2139,13 +2238,66 @@ static int defunct_gw(struct sip_msg* _m, char *_defunct_period, char *_s2)
 	return -1;
 	return -1;
     }
     }
     
     
-    /* Defunct gw */
     defunct_until = time((time_t *)NULL) + defunct_period;
     defunct_until = time((time_t *)NULL) + defunct_period;
     LM_DBG("defuncting gw with name <%.*s> until <%u>\n",
     LM_DBG("defuncting gw with name <%.*s> until <%u>\n",
 	   gws[gw_index].gw_name_len, gws[gw_index].gw_name, defunct_until);
 	   gws[gw_index].gw_name_len, gws[gw_index].gw_name, defunct_until);
     gws[gw_index].defunct_until = defunct_until;
     gws[gw_index].defunct_until = defunct_until;
+
+    return 1;
+}
+
+
+/*
+ * Inactivate current gw (provided that inactivate threshold has been reached)
+ */
+static int inactivate_gw(struct sip_msg* _m, char *_defunct_period, char *_s2)
+{
+    int_str lcr_id_val, index_val;
+    struct gw_info *gws;
+    unsigned int gw_index;
+
+    /* Check inactivate gw capability */
+    if (ping_interval_param == 0)  {
+	LM_ERR("no inactivate gw capability, activate by setting "
+	       "ping_interval module param\n");
+	return -1;
+    }
+
+    /* Get AVP values */
+    if (search_first_avp(lcr_id_avp_type, lcr_id_avp, &lcr_id_val, 0)
+	== NULL) {
+	LM_ERR("lcr_id_avp was not found\n");
+	return -1;
+    }
+    gws = gw_pt[lcr_id_val.n];
+    if (search_first_avp(defunct_gw_avp_type, defunct_gw_avp,
+			 &index_val, 0) == NULL) {
+	LM_ERR("defunct_gw_avp was not found\n");
+	return -1;
+    }
+    gw_index = index_val.n;
+    if ((gw_index < 1) || (gw_index > gws[0].ip_addr.u.addr32[0])) {
+	LM_ERR("gw index <%u> is out of bounds\n", gw_index);
+	return -1;
+    }
+    
+    if (gws[gw_index].state == GW_ACTIVE) {
+	gws[gw_index].state = GW_PINGING + ping_inactivate_threshold_param;
+    } else if (gws[gw_index].state > GW_INACTIVE) {
+	gws[gw_index].state--;
+    }
+    if (gws[gw_index].state > GW_INACTIVE) {
+	LM_DBG("failing gw '%.*s' will be inactivated after '%u' "
+	       "more failure(s)\n",
+	       gws[gw_index].gw_name_len, gws[gw_index].gw_name,
+	       gws[gw_index].state - GW_INACTIVE);
+    } else {
+	LM_DBG("failing gw '%.*s' has been inactivated\n",
+	       gws[gw_index].gw_name_len, gws[gw_index].gw_name);
+    }
+
     return 1;
     return 1;
-}    
+}
 
 
 
 
 /*
 /*
@@ -2180,6 +2332,91 @@ int rpc_defunct_gw(unsigned int lcr_id, unsigned int gw_id, unsigned int period)
 }
 }
 
 
 
 
+/* Check if OPTIONS ping reply matches a SIP reply code listed in
+   ping_valid_reply_codes param */
+static int check_extra_codes(unsigned int code)
+{
+    unsigned int i;
+    for (i = 0; i < ping_rc_count; i++) {
+	if (ping_valid_reply_codes[i] == code) return 0;
+    }
+    return -1;
+}
+
+
+/* Callback function that is executed when OPTIONS ping reply has been
+   received */
+static void ping_callback(struct cell *t, int type, struct tmcb_params *ps)
+{
+
+    struct gw_info *gw;
+    str uri;
+
+    gw = (struct gw_info *)(*ps->param);
+
+    /* SIP URI is taken from the Transaction.
+     * Remove the "To: <" (s+5) and the trailing >+new-line (s - 5 (To: <)
+     * - 3 (>\r\n)). */
+    uri.s = t->to.s + 5;
+    uri.len = t->to.len - 8;
+
+    LM_DBG("OPTIONS %.*s finished with code <%d>\n", 
+	   uri.len, uri.s, ps->code);
+
+    if (((ps->code >= 200) && (ps->code <= 299)) ||
+	(check_extra_codes(ps->code) == 0)) {
+	if ((uri.len == gw->uri_len) &&
+	    (strncmp(uri.s, &(gw->uri[0]), uri.len) == 0)) {
+	    LM_DBG("activating gw with uri %.*s\n", uri.len, uri.s);
+	    gw->state = GW_ACTIVE;
+	} else {
+	    LM_DBG("ignoring OPTIONS reply due to lcr.reload\n");
+	}
+    }
+
+    return;
+}
+
+
+/* Timer process for pinging inactive gateways */
+void ping_timer(unsigned int ticks, void* param)
+{
+    struct gw_info *gws;
+    uac_req_t uac_r;
+    str uri;
+    unsigned int i, j;
+
+    /* Ping each gateway that is GW_PINGING state */
+
+    for (j = 1; j <= lcr_count_param; j++) {
+
+	gws = gw_pt[j];
+
+	for (i = 1; i <= gws[0].ip_addr.u.addr32[0]; i++) {
+
+	    if (gws[i].state >= GW_PINGING) {
+
+		uri.s = &(gws[i].uri[0]);
+		uri.len = gws[i].uri_len;
+		LM_DBG("pinging gw uri %.*s\n", uri.len, uri.s);
+
+		set_uac_req(&uac_r, &ping_method, 0, 0, 0,
+			    TMCB_LOCAL_COMPLETED, ping_callback,
+			    (void*)&(gws[i]));
+		if (ping_socket_param.len > 0) {
+		    uac_r.ssock = &ping_socket_param;
+		}
+		
+		if (tmb.t_request(&uac_r, &uri, &uri, &ping_from_param, 0)
+		    < 0) {
+		    LM_ERR("unable to ping [%.*s]\n", uri.len, uri.s);
+		}
+	    }
+	}
+    }
+}
+
+
 /*
 /*
  * When called first time, rewrites scheme, host, port, and
  * When called first time, rewrites scheme, host, port, and
  * transport parts of R-URI based on first gw_uri_avp value, which is then
  * transport parts of R-URI based on first gw_uri_avp value, which is then
@@ -2311,7 +2548,7 @@ static int do_from_gw(struct sip_msg* _m, unsigned int lcr_id,
 
 
     /* Store tag and flags and return result */
     /* Store tag and flags and return result */
     if ((res != NULL) &&
     if ((res != NULL) &&
-  	((transport == PROTO_NONE) || (res->transport == transport))) {
+  	((transport == PROTO_NONE) || (res->transport_code == transport))) {
 	LM_DBG("request game from gw\n");
 	LM_DBG("request game from gw\n");
 	if (tag_avp_param) {
 	if (tag_avp_param) {
 	    val.s.s = res->tag;
 	    val.s.s = res->tag;
@@ -2496,7 +2733,7 @@ static int do_to_gw(struct sip_msg* _m, unsigned int lcr_id,
 
 
     /* Return result */
     /* Return result */
     if ((res != NULL) &&
     if ((res != NULL) &&
-  	((transport == PROTO_NONE) || (res->transport == transport))) {
+  	((transport == PROTO_NONE) || (res->transport_code == transport))) {
 	LM_DBG("request goes to gw\n");
 	LM_DBG("request goes to gw\n");
 	return 1;
 	return 1;
     } else {
     } else {

+ 16 - 3
modules/lcr/lcr_mod.h

@@ -3,7 +3,7 @@
  *
  *
  * Various lcr related constant, types, and external variables
  * Various lcr related constant, types, and external variables
  *
  *
- * Copyright (C) 2005-2012 Juha Heinanen
+ * Copyright (C) 2005-2014 Juha Heinanen
  *
  *
  * This file is part of SIP Router, a free SIP server.
  * This file is part of SIP Router, a free SIP server.
  *
  *
@@ -52,6 +52,7 @@
 #define MAX_TAG_LEN 64
 #define MAX_TAG_LEN 64
 #define MAX_USER_LEN 64
 #define MAX_USER_LEN 64
 #define MAX_PARAMS_LEN 64
 #define MAX_PARAMS_LEN 64
+#define MAX_NO_OF_REPLY_CODES 15
 
 
 typedef enum sip_protos uri_transport;
 typedef enum sip_protos uri_transport;
 
 
@@ -89,16 +90,25 @@ struct instance {
     struct instance *next;
     struct instance *next;
 };
 };
 
 
+/* gw states */
+/* if state > GW_INACTIVE has not yet reached failure threshold */
+#define GW_ACTIVE 0
+#define GW_PINGING 1
+#define GW_INACTIVE 2
+
 struct gw_info {
 struct gw_info {
     unsigned int gw_id;
     unsigned int gw_id;
     char gw_name[MAX_NAME_LEN];
     char gw_name[MAX_NAME_LEN];
     unsigned short gw_name_len;
     unsigned short gw_name_len;
-    uri_type scheme;
+    char scheme[5];
+    unsigned short scheme_len;
     struct ip_addr ip_addr;
     struct ip_addr ip_addr;
     char hostname[MAX_HOST_LEN];
     char hostname[MAX_HOST_LEN];
     unsigned short hostname_len;
     unsigned short hostname_len;
     unsigned int port;
     unsigned int port;
-    uri_transport transport;
+    uri_transport transport_code;
+    char transport[15];
+    unsigned int transport_len;
     char params[MAX_PARAMS_LEN];
     char params[MAX_PARAMS_LEN];
     unsigned short params_len;
     unsigned short params_len;
     unsigned int strip;
     unsigned int strip;
@@ -107,6 +117,9 @@ struct gw_info {
     char tag[MAX_TAG_LEN];
     char tag[MAX_TAG_LEN];
     unsigned short tag_len;
     unsigned short tag_len;
     unsigned int flags;
     unsigned int flags;
+    unsigned short state;
+    char uri[MAX_URI_LEN];
+    unsigned short uri_len;
     unsigned int defunct_until;
     unsigned int defunct_until;
 };
 };
 
 

+ 9 - 30
modules/lcr/lcr_rpc.c

@@ -63,8 +63,7 @@ static void dump_gws(rpc_t* rpc, void* c)
 {
 {
     void* st;
     void* st;
     unsigned int i, j;
     unsigned int i, j;
-    enum sip_protos transport;
-    str gw_name, hostname, params;
+    str scheme, gw_name, hostname, params, transport;
     str prefix, tag;
     str prefix, tag;
     struct gw_info *gws;
     struct gw_info *gws;
     char buf[INT2STR_MAX_LEN], *start;
     char buf[INT2STR_MAX_LEN], *start;
@@ -82,11 +81,9 @@ static void dump_gws(rpc_t* rpc, void* c)
 	    gw_name.s=gws[i].gw_name;
 	    gw_name.s=gws[i].gw_name;
 	    gw_name.len=gws[i].gw_name_len;
 	    gw_name.len=gws[i].gw_name_len;
 	    rpc->struct_add(st, "S", "gw_name", &gw_name);
 	    rpc->struct_add(st, "S", "gw_name", &gw_name);
-	    if (gws[i].scheme == SIP_URI_T) {
-		rpc->struct_add(st, "s", "scheme", "sip");
-	    } else {
-		rpc->struct_add(st, "s", "scheme", "sips");
-	    }
+	    scheme.s=gws[i].scheme;
+	    scheme.len=gws[i].scheme_len;
+	    rpc->struct_add(st, "S", "scheme", &scheme);
 	    switch (gws[i].ip_addr.af) {
 	    switch (gws[i].ip_addr.af) {
 	    case AF_INET:
 	    case AF_INET:
 		rpc->struct_printf(st, "ip_addr", "%d.%d.%d.%d",
 		rpc->struct_printf(st, "ip_addr", "%d.%d.%d.%d",
@@ -117,39 +114,21 @@ static void dump_gws(rpc_t* rpc, void* c)
 	    params.s=gws[i].params;
 	    params.s=gws[i].params;
 	    params.len=gws[i].params_len;
 	    params.len=gws[i].params_len;
 	    rpc->struct_add(st, "S", "params", &params);
 	    rpc->struct_add(st, "S", "params", &params);
-	    transport = gws[i].transport;
-	    switch(transport){
-	    case PROTO_UDP:
-		rpc->struct_add(st, "s", "transport", "UDP");
-		break;
-	    case PROTO_TCP:
-		rpc->struct_add(st, "s", "transport", "TCP");
-		break;
-	    case PROTO_TLS:
-		rpc->struct_add(st, "s", "transport", "TLS");
-		break;
-	    case PROTO_SCTP:
-		rpc->struct_add(st, "s", "transport", "SCTP");
-		break;
-	    case PROTO_OTHER:
-		rpc->struct_add(st, "s", "transport", "OTHER");
-		break;
-            case PROTO_WS:
-            case PROTO_WSS:
-	    case PROTO_NONE:
-		break;
-	    }
+	    transport.s=gws[i].transport;
+	    transport.len=gws[i].transport_len;
+	    rpc->struct_add(st, "S", "transport", &transport);
 	    prefix.s=gws[i].prefix;
 	    prefix.s=gws[i].prefix;
 	    prefix.len=gws[i].prefix_len;
 	    prefix.len=gws[i].prefix_len;
 	    tag.s=gws[i].tag;
 	    tag.s=gws[i].tag;
 	    tag.len=gws[i].tag_len;
 	    tag.len=gws[i].tag_len;
 	    start = int2strbuf(gws[i].defunct_until, &(buf[0]), INT2STR_MAX_LEN,
 	    start = int2strbuf(gws[i].defunct_until, &(buf[0]), INT2STR_MAX_LEN,
 			       &len);
 			       &len);
-	    rpc->struct_add(st, "dSSds",
+	    rpc->struct_add(st, "dSSdds",
 			    "strip",  gws[i].strip,
 			    "strip",  gws[i].strip,
 			    "prefix", &prefix,
 			    "prefix", &prefix,
 			    "tag",    &tag,
 			    "tag",    &tag,
 			    "flags",  gws[i].flags,
 			    "flags",  gws[i].flags,
+			    "state",  gws[i].state,
 			    "defunct_until",  start
 			    "defunct_until",  start
 			    );
 			    );
 	}
 	}

+ 4 - 0
modules/sctp/sctp_mod.c

@@ -37,7 +37,9 @@
 MODULE_VERSION
 MODULE_VERSION
 
 
 static int mod_init(void);
 static int mod_init(void);
+#ifdef USE_SCTP
 static int sctp_mod_pre_init(void);
 static int sctp_mod_pre_init(void);
+#endif
 
 
 
 
 static cmd_export_t cmds[]={
 static cmd_export_t cmds[]={
@@ -122,6 +124,7 @@ static int mod_init(void)
 #endif /* USE_SCTP */
 #endif /* USE_SCTP */
 }
 }
 
 
+#ifdef USE_SCTP
 static int sctp_mod_pre_init(void)
 static int sctp_mod_pre_init(void)
 {
 {
 	sctp_srapi_t api;
 	sctp_srapi_t api;
@@ -143,3 +146,4 @@ static int sctp_mod_pre_init(void)
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+#endif