소스 검색

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

Juha Heinanen 11 년 전
부모
커밋
85fdd590ca
7개의 변경된 파일690개의 추가작업 그리고 180개의 파일을 삭제
  1. 160 45
      modules/lcr/README
  2. 1 1
      modules/lcr/doc/lcr.xml
  3. 162 0
      modules/lcr/doc/lcr_admin.xml
  4. 338 101
      modules/lcr/lcr_mod.c
  5. 16 3
      modules/lcr/lcr_mod.h
  6. 9 30
      modules/lcr/lcr_rpc.c
  7. 4 0
      modules/sctp/sctp_mod.c

+ 160 - 45
modules/lcr/README

@@ -10,7 +10,7 @@ Juha Heinanen
 
    <[email protected]>
 
-   Copyright © 2005-2010 Juha Heinanen
+   Copyright (c) 2005-2014 Juha Heinanen
      __________________________________________________________________
 
    Table of Contents
@@ -63,16 +63,22 @@ Juha Heinanen
               3.36. lcr_gw_count (integer)
               3.37. dont_strip_or_tag_flag (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.1. load_gws(lcr_id[, uri_user[, caller_uri]])
               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
 
@@ -123,18 +129,24 @@ Juha Heinanen
    1.36. Setting lcr_gw_count module parameter
    1.37. Setting dont_strip_or_tag_flag module 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
 
@@ -186,16 +198,22 @@ Chapter 1. Admin Guide
         3.36. lcr_gw_count (integer)
         3.37. dont_strip_or_tag_flag (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.1. load_gws(lcr_id[, uri_user[, caller_uri]])
         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
 
@@ -332,6 +350,11 @@ Chapter 1. Admin Guide
    3.36. lcr_gw_count (integer)
    3.37. dont_strip_or_tag_flag (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)
 
@@ -788,15 +811,86 @@ modparam("lcr", "dont_strip_or_tag_flag", 10)
 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.1. load_gws(lcr_id[, uri_user[, caller_uri]])
    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]])
 
@@ -818,7 +912,7 @@ modparam("lcr", "fetch_rows", 3000)
 
    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))) {
         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.
 
-   Example 1.40. next_gw usage from a route block
+   Example 1.45. next_gw usage from a route block
 ...
 if (!next_gw()) {
         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()) {
         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
    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.
 
-   Example 1.42. defunct_gw usage
+   Example 1.48. defunct_gw usage
 ...
 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
    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,
    ONREPLY_ROUTE.
 
-   Example 1.43. from_gw usage
+   Example 1.49. from_gw usage
 ...
 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
    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,
    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);
 ...
 
-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
    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.
 
-   Example 1.45. to_gw usage
+   Example 1.51. to_gw usage
 ...
 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
    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.
 
-   Example 1.46. to_gw usage
+   Example 1.52. to_gw usage
 ...
 if (to_any_gw("192.55.66.2", 1)) {
         ...
@@ -1008,7 +1123,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
    Parameters: none
 
-   Example 1.47. lcr.reload RPC example
+   Example 1.53. lcr.reload RPC example
                 $ kamcmd lcr.reload
 
 5.2. lcr.dump_gws
@@ -1017,7 +1132,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
    Parameters: none
 
-   Example 1.48. lcr.dump_gws RPC example
+   Example 1.54. lcr.dump_gws RPC example
                 $ kamcmd lcr.dump_gws
 
 5.3. lcr.dump_rules
@@ -1027,7 +1142,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
    Parameters: none
 
-   Example 1.49. lcr.dump_rules RPC example
+   Example 1.55. lcr.dump_rules RPC example
                 $ kamcmd lcr.dump_rules
 
 5.4. lcr.defunct_gw
@@ -1040,7 +1155,7 @@ if (to_any_gw("192.55.66.2", 1)) {
 
    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
 
 6. Known Limitations

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

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

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

@@ -996,6 +996,136 @@ modparam("lcr", "fetch_rows", 3000)
 		</example>
 	</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>
@@ -1100,6 +1230,38 @@ if (!next_gw()) {
 		</example>
 	</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>
 		<title>
 		<function moreinfo="none">defunct_gw(period)</function>

+ 338 - 101
modules/lcr/lcr_mod.c

@@ -1,7 +1,7 @@
 /*
  * Least Cost Routing module
  *
- * Copyright (C) 2005-2012 Juha Heinanen
+ * Copyright (C) 2005-2014 Juha Heinanen
  * Copyright (C) 2006 Voice Sistem SRL
  *
  * This file is part of SIP Router, a free SIP server.
@@ -83,6 +83,7 @@
 #include "hash.h"
 #include "lcr_rpc.h"
 #include "../../rpc_lookup.h"
+#include "../../modules/tm/tm_load.h"
 
 MODULE_VERSION
 
@@ -210,6 +211,12 @@ static unsigned int defunct_capability_param = 0;
 /* dont strip or tag param */
 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
@@ -237,6 +244,13 @@ struct gw_info **gw_pt = (struct gw_info **)NULL;
 /* Pointer to rule_id info hash table */
 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
  */
@@ -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 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 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);
@@ -266,6 +281,8 @@ static cmd_export_t cmds[] = {
     {"next_gw", (cmd_function)next_gw, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
     {"defunct_gw", (cmd_function)defunct_gw, 1, 0, 0,
      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,
      REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
     {"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},
     {"dont_strip_or_prefix_flag",INT_PARAM, &dont_strip_or_prefix_flag_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}
 };
 
@@ -406,6 +428,7 @@ static int mod_init(void)
     str s;
     unsigned short avp_flags;
     unsigned int i;
+    char *at, *past, *sep;
 
     /* Register RPC commands */
     if (rpc_register_array(lcr_rpc)!=0) {
@@ -441,6 +464,9 @@ static int mod_init(void)
     tag_col.len = strlen(tag_col.s);
     flags_col.len = strlen(flags_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 */
     if (lcr_db_bind(&db_url)) {
@@ -539,10 +565,15 @@ static int mod_init(void)
 	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) {
 	    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)) {
 		LM_ERR("malformed or non AVP definition <%s>\n",
 		       defunct_gw_avp_param);
@@ -560,7 +591,7 @@ static int mod_init(void)
 	}
 	if (lcr_id_avp_param && *lcr_id_avp_param) {
 	    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)) {
 		LM_ERR("malformed or non AVP definition <%s>\n",
 		       lcr_id_avp_param);
@@ -576,6 +607,48 @@ static int mod_init(void)
 	    LM_ERR("AVP lcr_id_avp has not been defined\n");
 	    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) {
@@ -641,8 +714,8 @@ static int mod_init(void)
     /* gw tables themselves */
     /* ordered by ip_addr for from_gw/to_gw functions */
     /* 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++) {
 	gw_pt[i] = (struct gw_info *)shm_malloc(sizeof(struct gw_info) *
 						(lcr_gw_count_param + 1));
@@ -701,6 +774,7 @@ static void destroy(void)
 static void free_shared_memory(void)
 {
     int i;
+
     for (i = 0; i <= lcr_count_param; i++) {
 	if (rule_pt && 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,
 		     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 *hostname, unsigned int hostname_len,
 		     char *ip_string, unsigned int strip, char *prefix,
 		     unsigned int prefix_len, char *tag, unsigned int tag_len,
 		     unsigned int flags, unsigned int defunct_until)
 {
+    char *at, *string;
+    int len;
+
     gws[i].gw_id = gw_id;
     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].scheme = scheme;
+    memcpy(&(gws[i].scheme[0]), scheme, scheme_len);
+    gws[i].scheme_len = scheme_len;
     gws[i].ip_addr = *ip_addr;
     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);
     gws[i].params_len = params_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);
     gws[i].flags = flags;
     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;
 }
 
@@ -888,14 +995,14 @@ static int insert_gws(db1_res_t *res, struct gw_info *gws,
 		      unsigned int *gw_cnt)
 {
     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;
     struct in_addr in_addr;
     struct ip_addr ip_addr, *ip_p;
     str ip_string;
-    uri_type scheme;
-    uri_transport transport;
     
     for (i = 0; i < RES_ROW_N(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;
 	}
 	if (VAL_NULL(ROW_VALUES(row) + 3)) {
-	    scheme = SIP_URI_T;
+	    scheme = "sip:";
+	    scheme_len = 4;
 	} else {
 	    if (VAL_TYPE(ROW_VALUES(row) + 3) != DB1_INT) {
 		LM_ERR("lcr_gw uri scheme at row <%u> is not int\n", i);
 		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)) {
-	    transport = PROTO_NONE;
+	    transport_code = PROTO_NONE;
+	    transport = "";
+	    transport_len = 0;
 	} else {
 	    if (VAL_TYPE(ROW_VALUES(row) + 4) != DB1_INT) {
 		LM_ERR("lcr_gw transport at row <%u> is not int\n", i);
 		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 "
-		   "scheme at row <%u>\n", transport, i);
+		   "scheme at row <%u>\n", transport_code, i);
 	    return 0;
 	}
 	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)++;
 	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,
 		       tag, tag_len, flags, defunct_until)) {
 	    return 0;
@@ -1512,7 +1647,7 @@ int reload_tables()
 	lcr_dbf.free_result(dbh, res);
 	res = NULL;
 
-	/* swap tables */
+	/* Swap tables */
 	rule_pt_tmp = rule_pt[lcr_id];
 	gw_pt_tmp = gw_pt[lcr_id];
 	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,
 			    char *prefix, unsigned int prefix_len,
 			    char *tag, unsigned int tag_len,
 			    struct ip_addr *ip_addr, char *hostname, 
 			    unsigned int hostname_len, unsigned int port,
 			    char *params, unsigned int params_len,
-			    uri_transport transport, unsigned int flags)
+			    char *transport, unsigned int transport_len,
+			    unsigned int flags)
 {
     char *at, *string;
     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_chr(at, '|');
     /* scheme */
-    string = int2str(scheme, &len);
-    append_str(at, string, len);
+    append_str(at, scheme, scheme_len);
     append_chr(at, '|');
     /* strip */
     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_chr(at, '|');
     /* transport */
-    string = int2str(transport, &len);
-    append_str(at, string, len);
+    append_str(at, transport, transport_len);
     append_chr(at, '|');
     /* flags */
     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;
     str2int(&s, gw_index);
     /* scheme */
-    s.s = sep + 1;
-    sep = index(s.s, '|');
+    scheme->s = sep + 1;
+    sep = index(scheme->s, '|');
     if (sep == NULL) {
 	LM_ERR("scheme was not found in AVP value\n");
 	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 */
     s.s = sep + 1;
     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;
     sep = index(port->s, '|');
     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;
     }
     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;
     /* transport */
-    s.s = sep + 1;
-    sep = index(s.s, '|');
+    transport->s = sep + 1;
+    sep = index(transport->s, '|');
     if (sep == NULL) {
 	LM_ERR("transport was not found in AVP value\n");
 	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 */
     s.s = sep + 1;
     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;
 	}
 	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,
 			     gws[index].tag, tag_len,
 			     &gws[index].ip_addr,
 			     gws[index].hostname, hostname_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]);
 	val.s = value;
 	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 */
 	    t = rule->targets;
 	    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].prefix_len = pl->prefix_len;
 		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 */
     if (defunct_capability_param == 0) {
 	LM_ERR("no defunct gw capability, activate by setting "
-	       "defunct_capability_param module param\n");
+	       "defunct_capability module param\n");
 	return -1;
     }
 
-    /* Get parameter value */
-
     /* Get and check parameter value */
     defunct_period = strtol(_defunct_period, &tmp, 10);
     if ((tmp == 0) || (*tmp) || (tmp == _defunct_period)) {
 	LM_ERR("invalid defunct_period parameter %s\n", _defunct_period);
 	return -1;
     }
-    if (defunct_period < 1) {
+    if (defunct_period < 0) {
 	LM_ERR("invalid defunct_period param value %d\n", defunct_period);
 	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];
     if (search_first_avp(defunct_gw_avp_type, defunct_gw_avp,
 			 &index_val, 0) == NULL) {
-	LM_ERR("defucnt_gw_avp was not found\n");
+	LM_ERR("defunct_gw_avp was not found\n");
 	return -1;
     }
     gw_index = index_val.n;
@@ -2139,13 +2238,66 @@ static int defunct_gw(struct sip_msg* _m, char *_defunct_period, char *_s2)
 	return -1;
     }
     
-    /* Defunct gw */
     defunct_until = time((time_t *)NULL) + defunct_period;
     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].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;
-}    
+}
 
 
 /*
@@ -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
  * 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 */
     if ((res != NULL) &&
-  	((transport == PROTO_NONE) || (res->transport == transport))) {
+  	((transport == PROTO_NONE) || (res->transport_code == transport))) {
 	LM_DBG("request game from gw\n");
 	if (tag_avp_param) {
 	    val.s.s = res->tag;
@@ -2496,7 +2733,7 @@ static int do_to_gw(struct sip_msg* _m, unsigned int lcr_id,
 
     /* Return result */
     if ((res != NULL) &&
-  	((transport == PROTO_NONE) || (res->transport == transport))) {
+  	((transport == PROTO_NONE) || (res->transport_code == transport))) {
 	LM_DBG("request goes to gw\n");
 	return 1;
     } else {

+ 16 - 3
modules/lcr/lcr_mod.h

@@ -3,7 +3,7 @@
  *
  * 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.
  *
@@ -52,6 +52,7 @@
 #define MAX_TAG_LEN 64
 #define MAX_USER_LEN 64
 #define MAX_PARAMS_LEN 64
+#define MAX_NO_OF_REPLY_CODES 15
 
 typedef enum sip_protos uri_transport;
 
@@ -89,16 +90,25 @@ struct instance {
     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 {
     unsigned int gw_id;
     char gw_name[MAX_NAME_LEN];
     unsigned short gw_name_len;
-    uri_type scheme;
+    char scheme[5];
+    unsigned short scheme_len;
     struct ip_addr ip_addr;
     char hostname[MAX_HOST_LEN];
     unsigned short hostname_len;
     unsigned int port;
-    uri_transport transport;
+    uri_transport transport_code;
+    char transport[15];
+    unsigned int transport_len;
     char params[MAX_PARAMS_LEN];
     unsigned short params_len;
     unsigned int strip;
@@ -107,6 +117,9 @@ struct gw_info {
     char tag[MAX_TAG_LEN];
     unsigned short tag_len;
     unsigned int flags;
+    unsigned short state;
+    char uri[MAX_URI_LEN];
+    unsigned short uri_len;
     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;
     unsigned int i, j;
-    enum sip_protos transport;
-    str gw_name, hostname, params;
+    str scheme, gw_name, hostname, params, transport;
     str prefix, tag;
     struct gw_info *gws;
     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.len=gws[i].gw_name_len;
 	    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) {
 	    case AF_INET:
 		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.len=gws[i].params_len;
 	    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.len=gws[i].prefix_len;
 	    tag.s=gws[i].tag;
 	    tag.len=gws[i].tag_len;
 	    start = int2strbuf(gws[i].defunct_until, &(buf[0]), INT2STR_MAX_LEN,
 			       &len);
-	    rpc->struct_add(st, "dSSds",
+	    rpc->struct_add(st, "dSSdds",
 			    "strip",  gws[i].strip,
 			    "prefix", &prefix,
 			    "tag",    &tag,
 			    "flags",  gws[i].flags,
+			    "state",  gws[i].state,
 			    "defunct_until",  start
 			    );
 	}

+ 4 - 0
modules/sctp/sctp_mod.c

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