Browse Source

- added ser module for new version of iptrtpproxy-v2
- modified oob script to simplify patching with RTPPROXY stuff
- rtpproxy control rewritten in script
- re-INVITE won't proactively create new RTP session, it's not fully RFC3264 compliant but enable working with non-compliant clients (phones)

root 14 years ago
parent
commit
ffa10596bc

+ 91 - 41
etc/sip-router-oob.cfg

@@ -355,7 +355,10 @@ flags
   FLAG_SERWEB_RSVD2   : 10, # bit reserved for use with serweb
   FLAG_SERWEB_RSVD2   : 10, # bit reserved for use with serweb
   FLAG_SESSIONTIMER   : 11, # indicates that the UAC supports Session Timer
   FLAG_SESSIONTIMER   : 11, # indicates that the UAC supports Session Timer
   FLAG_RR_DONE        : 12, # the request got already one RR header
   FLAG_RR_DONE        : 12, # the request got already one RR header
-  FLAG_RTP_PROXY      : 13; # the RTP proxy is turned on
+  FLAG_RTP_PROXY      : 13, # the RTP proxy is turned on
+  FLAG_NAT_REG        : 14, # the UAC behind NAT, stored in location record
+  FLAG_INIT_DLG       : 15, # init INVITE dialog
+  FLAG_REVERSE_DIR    : 16; # set if request goes callee -> caller direction, requires rr.append_fromtag=1
 
 
 avpflags
 avpflags
   dialog_cookie;            # attribute will be stored in Route headers
   dialog_cookie;            # attribute will be stored in Route headers
@@ -366,12 +369,12 @@ avpflags
 #
 #
 #DEBCONF-DBURL-START
 #DEBCONF-DBURL-START
 modparam("speeddial|auth_db|usrloc|domain|uri_db|gflags|avp_db|db_ops",
 modparam("speeddial|auth_db|usrloc|domain|uri_db|gflags|avp_db|db_ops",
-         "db_url", "mysql://ser:heslo@127.0.0.1/ser")
+         "db_url", "mysql://ser:heslo@localhost/ser")
 #DEBCONF-DBURL-END
 #DEBCONF-DBURL-END
 
 
 # specify the path to your database for accounting
 # specify the path to your database for accounting
 #DEBCONF-DBURLACC-START
 #DEBCONF-DBURLACC-START
-modparam("acc_db", "db_url", "mysql://ser:heslo@127.0.0.1/ser")
+modparam("acc_db", "db_url", "mysql://ser:heslo@localhost/ser")
 #DEBCONF-DBURLACC-END
 #DEBCONF-DBURLACC-END
 
 
 
 
@@ -402,8 +405,8 @@ modparam("registrar", "max_expires", 600)
 modparam("registrar", "min_expires", 240)
 modparam("registrar", "min_expires", 240)
 
 
 # Identify natted contacts using a flag.
 # Identify natted contacts using a flag.
-modparam("registrar", "load_nat_flag", "FLAG_NAT")
-modparam("registrar", "save_nat_flag", "FLAG_NAT")
+modparam("registrar", "load_nat_flag", "FLAG_NAT_REG")
+modparam("registrar", "save_nat_flag", "FLAG_NAT_REG")
 
 
 # Maximum number of contacts.
 # Maximum number of contacts.
 modparam("registrar", "max_contacts", 10)
 modparam("registrar", "max_contacts", 10)
@@ -453,7 +456,7 @@ modparam("auth", "secret", "aqwedrftredswqwddcft")
 modparam("rr", "enable_full_lr", 1)
 modparam("rr", "enable_full_lr", 1)
 
 
 # Limit the length of the AVP cookie to necessary attributes only
 # Limit the length of the AVP cookie to necessary attributes only
-modparam("rr", "cookie_filter", "(account|rproxy|stimer)")
+modparam("rr", "cookie_filter", "(account|rproxy|stimer|dialog_id)")
 
 
 # You probably do not want that someone can simply read and change
 # You probably do not want that someone can simply read and change
 # the AVP cookie in your Routes, thus should really change this
 # the AVP cookie in your Routes, thus should really change this
@@ -462,6 +465,7 @@ modparam("rr", "cookie_secret", "sgsatewgdbsnmpoiewh")
 
 
 # The ftag Route parameter may be used to easily determine if a BYE
 # The ftag Route parameter may be used to easily determine if a BYE
 # is coming from caller or callee, but we prefer shorter messages
 # is coming from caller or callee, but we prefer shorter messages
+# Enable when FLAG_REVERSE_DIR is to be used
 modparam("rr", "append_fromtag", 0)
 modparam("rr", "append_fromtag", 0)
 
 
 
 
@@ -661,7 +665,7 @@ route[FORWARD]
 	# If this is an initial INVITE (without a To-tag) we might try
 	# If this is an initial INVITE (without a To-tag) we might try
 	# another target (call forwarding or voicemail) after receiving
 	# another target (call forwarding or voicemail) after receiving
 	# an error.
 	# an error.
-	if (method=="INVITE" && strempty(@to.tag)) {
+	if (isflagset(FLAG_INIT_DLG)) {
 		t_on_failure("FAILURE_ROUTE");
 		t_on_failure("FAILURE_ROUTE");
 	}
 	}
 
 
@@ -720,10 +724,22 @@ route[INIT]
 	# Check if the UAC is NATed and fix the message accordingly
 	# Check if the UAC is NATed and fix the message accordingly
 	route(UAC_NAT_DETECTION);
 	route(UAC_NAT_DETECTION);
 
 
+	# if needed then we MUST put after force_rport() which is located in NAT_DETECTION!!!
+	# Check t_reply() vs. sl_reply() usage in script
+	#if (!t_newtran()) {
+	#	sl_reply("500", "Internal tm error");
+	#	drop;
+	#}
+														
 	# Activate accounting for all initial INVITEs. In-dialog requests
 	# Activate accounting for all initial INVITEs. In-dialog requests
 	# are accounted by a RR cookie (see below).
 	# are accounted by a RR cookie (see below).
-	if (method == "INVITE" && strempty(@to.tag)) {
+	# It should work also when the call has been already forked at a previous router
+	if (method == "INVITE" && !isflagset(FLAG_TOTAG)) {
+		$dialog_id = @sys.unique; # make unique dialogid
 		setflag(FLAG_ACC);
 		setflag(FLAG_ACC);
+		setflag(FLAG_INIT_DLG);
+	} else if (isflagset(FLAG_TOTAG) && @hf_value.route[0].params.ftag != @from.tag) {
+		setflag(FLAG_REVERSE_DIR);  # callee -> caller
 	}
 	}
 
 
 	# Set flag and use it instead of the attribute.
 	# Set flag and use it instead of the attribute.
@@ -750,7 +766,7 @@ route[OPTIONS_REPLY]
 
 
 
 
 # Check if the sender of the request is behind a NAT device. If so,
 # Check if the sender of the request is behind a NAT device. If so,
-# fix the request so that other devices can talk to the sender none the less.
+# fix the request so that other devices can talk to the sender nonetheless.
 #
 #
 route[UAC_NAT_DETECTION]
 route[UAC_NAT_DETECTION]
 {
 {
@@ -806,7 +822,13 @@ route[UAC_NAT_DETECTION]
 		else {
 		else {
 			# Overwrite the Contact to allow proper in-dialog
 			# Overwrite the Contact to allow proper in-dialog
 			# routing.
 			# routing.
-			fix_nated_contact();
+			# but do not override if there is already a proxy in the path, we'll route by record-route,
+			# RURI responsibility takes to previous proxy
+			# TODO: shouldn't we rather limit to methods which are dialog aware (INVITE, UPDATE, SUBSCRIBE, ..)
+			if (strempty(@hf_value.record_route) || (@hf_value["contact"]!="" && @contact.uri.params.maddr!="")) {
+				fix_nated_contact();
+			}
+
 		}
 		}
 	}
 	}
 }
 }
@@ -847,6 +869,7 @@ route[UAS_NAT_DETECTION]
 		(nat_uac_test("1") ||
 		(nat_uac_test("1") ||
 		(@hf_value["contact"] != "" && @contact.uri.params.maddr != "")))
 		(@hf_value["contact"] != "" && @contact.uri.params.maddr != "")))
 	{
 	{
+		# TODO: check if no proxy between UAS&myself
 		fix_nated_contact();
 		fix_nated_contact();
 	}
 	}
 }
 }
@@ -900,6 +923,17 @@ route[PROCESS_ROUTES]
 	# subsequent messages withing a dialog should take the
 	# subsequent messages withing a dialog should take the
 	# path determined by the Route headers.
 	# path determined by the Route headers.
 	if (loose_route()) {
 	if (loose_route()) {
+		if (!defined $dialog_id) {
+			$dialog_id = $t.dialog_id; # there is only 1 dialog_id
+		}
+		xlog("L_DEBUG", "\n%mb\n\ndialogid -/from/to=%$dialog_id/%$f.dialog_id/%$t.dialog_id");
+		if (method == "INVITE" || method == "UPDATE" || method == "ACK" || method == "BYE") {
+			if (!defined $dialog_id) {
+				sl_reply("400", "Missing cookie");
+				drop;
+			}
+		}
+
 		# Mark routing logic in request.
 		# Mark routing logic in request.
 		append_hf("P-hint: rr-enforced\r\n");
 		append_hf("P-hint: rr-enforced\r\n");
 
 
@@ -961,6 +995,8 @@ route[RECORD_ROUTE]
 			setavpflag($account, "dialog_cookie");
 			setavpflag($account, "dialog_cookie");
 		}
 		}
 
 
+		setavpflag("$f.dialog_id", "dialog_cookie");
+
 		# Insert the RR header.
 		# Insert the RR header.
 		record_route();
 		record_route();
 
 
@@ -1035,7 +1071,11 @@ route[REGISTRAR]
 		# under this UID.  Note that this only works if local policy
 		# under this UID.  Note that this only works if local policy
 		# causes UIDs to have form compliant to RFC3261 URI
 		# causes UIDs to have form compliant to RFC3261 URI
 		# usernames.
 		# usernames.
-		$tu.uid = @ruri.user;
+		if (@ruri.user!="")
+			$tu.uid = @ruri.user;
+		if (isflagset(FLAG_NAT)) {
+			setflag(FLAG_NAT_REG);
+		}
 		if (!save_mem_nr("location")) {
 		if (!save_mem_nr("location")) {
 			log(1, "Error while saving replicated REGISTER.\n");
 			log(1, "Error while saving replicated REGISTER.\n");
 		}
 		}
@@ -1107,6 +1147,9 @@ route[REGISTRAR]
 	#	drop;
 	#	drop;
 	#}
 	#}
 
 
+	if (isflagset(FLAG_NAT)) {
+		setflag(FLAG_NAT_REG);
+	}
 	# Everything is fine. Store the binding.
 	# Everything is fine. Store the binding.
 	if (!save_contacts("location")) {
 	if (!save_contacts("location")) {
 		sl_reply("400", "Invalid REGISTER Request");
 		sl_reply("400", "Invalid REGISTER Request");
@@ -1277,6 +1320,10 @@ route[INBOUND]
 	if (lookup_contacts("location")) {
 	if (lookup_contacts("location")) {
 		append_hf("P-hint: usrloc applied\r\n");
 		append_hf("P-hint: usrloc applied\r\n");
 
 
+		# destination is behind NAT
+		if (isflagset(FLAG_NAT_REG)) {
+			setflag(FLAG_NAT); /* client was behind NAT when made registration */
+		}
 		# We set the tm module timers according to the prefences
 		# We set the tm module timers according to the prefences
 		# of the callee (avoid too long ringing of his phones).
 		# of the callee (avoid too long ringing of his phones).
 		# Note1: Timer values have to be in ms now!
 		# Note1: Timer values have to be in ms now!
@@ -1337,7 +1384,7 @@ route[PSTN]
 	# format that is accepted by your gateway here.
 	# format that is accepted by your gateway here.
 
 
 	# Check permissions of the caller for initial INVITEs.
 	# Check permissions of the caller for initial INVITEs.
-	if (method == "INVITE" && strempty(@to.tag)) {
+	if (isflagset(FLAG_INIT_DLG)) {
 		if ($f.gw_acl != "1") {
 		if ($f.gw_acl != "1") {
 			sl_reply("403", "PSTN Not Permitted");
 			sl_reply("403", "PSTN Not Permitted");
 			drop;
 			drop;
@@ -1375,6 +1422,7 @@ route[CATCH_CANCEL]
 	if (method == CANCEL) {
 	if (method == CANCEL) {
 		# t_relay_cancel() will stop processing if a matching
 		# t_relay_cancel() will stop processing if a matching
 		# INVITE was found.
 		# INVITE was found.
+		xlog("L_DEBUG", "catching cancel dialogid=%$dialog_id\n");
 		if (!t_relay_cancel()) {
 		if (!t_relay_cancel()) {
 			# An INVITE was found but some error occurred.
 			# An INVITE was found but some error occurred.
 			sl_reply("500", "Internal Server Error");
 			sl_reply("500", "Internal Server Error");
@@ -1505,37 +1553,39 @@ route[ENUM]
 #
 #
 failure_route[FAILURE_ROUTE]
 failure_route[FAILURE_ROUTE]
 {
 {
-	# Mark that we are operating from a failure route.
-	setflag(FLAG_FAILUREROUTE);
-
-	if (t_check_status("486|600")) {
-		# If we received a busy and a busy target is set, forward
-		# it there.
-		# Note: Again, the forwarding target has to be a routeable
-		# URI. We redirect using 3xx to avoid possible issues with
-		# credentials (if we consumed them, they may be missing in
-		# a loop, if we don't consume them, messages are bigger and
-		# more vulnerable)
-		if ($tu.fwd_busy_target != "") {
-			#attr2uri("$tu.fwd_busy_target");
-			#route(FORWARD);
-			attr_destination("$tu.fwd_busy_target");
-			t_reply("302", "Redirect On Busy");
+	if (isflagset(FLAG_INIT_DLG)) {
+		# Mark that we are operating from a failure route.
+		setflag(FLAG_FAILUREROUTE);
+
+		if (t_check_status("486|600")) {
+			# If we received a busy and a busy target is set, forward
+			# it there.
+			# Note: Again, the forwarding target has to be a routeable
+			# URI. We redirect using 3xx to avoid possible issues with
+			# credentials (if we consumed them, they may be missing in
+			# a loop, if we don't consume them, messages are bigger and
+			# more vulnerable)
+			if ($tu.fwd_busy_target != "") {
+				attr2uri("$tu.fwd_busy_target");
+				#attr_destination("$tu.fwd_busy_target");
+				#route(FORWARD);
+				t_reply("302", "Redirect On Busy");
+			}
+			# Alternatively, you could forward the request to
+			# SEMS/voicemail here
 		}
 		}
-		# Alternatively, you could forward the request to
-		# SEMS/voicemail here
-	}
-	else if (t_check_status("408|480")) {
-		# If we received no answer and the noanswer target is set,
-		# forward it there.
-		# Note: See above.
-		if ($tu.fwd_noanswer_target != "") {
-			#attr2uri("$tu.fwd_noanswer_target");
-			#route(FORWARD);
-			attr_destination("$tu.fwd_noanswer_target");
-			t_reply("302", "Redirect On No Answer");
+		else if (t_check_status("408|480")) {
+			# If we received no answer and the noanswer target is set,
+			# forward it there.
+			# Note: See above.
+			if ($tu.fwd_noanswer_target != "") {
+				attr2uri("$tu.fwd_noanswer_target");
+				#attr_destination("$tu.fwd_noanswer_target");
+				#route(FORWARD);
+				t_reply("302", "Redirect On No Answer");
+			}
 		}
 		}
-	}
+	} # if (isflagset...
 }
 }
 
 
 
 

+ 16 - 3
modules/iptrtpproxy/Makefile

@@ -6,8 +6,17 @@
 # WARNING: do not run this directly, it should be run by the master Makefile
 # WARNING: do not run this directly, it should be run by the master Makefile
 #
 #
 
 
+# select if xtables.h (=0) or iptables.h (=1)  header files are installed 
+IPT_RTPPROXY_IPTABLES_API=1
+
 # define path to netfilter/iptables source directory
 # define path to netfilter/iptables source directory
-iptdir = ~/iptables
+#iptdir = ~/iptables
+iptdir = /root/netfilter-RTPPROXY-v2
+#iptdir=/mnt/home/tmandys/work/tma/netfilter-RTPPROXY-v2 
+#iptdir=$(BUILDHOME)/apps/iptrtpproxy-v2 
+#kerneldir=/usr/src/kernels/2.6.18-1.2849prerel3.3.0_63.1.0-i686/include 
+kerneldir=/root/iptables-1.4.2/include
+
 include ../../Makefile.defs
 include ../../Makefile.defs
 auto_gen=
 auto_gen=
 NAME=iptrtpproxy.so
 NAME=iptrtpproxy.so
@@ -15,8 +24,12 @@ NAME=iptrtpproxy.so
 #		-L $(iptdir)/extensions -lext
 #		-L $(iptdir)/extensions -lext
 # look for libipt_RTPPROXY first under /lib/xtables (default install dir)
 # look for libipt_RTPPROXY first under /lib/xtables (default install dir)
 # and if not found in $(iptdir)/extensions (compile dir)
 # and if not found in $(iptdir)/extensions (compile dir)
-LIBS = -L/lib/xtables -L$(iptdir)/extensions -lipt_RTPPROXY -ldl
-INCLUDES = -I$(iptdir)/include
+LIBS = -L $(iptdir)/libiptc -L $(iptdir)/iptables/extensions -lipt_RTPPROXY
+INCLUDES = -I $(iptdir)/linux-2.6/include -I $(kerneldir) -I $(iptdir)/iptables/include
+
+ifeq ($(IPT_RTPPROXY_IPTABLES_API),1)
+DEFS+= -DIPT_RTPPROXY_IPTABLES_API=1
+endif
 
 
 DEFS+=-DSER_MOD_INTERFACE
 DEFS+=-DSER_MOD_INTERFACE
 
 

+ 338 - 64
modules/iptrtpproxy/doc/iptrtpproxy.xml

@@ -27,8 +27,8 @@
 	</copyright>
 	</copyright>
 	<revhistory>
 	<revhistory>
 	    <revision>
 	    <revision>
-		<revnumber>$Revision$</revnumber>
-		<date>$Date$</date>
+		<revnumber>$Revision: 1.3 $</revnumber>
+		<date>$Date: 2009/08/25 19:30:15 $</date>
 	    </revision>
 	    </revision>
 	</revhistory>
 	</revhistory>
 
 
@@ -41,17 +41,18 @@
 	<para>
 	<para>
 	It provides similar functionality as <emphasis>nathelper</emphasis> but
 	It provides similar functionality as <emphasis>nathelper</emphasis> but
 	communicates with <emphasis>netfilter</emphasis> kernel <emphasis>xt_RTPPROXY</emphasis> module using
 	communicates with <emphasis>netfilter</emphasis> kernel <emphasis>xt_RTPPROXY</emphasis> module using
-	<emphasis>libipt_RTPPROXY</emphasis> userspace library.
-	All RTP streams are
-	manipulated directly in kernel space, no data is copied from
-	kernel to userspace and back, it reduces load and delay.
+	<emphasis>libipt_RTPPROXY</emphasis> userspace library. 
+	See <ulink url="http://www.2p.cz/en/netfilter_rtp_proxy">http://www.2p.cz/en/netfilter_rtp_proxy</ulink>
+	All RTP streams are 
+	manipulated directly in kernel space, no data is copied from 
+	kernel to userspace and back, it reduces load and delay. 
 	See <ulink url="http://www.2p.cz/en/netfilter_rtp_proxy">
 	See <ulink url="http://www.2p.cz/en/netfilter_rtp_proxy">
 	http://www.2p.cz/en/netfilter_rtp_proxy</ulink> for more details.
 	http://www.2p.cz/en/netfilter_rtp_proxy</ulink> for more details.
 	</para>
 	</para>
 
 
 	<para>
 	<para>
 	The ser module is written as light-weighted, there is not implemented
 	The ser module is written as light-weighted, there is not implemented
-	any dialog managment as in <emphasis>nathelper</emphasis>, the reason is that such API
+	any dialog managment as in <emphasis>nathelper</emphasis>, the reason is that such an API
 	should be provided by core or specialized dialog manager module.
 	should be provided by core or specialized dialog manager module.
 	Because such module is not in CVS, session information may be stored 
 	Because such module is not in CVS, session information may be stored 
 	in extra attributes of <emphasis>avp_db</emphasis> module and
 	in extra attributes of <emphasis>avp_db</emphasis> module and
@@ -67,6 +68,12 @@
 	<emphasis>Nathelper</emphasis> may be still used for testing if client is behind the NAT.
 	<emphasis>Nathelper</emphasis> may be still used for testing if client is behind the NAT.
 	</para>
 	</para>
 
 
+	<para>
+	There is also support for media authorization. Number of codec sets may be defined. When a message containing 
+	SDP offer/answer is being processed then current codecs and streams may be inspected, removed or signallized
+	according a codec set.
+	</para>
+
 
 
 	<para>
 	<para>
 	Limitations:
 	Limitations:
@@ -94,7 +101,7 @@
 	<itemizedlist>
 	<itemizedlist>
 		<listitem>
 		<listitem>
 		<para>
 		<para>
-		netfilter xt_RTPROXY &amp; libipt_RTPPROXY,
+		netfilter xt_RTPPROXY &amp; libipt_RTPPROXY,
 	    see <ulink url="http://www.2p.cz/en/netfilter_rtp_proxy">http://www.2p.cz/en/netfilter_rtp_proxy</ulink>
 	    see <ulink url="http://www.2p.cz/en/netfilter_rtp_proxy">http://www.2p.cz/en/netfilter_rtp_proxy</ulink>
 		</para>
 		</para>
 		</listitem>
 		</listitem>
@@ -118,7 +125,8 @@
 		<title><varname>config</varname> (string)</title>
 		<title><varname>config</varname> (string)</title>
 		<para>
 		<para>
   References <emphasis>iptrtpproxy.cfg</emphasis>, see <emphasis>iptrtpproxy_helper</emphasis>. Default value
   References <emphasis>iptrtpproxy.cfg</emphasis>, see <emphasis>iptrtpproxy_helper</emphasis>. Default value
-		is <emphasis>/etc/iptrtpproxy.cfg</emphasis>.
+		is <emphasis>/etc/iptrtpproxy.cfg</emphasis>. If only codec authorization is to be used then 
+		<emphasis>/dev/null</emphasis> may be used.
 		</para>
 		</para>
 		</section>
 		</section>
 
 
@@ -131,28 +139,110 @@
 		The format is:
 		The format is:
 		</para>
 		</para>
 			<programlisting>
 			<programlisting>
-  name "=" value * ( ";" name "=" value )
+  "name=" value * ( ";" name "=" value )
 
 
-  name =  "name" | "*" | ( "ringing-timeout " ) | ( ( "learning-timeout-" | "always-learn-") ("a" | "b") )
+  name =  "aggregation" | "sip-addr-" 
 			</programlisting>
 			</programlisting>
 
 
 		<para>
 		<para>
-  The meaning of parameters is described in <emphasis>libipt_RTPROXY</emphasis> and <emphasis>iptrtpproxy</emphasis> documentation.
-  <emphasis>ringing timeout</emphasis> is explicit timeout in sec regarding ringing. Ringing requires manual callee action, i.e.
-  it may takes long time. Default value is 60 sec.
+  The <emphasis>name</emphasis> is the switchboard name as declared in config and will be used by script functions and references switchboard. 
+  It's mandatory parameter. The special name <emphasis>*</emphasis> set values for all switchboards. 
+		</para>
+		<para>
+  The <emphasis>sip-addr</emphasis> is address used by <function>iptrtpproxy_ser_param(by_sip_ip)</function> to find a switchboard for particular
+  connection. If not explicitly configured then RTP switchboard gate address are used for this feature. 
 		</para>
 		</para>
 		<para>
 		<para>
-  The <emphasis>name</emphasis>" is identifier that will be used by script functions and references switchboard. 
-  It's mandatory parameter. More switchboards may be declared. The special name <emphasis>*</emphasis> set values
-  for all switchboards.
+The <emphasis>aggregation</emphasis> enables to aggregate more switchboards in cluster and to widden bandwidth.
+Aggregation will take <emphasis>sip-addr</emphasis> from the first switchboard of its. 
 		</para>
 		</para>
 		<example>
 		<example>
 			<title>Declare <varname>switchboard</varname></title>
 			<title>Declare <varname>switchboard</varname></title>
 			<programlisting>
 			<programlisting>
 	...
 	...
 	modparam("iptrtpproxy", "config", "/etc/iptrtpproxy.cfg");
 	modparam("iptrtpproxy", "config", "/etc/iptrtpproxy.cfg");
-	modparam("iptrtpproxy", "switchboard", "name=*;learning-timeout-a=10;learning-timeout-b=10;ringing-timeout=90");
-	modparam("iptrtpproxy", "switchboard", "name=my;ringing-timeout=60");
+	modparam("iptrtpproxy", "switchboard", "name=my1;sip-addr-a=1.2.3.4;sip-addr-b=5.6.7.8");
+	modparam("iptrtpproxy", "switchboard", "name=my2;sip-addr-a=2.3.4.5;sip-addr-b=3.4.5.6;aggregation=my23");
+	modparam("iptrtpproxy", "switchboard", "name=my3;aggregation=my23");
+	modparam("iptrtpproxy", "switchboard", "name=*;aggregation=my123");
+	...
+			</programlisting>
+		</example>
+		</section>
+
+		<section id="rpc_heartbeat_timeout">
+		<title><varname>rpc_heartbeat_timeout</varname> (int)</title>
+		<para>
+		Timeout in seconds used for rerequest remote RTP proxy via RPC command after preceeding error.
+		In other words if a RPC server is unresponsive at the moment then next attempt will be forced
+		after this timeout. Default value is <emphasis>30</emphasis>.
+		</para>
+		</section>
+
+		<section id="hostname">
+		<title><varname>hostname</varname> (string)</title>
+		<para>
+		The hostname used by RPC to identify machine where Ser is running to communicate which RTP proxy
+		via local interface. Default value is taken from system hostname.
+		</para>
+		</section>
+
+		<section id="declare_codec">
+		<title><varname>declare_codec</varname> (string)</title>
+		<para>
+		There are basic implicit codecs compiled in module, more codecs may be added by this parameter (one codec per modparam).
+		</para>
+		</section>
+
+		<section id="codec_set">
+		<title><varname>codec_set</varname> (string)</title>
+		<para>
+		Declares new codec set. Codecs are declared for each media type independently. 
+		</para>
+		<para>
+		The format is:
+		</para>
+			<programlisting>
+  "name=" value * ( ";" name "=" value )
+
+  name =  "media_type" | "rights" | "codecs" | "max_streams" | ( "rtp" | "rtcp" ) "_" ( "bytes" | "packets" )
+
+  media_types = "audio" | "video" | "application" | "text" | "message" | "data" | "control" | "?" | "*"
+			</programlisting>		
+
+		<para>
+  The <emphasis>name</emphasis> is the codec set name to be defined.
+		</para>
+		<para>
+  The <emphasis>media_type</emphasis> belongs to type at <emphasis>m=</emphasis> SDP line. Question mark means
+  "unknown media" type and asterisk "all media types".
+		</para>
+		<para>
+  The <emphasis>max_streams</emphasis> defines how many streams (m= lines) is allowed per media type.
+		</para>
+		<para>
+  The <emphasis>rights</emphasis> defines if particular codec is allowed <emphasis>0</emphasis>, disallowed, i.e. will be removed
+  if bit AND operation with <function>remove_codec_mask</function> is non-zero or its presence will be signallized by <function>@iptrtpproxy.auth_rights</function> (any other value).
+		</para>
+		<para>
+  The <emphasis>codecs</emphasis> comma separated list of codecs. Previous <emphasis>media_type&amp;rights</emphasis> will be applied.
+		</para>
+		<para>
+  The <emphasis>rtp/rtcp_bytes/packets</emphasis> limits bandwidth per <emphasis>media_type</emphasis> (0 is unlimited). It will override
+  bandwidth limited by <function>iptrtpproxy_set_param("throttle_*")</function>.
+		</para>
+		<example>
+			<title>Declare <varname>codec_set</varname></title>
+			<programlisting>
+	...
+	# enable all codecs, default state when codec is declared
+	modparam("iptrtpproxy", "codec_set", "name=cs1;media_type=*;max_streams=9999;rights=0;codecs=*");
+	# allow only 2 audio and 1 video stream
+	modparam("iptrtpproxy", "codec_set", "name=cs2;media_type=*;max_streams=0;media_type=audio;max_streams=2;media_type=video;max_streams=1");
+	# dtto, allow only a few audio and video codecs, GSM codec is allowed but signallized
+	modparam("iptrtpproxy", "codec_set", "name=cs3;media_type=*;max_streams=0;rights=1;codecs=*;media_type=audio;max_streams=2;rights=0;codecs=PCMU,G729,G728,parityfec,telephone-events;rights=2;codecs=GSM;media_type=video;max_streams=1;rights=0;codecs=jpeg,parityfec");
+	# limit max. bandwidth for video¨	
+	modparam("iptrtpproxy", "codec_set", "name=cs4;media_type=video;rtp_bytes=10000;rtcp_bytes=1000");
 	...
 	...
 			</programlisting>
 			</programlisting>
 		</example>
 		</example>
@@ -165,27 +255,43 @@
 
 
 		<section id="iptrtpproxy_alloc">
 		<section id="iptrtpproxy_alloc">
 		<title>
 		<title>
-			<function>iptrtpproxy_alloc(gate_a_to_b, switchboard_id)</function>
+			<function>iptrtpproxy_alloc(gate_a_to_b [, existing_sess_ids])</function>
 		</title>
 		</title>
 		<para>
 		<para>
 			Parses SDP content and allocates for each RTP media stream one RTP proxy session.
 			Parses SDP content and allocates for each RTP media stream one RTP proxy session.
-			SDP is updates to reflect allocated sessions.
+			SDP is updates to reflect allocated sessions. Switchboard/aggregation is set using
+			<function>iptrtpproxy_set_param(by_sip_ip)</function> or <function>iptrtpproxy_set_param("switchboard/aggregation")</function>.
+		</para>
+		<para>
+			Aggregation supports load balancing among more RTP proxies controlled by RPC.
+			The module try to allocate at machines/switchboards in following order (priorities)
+			not yet asked (or being heartbeated) machines, responsive machines, switchboards
+			having percentualy more free slots, non responsive machines.
+		</para>
+		<para>
+			Proxy may hide caller identity provided at <emphasis>o=</emphasis> line using
+			<function>@iptrtpproxy.o_name/addr</function> and <function>iptrtpproxy_set_param(o_name/addr)</function>
+			functions. But the script is responsible for rewritting to original values in
+			a response or a callee initiated re-INVITE. Therefore original value need to be stored
+			in-dialog.
 		</para>
 		</para>
 		<itemizedlist>
 		<itemizedlist>
 			<listitem>
 			<listitem>
 			<para>
 			<para>
 				if <emphasis>gate_a_to_b</emphasis> bit 0 is set 
 				if <emphasis>gate_a_to_b</emphasis> bit 0 is set 
 				then SDP regards to gate-a to gate-b direction.
 				then SDP regards to gate-a to gate-b direction.
-				If bit 1 is set then <emphasis>ringing timeout</emphasis>
-				is used instead of <emphasis>learning timeout</emphasis> for
-				particular gate timeout.
 			</para>
 			</para>
 			</listitem>
 			</listitem>
 			<listitem>
 			<listitem>
 			<para>
 			<para>
-				<emphasis>switchboard_id</emphasis> is reference to a switchboard with name declared as
-				<varname>switchboard</varname> modparam. If empty then use switchboard found by
-				<function>iptrtpproxy_find</function> (equal using <function>@iptrtpproxy.switchboard</function>).
+				<emphasis>protected_session_ids</emphasis> list of existing sessions
+				enables reusing already allocated sessions in re-INVITE without 
+				allocating new sessions for each stream in SDP regardless a IP/port
+				is required. It's mostly undesirable, typically "hold-on" is
+				done via re-INVITE without any change. There is drawback because
+				callee cannot change IP:port in 200OK which is legal case in RFC3264. 
+				But because some non-RFC3264 compliant phones dislike proactively 
+				changed IP:port at RTP proxy it seems it's less evil.
 			</para>
 			</para>
 			</listitem>
 			</listitem>
 			<listitem>
 			<listitem>
@@ -199,10 +305,36 @@
 			<title><function>iptrtpproxy_alloc</function> usage</title>
 			<title><function>iptrtpproxy_alloc</function> usage</title>
 			<programlisting>
 			<programlisting>
 	...
 	...
-	if (iptrtpproxy_alloc("1", "my")) {
-	  $sess_ids = @iptrtpproxy.session_ids;
-	  # save sess_ids in dialog
+	if (!iptrtpproxy_set_param("aggregation_by_sip_ip_a", "@received.ip")) {
+		if (!iptrtpproxy_set_param("switchboard_by_sip_ip_a", "@received.ip")) {
+			t_reply("500", "RTP proxy error");
+			drop;
+		}
+	}
+	eval_push("x:%@next_hop.src_ip");
+	if (@eval.get[-1] == @received.ip) {
+		if (@iptrtpproxy.aggregation_a) {
+			iptrtpproxy_set_param("aggregation_b", "@iptrtpproxy.aggregation_a");
+		}
+		else {
+			iptrtpproxy_set_param("switchboard_b", "@iptrtpproxy.switchboard_a");
+		}
+	}
+	else {
+		if (!iptrtpproxy_set_param("aggregation_by_sip_ip_b", "@eval.get[-1]")) {
+			if (!iptrtpproxy_set_param("switchboard_by_sip_ip_b", "@eval.get[-1]")) {
+				t_reply("500", "RTP proxy error");
+				drop;
+			}
+		}
 	}
 	}
+	eval_remove(-1, 1);
+	
+	if (!iptrtpproxy_alloc("1")) {
+		t_reply("500", "RTP proxy error");
+		drop;
+	}
+	$sess_ids = @iptrtpproxy.session_ids;
 	...
 	...
 			</programlisting>
 			</programlisting>
 		</example>
 		</example>
@@ -218,14 +350,22 @@
 		stream has port zero particular session is released), the
 		stream has port zero particular session is released), the
 		result of <function>@iptrtpproxy.session_ids</function> should be stored for future in-dialog usage.
 		result of <function>@iptrtpproxy.session_ids</function> should be stored for future in-dialog usage.
 		</para>
 		</para>
+		<para>
+			The SDP contect is also affected by <function>iptrtpproxy_set_param(o_name/addr)</function>
+			functions. If a stream is deactivated in SDP then Sessions may be deleted unless 
+			mentioned in <emphasis>protected_session_ids</emphasis>.
+		</para>
 		<itemizedlist>
 		<itemizedlist>
 			<listitem>
 			<listitem>
 			<para>
 			<para>
 				if <emphasis>gate_a_to_b</emphasis> bit 0 is set 
 				if <emphasis>gate_a_to_b</emphasis> bit 0 is set 
 				then SDP regards to gate-a to gate-b direction.
 				then SDP regards to gate-a to gate-b direction.
-				If bit 1 is set then <emphasis>ringing timeout</emphasis>
-				is used instead of <emphasis>learning timeout</emphasis> for
-				particular gate timeout.
+			</para>
+			<para>
+				if <emphasis>gate_a_to_b</emphasis> bit 1 is set 
+				then SDP is updated only, no RTP session are affected.
+				Should be used when handling retransmission in onreply route,
+				retransmission replies are not eaten be tm module!
 			</para>
 			</para>
 			</listitem>
 			</listitem>
 		</itemizedlist>
 		</itemizedlist>
@@ -256,9 +396,6 @@
 			<para>
 			<para>
 				if <emphasis>gate_a_to_b</emphasis> bit 0 is set 
 				if <emphasis>gate_a_to_b</emphasis> bit 0 is set 
 				then it regards to gate-a to gate-b direction.
 				then it regards to gate-a to gate-b direction.
-				If bit 1 is set then <emphasis>ringing timeout</emphasis>
-				is used instead of <emphasis>learning timeout</emphasis> for
-				particular gate timeout.
 			</para>
 			</para>
 			</listitem>
 			</listitem>
 		</itemizedlist>
 		</itemizedlist>
@@ -267,6 +404,12 @@
 			<programlisting>
 			<programlisting>
 	...
 	...
 	# load $sess_ids from dialog
 	# load $sess_ids from dialog
+	if (status=~"18[0-9]") {
+		iptrtpproxy_set_param("learning_timeout", "60");
+	}
+	else {
+		iptrtpproxy_set_param("learning_timeout", "5");
+	}
 	if (iptrtpproxy_adjust_timeout("0", $sess_ids)) {
 	if (iptrtpproxy_adjust_timeout("0", $sess_ids)) {
 	}
 	}
 	...
 	...
@@ -279,7 +422,8 @@
 		</title>
 		</title>
 		<para>
 		<para>
 		Delete sessions identified by <emphasis>session_ids</emphasis>. May be used when dialog is being
 		Delete sessions identified by <emphasis>session_ids</emphasis>. May be used when dialog is being
-		destroyed (BYE) or when INVITE failed in failure route.
+		destroyed (BYE) or when INVITE failed in failure route. If <emphasis>protected_session_ids</emphasis>
+		list is provided then this set is excluded from sessions to be deleted.
 		</para>
 		</para>
 		<example>
 		<example>
 			<title><function>iptrtpproxy_delete</function> usage</title>
 			<title><function>iptrtpproxy_delete</function> usage</title>
@@ -292,39 +436,108 @@
 		</example>
 		</example>
 		</section>
 		</section>
 
 
-		<section id="iptrtpproxy_find">
+		<section id="iptrtpproxy_authorize_media">
+		<title>
+			<function>iptrtpproxy_authorize_media()</function>
+		</title>
+		<para>
+		Authorizes SDP media according currect <emphasis>codec_set</emphasis>. If bit AND operation 
+		between rights in codec set and <function>remove_codec_mask</function> is non zero then
+		such a codec are to be removed. The result may be obtained from
+		<function>@iptrtpproxy.auth_rights</function> which returns max. right which has been applied when
+		processing all codecs of enabled streams.
+		</para>
+		<para>
+		The function MUST NOT be called after <function>iptrtpproxy_alloc/update</function>! 
+		But the function may be called several times to authorize using more codec sets.
+		</para>
+		<example>
+			<title><function>iptrtpproxy_authorize_media</function> usage</title>
+			<programlisting>
+	...
+	if (@iptrtpproxy.active_media_num == "0") break;
+	iptrtpproxy_set_param("codec_set", "cs2");
+	iptrtpproxy_set_param("remove_codec_mask", "1");
+	if (!iptrtpproxy_authorize_media()) {
+		t_reply("415", "Cannot authorize media");
+		drop;
+	}
+	iptrtpproxy_set_param("codec_set", "cs3");
+	if (!iptrtpproxy_authorize_media()) {
+		t_reply("415", "Cannot authorize media");
+		drop;
+	}
+	if (@iptrtpproxy.active_media_num == "0") {
+		t_reply("488", "Not acceptable here");
+		drop;
+	}
+	if (@iptrtpproxy.auth_rights == "2") {
+		append_hf_value("Contact: &lt;sip:1.2.3.4&gt;");
+		t_reply("301", "Redirect to transcoder");
+		drop;
+
+	}
+	...
+			</programlisting>
+		</example>
+		</section>
+
+		<section id="iptrtpproxy_set_param">
 		<title>
 		<title>
-			<function>iptrtpproxy_find(gate_a, gate_b)</function>
+			<function>iptrtpproxy_set_param(param, value)</function>
 		</title>
 		</title>
 		<para>
 		<para>
-			Find corresponding switchboard and set <function>@iptrtpproxy.switchboard</function> 
-			and <function>@iptrtpproxy.direction</function>.
+			Set particular parameter needed mainly by <function>iptrtpproxy_alloc/update/adjust_timeout</function>.
+			The paramter value is availble via <function>@iptrtpproxy.&lt;param&gt;</function>.
 		</para>
 		</para>
 		<itemizedlist>
 		<itemizedlist>
 			<listitem>
 			<listitem>
 			<para>
 			<para>
-				if <emphasis>gate_a/b</emphasis> switchboard identification
+				Supported parameters: <emphasis>expiration_timeout</emphasis>, <emphasis>ttl</emphasis>, <emphasis>learning_timeout</emphasis>, 
+				<emphasis>always_learn</emphasis>,
+				<emphasis>aggregation_a</emphasis>, <emphasis>aggregation_b</emphasis>, 
+				<emphasis>switchboard_a</emphasis>, <emphasis>switchboard_b</emphasis>,
+				<emphasis>throttle_mark</emphasis>,
+				<emphasis>throttle_rtp_max_bytes</emphasis>, <emphasis>throttle_rtp_max_packets</emphasis>, 
+				<emphasis>throttle_rtcp_max_bytes</emphasis>, <emphasis>throttle_rtcp_max_packets</emphasis>.
 			</para>
 			</para>
 			</listitem>
 			</listitem>
+		</itemizedlist>
+		</section>
+
+		<section id="iptrtpproxy_set_param(by_sip_ip)">
+		<title>
+			<function>iptrtpproxy_set_param("(aggregation/switchboard)_by_sip_ip_(a/b)", sip_ip)</function>
+		</title>
+		<para>
+			Find corresponding aggregation or switchboard and set 
+			<function>@iptrtpproxy.aggregation_a/b</function> or <function>@iptrtpproxy.switchboard_a/b</function>.
+			Switchboards/aggregations are compared against <emphasis>sip-addr</emphasis>, 
+			it allow separate SIP and RTP traffic and RTP aggregation.
+		</para>
+		<itemizedlist>
 			<listitem>
 			<listitem>
 			<para>
 			<para>
-				function returns true if switch was found
+				<emphasis>sip_ip</emphasis> IP to be compared, typically <function>@received.ip</function>
+				or <function>@next_hop.src_ip</function>.
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+				function returns true if switchboard/aggregation was found
 			</para>
 			</para>
 			</listitem>
 			</listitem>
 		</itemizedlist>
 		</itemizedlist>
-		<example>
-			<title><function>iptrtpproxy_find</function> usage</title>
-			<programlisting>
-	...
-	if (iptrtpproxy_find("@received.ip", "@next_hop.src_ip")) {
-		if (iptrtpproxy_alloc("1", "")) {
-			  $sess_ids = @iptrtpproxy.session_ids;
+		</section>
 
 
-		}
-	}
-	...
-			</programlisting>
-		</example>
+		<section id="iptrtpproxy_set_param(protected_session_ids)">
+		<title>
+			<function>iptrtpproxy_set_param("protected_session_ids", sess_ids)</function>
+		</title>
+		<para>
+			Used for reusing sessions in <function>iptrtpproxy_alloc</function>,  <function>iptrtpproxy_update</function>
+			and <function>iptrtpproxy_delete</function>.
+		</para>
 		</section>
 		</section>
 
 
 		<section id="iptrtpproxy.session_ids">
 		<section id="iptrtpproxy.session_ids">
@@ -341,8 +554,9 @@
 
 
 		<programlisting>
 		<programlisting>
 
 
-	switchboard_name [ ":" [ session_id *( "," session_id) ] ]
-	session_id = *( [0-9] )   ; empty when no session allocated
+	switchboard_name [ ":" [ session_id "/" created ] * ( "," session_id "/" created ) ] ]
+	session_id = * ( [0-9] )   ; empty when no session allocated
+	created = timestamp
 		</programlisting>
 		</programlisting>
 
 
 		</section>
 		</section>
@@ -352,27 +566,87 @@
 			<function>@iptrtpproxy.sdp_ip</function>
 			<function>@iptrtpproxy.sdp_ip</function>
 		</title>
 		</title>
 		<para>
 		<para>
-		Return first rewritten IP provided in SDP <emphasis>c=</emphasis> line.
+		Return first rewritten IP provided at SDP <emphasis>c=</emphasis> line.
 		</para>
 		</para>
 		</section>
 		</section>
 
 
-		<section id="iptrtpproxy.switchboard">
+		<section id="iptrtpproxy.o_name">
 		<title>
 		<title>
-			<function>@iptrtpproxy.switchboard</function>
+			<function>@iptrtpproxy.o_name</function>
 		</title>
 		</title>
 		<para>
 		<para>
-		Switchboard found by <function>iptrtpproxy_find</function>.
+		Return username from original <emphasis>o=</emphasis> line.
 		</para>
 		</para>
 		</section>
 		</section>
 
 
-		<section id="iptrtpproxy.direction">
+		<section id="iptrtpproxy_set_param(o_name)">
 		<title>
 		<title>
-			<function>@iptrtpproxy.direction</function>
+			<function>iptrtpproxy_set_param("o_name", value)</function>
 		</title>
 		</title>
 		<para>
 		<para>
-		Direction determined by <function>iptrtpproxy_find</function>. 1..gate A->B, 0..gate B->A
+		Username to be rewritten at <emphasis>o=</emphasis> line by <function>iptrtpproxy_alloc/update</function> 
+		to hide caller identity. If value is blank then username is left unchanged.
 		</para>
 		</para>
 		</section>
 		</section>
+
+		<section id="iptrtpproxy.o_addr">
+		<title>
+			<function>@iptrtpproxy.o_addr</function>
+		</title>
+		<para>
+		Return address from original <emphasis>o=</emphasis> line.
+		</para>
+		</section>
+
+		<section id="iptrtpproxy_set_param(o_addr)">
+		<title>
+			<function>iptrtpproxy_set_param("o_addr", value)</function>
+		</title>
+		<para>
+		Address to be rewritten at <emphasis>o=</emphasis> line by <function>iptrtpproxy_alloc/update</function> 
+		to hide caller identity. If value is blank then address is left unchanged.
+		</para>
+		</section>
+
+		<section id="iptrtpproxy_set_param(codec_set)">
+		<title>
+			<function>iptrtpproxy_set_param("codec_set", value)</function>
+		</title>
+		<para>
+		Codec set for <function>iptrtpproxy_authorize_media</function>. Current codec set
+		may be obtained by <function>@iptrtpproxy.codec_set</function>.
+		</para>
+		</section>
+
+		<section id="iptrtpproxy_set_param(remove_codec_mask)">
+		<title>
+			<function>iptrtpproxy_set_param("remove_codec_mask", value)</function>
+		</title>
+		<para>
+		Mask used in <function>iptrtpproxy_authorize_media</function>. Current mask 
+		may be obtained by <function>@iptrtpproxy.remove_codec_mask</function>.
+		</para>
+		</section>
+
+		<section id="iptrtpproxy.auth_rights">
+		<title>
+			<function>@iptrtpproxy.auth_rights</function>
+		</title>
+		<para>
+		Result of <function>iptrtpproxy_authorize_media</function>.
+		</para>
+		</section>
+
+		<section id="iptrtpproxy.active_media_num">
+		<title>
+			<function>@iptrtpproxy.active_media_num</function>
+		</title>
+		<para>
+		Returns number of active media streams in SDP. <function>iptrtpproxy_authorize_media</function>
+		may disable some streams, i.e. returned value may change after authorization.
+		</para>
+		</section>
+
 	</section>
 	</section>
 
 
 </section>
 </section>

+ 0 - 225
modules/iptrtpproxy/examples/ser.cfg

@@ -1,225 +0,0 @@
-# $Id$
-#
-# Simple application level gateway config script.
-#
-#
-
-# ------------------ module loading ----------------------------------
-
-loadmodule "./modules/mysql/mysql.so"
-
-loadmodule "./modules/iptrtpproxy/iptrtpproxy.so"
-loadmodule "./modules/maxfwd/maxfwd.so"
-loadmodule "./modules/rr/rr.so"
-loadmodule "./modules/tm/tm.so"
-loadmodule "./modules/sl/sl.so"
-loadmodule "./modules/avp_db/avp_db.so"
-loadmodule "./modules/usrloc/usrloc.so"
-loadmodule "./modules/nathelper/nathelper.so"
-loadmodule "./modules/registrar/registrar.so"
-
-flags
-	FLAG_NAT_REG,
-	FLAG_NAT;
-
-
-avpflags
-	rtpproxy_dlg,
-	dialog_cookie;
-
-# ----------------- setting module-specific parameters ---------------
-
-modparam("avp_db|usrloc", "db_url", "mysql://ser:[email protected]/ser");
-
-modparam("usrloc", "db_mode", 1)
-
-modparam("rr", "cookie_secret", "MyRRAVPcookiesecret");
-
-modparam("nathelper", "rtpproxy_disable", 1);
-
-modparam("iptrtpproxy", "config", "/etc/iptrtpproxy.cfg");
-modparam("iptrtpproxy", "switchboard", "name=*;learning-timeout-a=10;learning-timeout-b=10;ringing-timeout=90");
-
-modparam("registrar", "save_nat_flag", "FLAG_NAT_REG");
-modparam("registrar", "load_nat_flag", "FLAG_NAT_REG");
-
-modparam("avp_db", "attr_group", "id=rtp_dlg,flag=rtpproxy_dlg,table=rtpproxy_attrs");
-
-# ------------------ main fun below ----------------------------------
-
-route {
-
-	if (!mf_process_maxfwd_header("10")) {
-		sl_send_reply("483", "Too Many Hops");
-		break;
-	};
-	if (msg:len > max_len) {
-		sl_send_reply("513", "Message too big");
-		break;
-	};
-#	if (method=="INVITE") {
-#		if (!(uri == myself)) {
-#			sl_send_reply("403", "Call cannot be served here");
-#			break;
-#		}
-#	};
-
-	if (!t_newtran()) {
-		sl_send_reply("500", "Internal error newtran");
-		break;
-	};
-	# Do strict routing if pre-loaded route headers present
-	if (loose_route()) {
-		if ($dialog_id) {  # AVP loaded from cookie
-			if (method == "INVITE" || method == "UPDATE") {   # TODO: check if there is a SDP
-				# re-INVITEs
-				if (@route[0].ftag == @from.tag) {
-					$gate_a_to_b = 1;
-					if ($init_dialog)
-						$tmp = 3;
-					else
-						$tmp = 1;
-				}
-				else {
-					$gate_a_to_b = 0;
-					if ($init_dialog)
-						$tmp = 2;
-					else
-						$tmp = 0;
-				}
-				resetflag("FLAG_NAT");
-				# test if caller is behind NAT 
-				if (nat_uac_test("18")) {
-					setflag("FLAG_NAT");
-				}
-				# is callee behind NAT? we know it from registration
-				if (lookup("location")) {
-					if (isflagset("FLAG_NAT_REG"))
-						setflag("FLAG_NAT");
-				}
-				else {
-					sl_reply("404", "Unknown user in To");
-					break;
-				}
-
-				if ($sess_ids) {  # pending non-confirmed sessions?
-					iptrtpproxy_delete("$sess_ids");
-					$sess_ids = "";
-				}
-				if (isflagset("FLAG_NAT")) {					
-					if (!iptrtpproxy_alloc("$tmp", "my")) {
-						sl_reply("500", "RTP proxy error");
-						break;
-					}
-					$sess_ids = @iptrtpproxy.session_ids;
-					t_on_reply("invite_response");
-					t_on_failure("invite_failure");
-				}
-			}
-			else if (method == "BYE") {   # CANCEL ?
-				t_on_reply("bye_response");
-			}
-		}
-		t_relay();
-		break;
-	};
-
-	if (method=="REGISTER") {
-
-		if (nat_uac_test("18")) {
-			setflag("FLAG_NAT_REG");
-		}
-
-		if (!save_contacts("location")) {
-			sl_reply("400", "Invalid REGISTER Request");
-			break;
-		}
-	
-		break;
-	}
-
-	if (method=="INVITE" && @to.tag=="") {
-		$dialog_id = @sys.unique;
-		$init_dialog = 1;
-
-		# test if caller is behind NAT 
-		if (nat_uac_test("18")) {
-			setflag("FLAG_NAT");
-		}
-		
-		# is callee behind NAT? we know it from registration
-		if (lookup("location")) {
-			if (isflagset("FLAG_NAT_REG"))
-				setflag("FLAG_NAT");
-		}
-		else {
-			sl_reply("404", "Unknown user in To");
-			break;
-		}
-		# test if call comming at gate a or b by ip, port etc.
-		$gate_a_to_b = 1;  # TODO: we do not consider two gates now, a->b & ringing-timeout
-
-		if (isflagset("FLAG_NAT")) {
-			if (iptrtpproxy_alloc("3", "my")) {
-				$sess_ids = @iptrtpproxy.session_ids;
-				t_on_reply("invite_response");
-				t_on_failure("invite_failure");
-			}
-		}
-
-		setavpflag("$f.dialog_id", "dialog_cookie");
-		record_route();  # it will add ftag to record-route, we can check direction 
-	}
-
-	if (!t_relay()) {
-		sl_reply_error();
-	}
-
-}
-
-onreply_route["invite_response"] {
-	if (status=~"18[0-9]" || status=~"2[0-9][0-9]") {
-		if ($gate_a_to_b == "1") {
-			if ($init_dialog && status=~"18[0-9]")
-				$tmp = 2;  # ringing-timeout
-			else
-				$tmp = 0;
-		}
-		else {
-			if ($init_dialog && status=~"18[0-9]")
-				$tmp = 3;  # ringing-timeout
-			else
-				$tmp = 1;
-		}
-		if (iptrtpproxy_update("$tmp", "$sess_ids")) {
-			if (status=~"2[0-9][0-9]") {
-				if ($init_dialog) {
-					iptrtpproxy_adjust_timeout("$gate_a_to_b", "$sess_ids");  # decrease ringing-timeout
-				}
-				# delete old session, e.g. in case of re-INVITE
-				load_extra_attrs("rtp_dlg", "$dialog_id");
-				iptrtpproxy_delete("$dlg_sess_ids");
-				# save session ids
-				$dlg_sess_ids = @iptrtpproxy.session_ids;
-				setavpflag("$f.dlg_sess_ids", "rtpproxy_dlg");
-				save_extra_attrs("rtp_dlg", "$dialog_id");
-			}
-		}
-	}
-}
-
-failure_route["invite_failure"] {
-	if ($sess_ids) {
-		iptrtpproxy_delete("$sess_ids");
-		$sess_ids = "";	
-	}
-}
-
-onreply_route["bye_response"] {
-	if (status=~"2[0-9][0-9]") {
-		load_extra_attrs("rtp_dlg", "$dialog_id");
-		iptrtpproxy_delete("$dlg_sess_ids");
-		remove_extra_attrs("rtp_dlg", "$dialog_id");
-	}
-}
-

File diff suppressed because it is too large
+ 1153 - 217
modules/iptrtpproxy/iptrtpproxy.c


+ 110 - 17
modules/iptrtpproxy/examples/clean_rtpproxy_attrs → modules/iptrtpproxy/oob/clean_rtpproxy_attrs

@@ -1,16 +1,16 @@
 #!/usr/bin/env perl
 #!/usr/bin/env perl
 
 
-# $Id$
+# $Id: clean_rtpproxy_attrs,v 1.3 2009/07/15 23:02:15 tma0 Exp $
 
 
 # The script cleans up orphaned RTP proxy attributes from rtpproxy_attrs.
 # The script cleans up orphaned RTP proxy attributes from rtpproxy_attrs.
 # The orphaned attribute is the attribute having none of its sessions.
 # The orphaned attribute is the attribute having none of its sessions.
 # in iptrtpproxy session list.
 # in iptrtpproxy session list.
 
 
 use warnings;
 use warnings;
-#use strict;
+use strict;
 use DBI;
 use DBI;
 
 
-my $version = "0.1";
+my $version = "0.2";
 my $defConfigFile = "/etc/iptrtpproxy.cfg";
 my $defConfigFile = "/etc/iptrtpproxy.cfg";
 my $configFile = $defConfigFile;
 my $configFile = $defConfigFile;
 my $verbose = 0;
 my $verbose = 0;
@@ -63,12 +63,14 @@ sub parseConfigFile($)
 	my $cfg = Config::IniFiles->new( -file => $configFile, -default => "<default>" );
 	my $cfg = Config::IniFiles->new( -file => $configFile, -default => "<default>" );
 
 
 	for my $section ($cfg->Sections) {
 	for my $section ($cfg->Sections) {
-		if ($section =~ /^[a-zA-Z_]+$/) {
-			print STDERR "Parsing section '$section'\n" if $verbose;
-			for my $param ('addr-a', 'addr-b', 'port-a', 'port-b') {
+		if ($section =~ /^switchboard:.+$/) {
+			my $name = $section;
+			$name =~ s/^switchboard://;
+			print STDERR "Parsing section '$section', name='$name'\n" if $verbose;
+			for my $param ('addr', 'port') {
 				my $val = $cfg->val($section, $param);
 				my $val = $cfg->val($section, $param);
 				if ($val) {
 				if ($val) {
-					$switchboards{$section}{$param} = $val;
+					$switchboards{$name}{$param} = $val;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -78,13 +80,63 @@ ret:
 	return %switchboards;
 	return %switchboards;
 }
 }
 
 
+my %skip;
+sub Config_Extend_MySQL__read_config {
+# copy pasted not to add another dependency
+    my ($what, $path) = @_;
+    my $content = "";
+    my $opts = {}; #{ err_mode => "quiet" };
+
+use File::Basename          qw(dirname);
+use File::Spec::Functions   qw(catfile rel2abs);
+
+    if ($what eq "file") {
+        my $base_dir = dirname($path);
+        open (MYFILE, $path);
+        while (<MYFILE>) {
+	 	$content .= $_;
+	}
+	close (MYFILE); 
+        # handle single param (without value)
+        $content =~ s{^ \s* (\w+ (?:-\w+)* ) \s* $}{$1 = yes}xgm;
+        # handle includes
+        $content =~ s{^ \s* !include(dir)? \s+ (.+) \s* $}
+                     { __read_config($1 || "file", rel2abs($2, $base_dir)) }xgme;
+    }
+    elsif ($what eq "dir") {
+        opendir(my $dirh, $path) or return "";
+
+        while (my $file = readdir($dirh)) {
+            # skip invisible files and directories we shouldn't 
+            # recurse into, like ../ or CVS/
+            next if $skip{$file} or index($file, ".") == 0;
+
+            my $filepath = catfile($path, $file);
+
+            if (-f $filepath) {
+                $content .= Config_Extend_MySQL__read_config(file => $filepath)
+            }
+            elsif (-d _) {
+                $content .= Config_Extend_MySQL__read_config(dir => $filepath)
+            }
+        }
+
+        closedir($dirh);
+    }
+
+    return $content
+}
+
+
+
 my $scriptName = $0;
 my $scriptName = $0;
 $scriptName =~ s!^.*/!!;
 $scriptName =~ s!^.*/!!;
 
 
 sub printUsage {
 sub printUsage {
 	print "$scriptName, Version: $version\n";
 	print "$scriptName, Version: $version\n";
-	print "usage: $scriptName [-f <iptrtpproxy_config>] [-v] [-h] [-H <db_host>] [-S <db_sock>] [-D <db_name>] [-u <db_user>] [-p <db_password>]\n";
+	print "usage: $scriptName [-f <iptrtpproxy_config>] [-v] [-h] [-M <mysql_conf>[-H <db_host>] [-S <db_sock>] [-D <db_name>] [-u <db_user>] [-p <db_password>]\n";
 	print "  iptrtpproxy_config .. iptrtpproxy_helper config, default: $defConfigFile\n";
 	print "  iptrtpproxy_config .. iptrtpproxy_helper config, default: $defConfigFile\n";
+	print "  mysql_conf .. database mysql.conf to get connection params\n";
 	print "  db_host .. database MYSQL  host, default: $defmysqlhost\n";
 	print "  db_host .. database MYSQL  host, default: $defmysqlhost\n";
 	print "  db_sock .. MYSQL socket, default: $defmysqlsock\n";
 	print "  db_sock .. MYSQL socket, default: $defmysqlsock\n";
 	print "  db_name .. database name, default: $defmysqldb\n";
 	print "  db_name .. database name, default: $defmysqldb\n";
@@ -109,6 +161,45 @@ while ($#ARGV >= 0) {
 		} else {
 		} else {
 			$configFile = shift(@ARGV);
 			$configFile = shift(@ARGV);
 		}
 		}
+	} elsif ($arg eq '-M') {
+		if ($#ARGV < 0) {
+			print STDERR "ERROR: mysql conf file name required\n";
+			&printUsage();
+			exit(1);
+		} else {
+			my $mysqlconf = shift(@ARGV);
+			dbg "Parsing mysql config file '$mysqlconf'\n" if $verbose;
+			if (-e $mysqlconf) {
+
+				# read the file and resolve the MySQL-isms
+				my $content = Config_Extend_MySQL__read_config('file', $mysqlconf);
+				my $fh = undef;
+				if ($] < 5.008) {
+				        require IO::String;
+					$fh = IO::String->new(\$content);
+				}
+				else {
+					open($fh, "<", \$content);
+				}
+				my $cfg = Config::IniFiles->new( -file => $fh );
+				if (!$cfg) {
+					print STDERR "ERROR: @Config::IniFiles::errors\n";
+				}
+				else {
+					my $val;
+					$val = $cfg->val('client', 'database');
+					$mysqldb = $val if $val;
+					$val = $cfg->val('client', 'user');
+					$mysqluser = $val if $val;
+					$val = $cfg->val('client', 'password');
+					$mysqlpassword = $val if $val;
+					$val = $cfg->val('client', 'socket');
+					$mysqlsock = $val if $val;
+				}
+			} else {
+				print STDERR "ERROR: mysql conf '$mysqlconf' not found\n"
+			}
+		}
 	} elsif ($arg eq '-D') {
 	} elsif ($arg eq '-D') {
 		if ($#ARGV < 0) {
 		if ($#ARGV < 0) {
 			print STDERR "ERROR: database name required\n";
 			print STDERR "ERROR: database name required\n";
@@ -163,6 +254,8 @@ while ($#ARGV >= 0) {
 
 
 dbg "Verbose: $verbose";
 dbg "Verbose: $verbose";
 
 
+my $fileName = $_[0];
+
 dbg "Connecting: mysql://$mysqluser\@$mysqlhost/$mysqldb";
 dbg "Connecting: mysql://$mysqluser\@$mysqlhost/$mysqldb";
 myexit("Cannot connect database", 2) unless my $dbh = DBI->connect("dbi:mysql:database=$mysqldb".
 myexit("Cannot connect database", 2) unless my $dbh = DBI->connect("dbi:mysql:database=$mysqldb".
 	($mysqlhost?";host=$mysqlhost":'').
 	($mysqlhost?";host=$mysqlhost":'').
@@ -172,31 +265,31 @@ myexit("Cannot connect database", 2) unless my $dbh = DBI->connect("dbi:mysql:da
 dbg "Reading rtpproxy_attrs";
 dbg "Reading rtpproxy_attrs";
 my %attrs = ();
 my %attrs = ();
 # copy rtpproxy_attrs to memory, we need snapshot of table
 # copy rtpproxy_attrs to memory, we need snapshot of table
-$sth = $dbh->prepare("SELECT * FROM rtpproxy_attrs");
+my $sth = $dbh->prepare("SELECT * FROM rtpproxy_attrs");
 $sth->execute();
 $sth->execute();
-my $i = 0;
+my $n = 0;
 while (my $href = $sth->fetchrow_hashref()) {
 while (my $href = $sth->fetchrow_hashref()) {
 
 
-	if ($href->{'name'} eq 'dlg_sess_ids') {
+	if ($href->{'name'} eq 'dlg_rtp_sess_ids') {
 		$attrs{$href->{'id'}} = $href->{'value'};
 		$attrs{$href->{'id'}} = $href->{'value'};
-		$i++;
+		$n++;
 	}
 	}
 }
 }
 $sth->finish();
 $sth->finish();
-dbg "rtpproxy_attrs records: $i";
+dbg "rtpproxy_attrs records: $n";
 
 
 my %cfgSwitchboards = &parseConfigFile($configFile);
 my %cfgSwitchboards = &parseConfigFile($configFile);
 
 
 my %switchboards = ();
 my %switchboards = ();
 for my $id (keys %cfgSwitchboards) {
 for my $id (keys %cfgSwitchboards) {
 	dbg "Switchboard: $id";
 	dbg "Switchboard: $id";
-	$cmd = "iptrtpproxy list --addr-a $cfgSwitchboards{$id}{'addr-a'} --port-a $cfgSwitchboards{$id}{'port-a'} --addr-b $cfgSwitchboards{$id}{'addr-b'} --port-b $cfgSwitchboards{$id}{'port-b'} --force-switchboard-audit --format tab";
+	my $cmd = "iptrtpproxy list --addr-a $cfgSwitchboards{$id}{'addr'} --port-a $cfgSwitchboards{$id}{'port'} --format tab --sessions";
 	my @lines = &do_exec($cmd);
 	my @lines = &do_exec($cmd);
 	my @l = split(/\t/, $lines[0]);
 	my @l = split(/\t/, $lines[0]);
 	my $sess_id = -1;
 	my $sess_id = -1;
 	my $tick = -1;
 	my $tick = -1;
 	for my $i (0..$#l) {
 	for my $i (0..$#l) {
-		if ($l[$i] eq 'sess-id') {
+		if ($l[$i] eq 'sess-id-a') {
 			$sess_id = $i;
 			$sess_id = $i;
 		} elsif ($l[$i] eq 'created/tick') {
 		} elsif ($l[$i] eq 'created/tick') {
 			$tick = $i;
 			$tick = $i;
@@ -204,7 +297,7 @@ for my $id (keys %cfgSwitchboards) {
 	}
 	}
 	dbg "Lines: $#lines, Columns: sess-id: $sess_id, tick: $tick";
 	dbg "Lines: $#lines, Columns: sess-id: $sess_id, tick: $tick";
 	for my $i (1..$#lines) {
 	for my $i (1..$#lines) {
-		my @l = split(/\t/, $lines[$i]);
+		@l = split(/\t/, $lines[$i]);
 		$switchboards{$id}{$l[$sess_id]} = $l[$tick];
 		$switchboards{$id}{$l[$sess_id]} = $l[$tick];
 	}
 	}
 }
 }
@@ -221,7 +314,7 @@ loop: for my $id (keys %attrs) {
 		goto del;
 		goto del;
 	}
 	}
 	my @ss = split( /,/, $s);
 	my @ss = split( /,/, $s);
-	for $i (0..$#ss) {
+	for my $i (0..$#ss) {
 		my ($sess_id, $tick) = split ( /\//, $ss[$i]);
 		my ($sess_id, $tick) = split ( /\//, $ss[$i]);
 		if ($sess_id eq "" || !defined $tick || $tick eq "") {
 		if ($sess_id eq "" || !defined $tick || $tick eq "") {
 			next;
 			next;

+ 0 - 0
modules/iptrtpproxy/examples/rtpproxy_attrs.sql → modules/iptrtpproxy/oob/rtpproxy_attrs.sql


+ 428 - 0
modules/iptrtpproxy/oob/sip-router-oob-RTPPROXY.cfg.inc

@@ -0,0 +1,428 @@
+#
+# $Id: ser-oob-RTPPROXY.cfg.inc,v 1.46 2008/09/15 15:51:44 tirpi Exp $
+#
+# BEGIN: RTP proxy include section
+#
+# INSTALLATION: 
+# 1) apply patch ser-oob-RTPPROXY.cfg.patch to ser-oob.cfg
+# 2) add content of this file in ser-oob.cfg
+# 3) modify parameters
+#
+# Problem when forking and early media are received. In this case we should create for each branch extra dialog_id,
+# record-route it and call route[RTPPROXY_PROCESS_REQUEST] for each branch. But it's limited by
+# ser lump system.
+# Important: t_newtran() must be executed before RTPPROXY_PROCESS_REQUEST is executed to eat retransmissions
+# 
+# global script flags/AVPs used:
+#    FLAG_INIT_DLG ... if dialog initialize request
+#    FLAG_NAT ... test when RTP proxy will be applied
+#    $dialog_id ... unique dialog id
+#
+# local to RTP proxy AVPs (rtp_dlg_* are dialog AVPs) 
+#    $rtp_dlg_sess_ids ... list of confirmed (INVITE/200OK/ACK, INVITE/180/PRACK, UPDATE/200OK ) RTP sessions
+#    $rtp_dlg_sess_ids2 ... list of non-confirmed sessions
+#    $rtp_method_flag ... request method passed to onreply/failure
+#
+# rtpproxy.* configuration params
+#
+# Note: INVITE w/o offer, 200OK w/offer, PRACK/ACK w/answer (RFC3262) not supported.
+
+# RTP Proxy options
+# if RTP stream uses a different interface as SIP we can define sip-ip to find switchboard according received and next hop address
+loadmodule "iptrtpproxy"
+#loadmodule "modules/iptrtpproxy-v2/iptrtpproxy.so"
+
+modparam("iptrtpproxy", "config", "/etc/iptrtpproxy.cfg");
+
+#DEBCONF-RTTPPROXY-START
+modparam("iptrtpproxy", "switchboard", "name=*;sip-addr=213.192.59.91");   ## TODO change back to .75
+#modparam("iptrtpproxy", "switchboard", "name=gate_a;sip-addr=1.2.3.4;aggregation=AA")
+#modparam("iptrtpproxy", "switchboard", "name=gate_b;aggregation=A")
+modparam("iptrtpproxy", "codec_set", "name=any;media_type=*;rights=0;codecs=*;max_streams=9999");
+modparam("iptrtpproxy", "codec_set", "name=audio_only;media_type=*;rights=0;codecs=*;max_streams=0;media_type=audio;max_streams=9999");
+modparam("iptrtpproxy", "codec_set", "name=PCMU_PCMA_only;media_type=*;rights=1;codecs=*;rights=0;codecs=PCMU,PCMA");
+modparam("iptrtpproxy", "codec_set", "name=parityfec_only;media_type=*;rights=1;codecs=*;rights=0;codecs=parityfec");
+#DEBCONF-RTTPPROXY-END
+
+avpflags
+  rtpproxy_dlg;             # extra attributes
+
+modparam("avp_db", "attr_group", "id=rtp_dlg,flag=rtpproxy_dlg,table=rtpproxy_attrs");
+
+#
+# Whether to enable or disable the rtp proxy. Possible values are:
+# "0" -- always disable
+# "1" -- always enable regardless of whether UAC or UAS is behind NAT
+# "detect" -- detect whether the UAC or the UAS is behind NAT,
+#             and enable the rtp proxy when necessary
+# "compare" -- Check whether the inbound and outbound interfaces
+#		differ. If not, do not enable rtp proxy.
+#
+#DEBCONF-RTP_ENABLE-START
+rtp_proxy.enabled = "detect" desc "indicates whether the RTP Proxy is enabled or not (0/1/detect)"
+rtp_proxy.learning_timeout = "20" desc "RTP proxy learning timeout (sec)"
+rtp_proxy.ringing_timeout = "90" desc "RTP proxy ringing timeout (sec)"
+rtp_proxy.expiration_timeout = "30" desc "RTP proxy expiration timeout (sec)"
+rtp_proxy.always_learn = "0" desc "RTP proxy always learn (0/1)"
+rtp_proxy.hide_o_addr = "" desc "Fake address to hide original address provided at SDP o= line"
+rtp_proxy.codec_set = "any" desc "Codec authorization set"
+# throttled packets are MARKed and need be removed by a netfilter rule in FORWARD chain
+rtp_proxy.throttle_mark = "1" desc "Netfilter MARK of packets being throttled"
+rtp_proxy.throttle_rtp_max_bytes = "0" desc "Max. RTP bytes per sec rate"
+rtp_proxy.throttle_rtcp_max_bytes = "0" desc "Max. RTCP bytes per sec rate"
+rtp_proxy.throttle_rtp_max_packets = "0" desc "Max. RTP packets per sec rate"
+rtp_proxy.throttle_rtcp_max_packets = "0" desc "Max. RTCP packets per sec rate"
+#DEBCONF-RTP_ENABLE-END
+
+route[RTPPROXY_PROCESS_REQUEST] {
+	if (method != "INVITE" && method != "UPDATE" && method != "BYE" && method != "ACK" && method != "PRACK") {
+		# we are interested in INVITE/BYE dialog related requests only
+		break;
+	}
+	if (!defined $dialog_id) { # AVP loaded from route cookie
+		if (@method != "ACK")
+			t_reply("400", "Missing cookie");   # 500 (=our bug) in case of init request because it'd been generated by ourselves
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: missing cookie: %@hf_value.route[0]\n");
+		drop;
+	}
+	xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST:\n%mb");
+	if (isflagset(FLAG_INIT_DLG)) {
+		# if init request then test RTP proxy is to by applied
+		if (@cfg_get.rtp_proxy.enabled == "0") {
+			# RTP Proxy is disabled
+			break;
+		} else if (@cfg_get.rtp_proxy.enabled == "detect") {
+			if (!isflagset(FLAG_NAT)) {
+				# If no NAT is involved we don't have to do here anything.
+				break;
+			}
+		} else if (@cfg_get.rtp_proxy.enabled != "1") {
+			# This is not a valid setting
+			xlog("L_ERR", "RTPPROXY_PROCESS_REQUEST: Unknown option for rtp_proxy.enabled: %@cfg_get.rtp_proxy.enabled\n");
+			return;
+		} # else rtp proxy is permanently enabled
+		if (isflagset(FLAG_RTP_PROXY)) {
+			break;
+		}
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: routing '%@received.ip'->'%@next_hop.src_ip', src_ip:%@src.ip\n");
+		if (@src.ip == @received.ip) {
+			# it's case when ser forwards message to itself, if the second pass would 
+			# have added rtpproxy it'd rtpproxy twice through local chain, there is a problem
+			# in netfilter
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: do not add RTPPROXY because srcip=receivedip\n");
+			break;
+		}
+
+		if (method != "INVITE" || strempty(@msg.body)) {  # limitation, initial INVITE must provide SDP to allocate session
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: initial INVITE without body\n");
+			break;
+		}
+		if ((@cfg_get.rtp_proxy.enabled == "compare") && (@received.ip == @next_hop.src_ip)) {
+			# Inbound and outbound interfaces are the same,
+			# do not proxy.
+			break;
+		}
+	}
+
+	if ((method == "INVITE" || method == "UPDATE") && @msg.body!="") {
+		if (@iptrtpproxy.active_media_num == "0") break; 
+		iptrtpproxy_set_param("codec_set", "@cfg_get.rtp_proxy.codec_set");
+		iptrtpproxy_set_param("remove_codec_mask", "1");
+		if (!iptrtpproxy_authorize_media()) {
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: cannot authorize media\n");
+			t_reply("415", "Cannot authorize media");
+			drop;
+		}
+		if (@iptrtpproxy.active_media_num == "0") {
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: no acceptable codec\n");
+			t_reply("488", "Not acceptable here");
+			drop;
+		}
+
+		# if RTP stream uses the same interface as SIP we can find switchboard according received and next hop address
+		# It's always needed for dialog init requests but sometimes may be needed also for 
+		# re-INVITEs in case that switchboards could not be obtained from $rtp_dlg_sess_ids2, i.e. reused existing sesssions
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: find switchboard-a for '%@received.ip'\n");
+		if (!iptrtpproxy_set_param("switchboard_by_sip_ip_a", "@received.ip")) {
+			xlog("L_ERR", "RTPPROXY_PROCESS_REQUEST: Cannot find switchboard-a for routing '%@received.ip'\n");  
+			t_reply("500", "RTP proxy error");
+			drop;
+		}
+		eval_push("x:%@next_hop.src_ip");
+		if (@eval.get[-1] == @received.ip) {
+			iptrtpproxy_set_param("switchboard_b", "@iptrtpproxy.switchboard_a");
+		}
+		else {
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: find switchboard-b for '%@eval.get[-1]'\n");
+			if (!iptrtpproxy_set_param("switchboard_by_sip_ip_b", "@eval.get[-1]")) {
+				xlog("L_ERR", "RTPPROXY_PROCESS_REQUEST: Cannot find switchboard for routing '%@eval.get[-1]'\n");  
+				t_reply("500", "RTP proxy error");
+				drop;
+			}
+		}
+		eval_remove(-1, 1);
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: INVITE: %@iptrtpproxy.switchboard_a->%@iptrtpproxy.switchboard_b, learning-timeout: %@iptrtpproxy.learning_timeout, expiration-timeout: %@iptrtpproxy.expiration_timeout, always-learn: %@iptrtpproxy.always_learn\n");
+
+		# you can call a custom procedure to find switchboards and override default behaviour here
+		if (strempty(@iptrtpproxy.switchboard_a) || strempty(@iptrtpproxy.switchboard_b)) {  # do not use proxy
+			break;
+		}
+
+		# no "uncontrolled" break bellow this line !!!
+		if (isflagset(FLAG_INIT_DLG)) {  # INVITE && !to.tag
+			route("RTPPROXY_REMOVE_ATTRS");
+			iptrtpproxy_set_param("learning_timeout", "@cfg_get.rtp_proxy.ringing_timeout");
+
+		} else {
+			# remove any non-confirmed
+			route("RTPPROXY_LOAD_ATTRS");
+			if ($rtp_dlg_sess_ids2!="") {
+				# there is a non confirmed request in progress, wait till is confirmed
+				t_reply("491", "Pending request");
+				drop;
+			}
+			if (strempty($rtp_dlg_sess_ids)) {
+				# limitation, initial INVITE must provide SDP to allocate session
+				break;  # possible because $rtp_dlg_* are null
+			}
+			iptrtpproxy_set_param("protected_session_ids", "$rtp_dlg_sess_ids");
+			iptrtpproxy_delete("$rtp_dlg_sess_ids2");
+			del_attr("$rtp_dlg_sess_ids2");
+			iptrtpproxy_set_param("learning_timeout", "@cfg_get.rtp_proxy.learning_timeout");
+		}
+
+		if (@cfg_get.rtp_proxy.hide_o_addr != "") {
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: INVITE: rewrite o_addr='%@cfg_get.rtp_proxy.hide_o_addr'\n");
+			iptrtpproxy_set_param("o_addr", "@cfg_get.rtp_proxy.hide_o_addr");
+		}
+		# we are optimistic and do not expect callee will change stream ip:port when caller has not done it.
+		# If we want to be fully RFC3264 compliant we'd alloc completely new RTP session set for all
+		# streams provided in caller's SDP regardless a change detected since last confirmed INVITE.
+		# This solution has two drawbacks, the 1st it may lead to RTP session exhastion, the 2nd there are 
+		# non re-INVITE compliant phones which does not like IP:port change in SDP
+		iptrtpproxy_set_param("protected_session_ids", "$rtp_dlg_sess_ids");
+
+		# throttling stuff
+		iptrtpproxy_set_param("throttle_mark", "@cfg_get.rtp_proxy.throttle_mark");
+		iptrtpproxy_set_param("throttle_rtp_max_bytes", "@cfg_get.rtp_proxy.throttle_rtp_max_bytes");
+		iptrtpproxy_set_param("throttle_rtcp_max_bytes", "@cfg_get.rtp_proxy.throttle_rtcp_max_bytes");
+		iptrtpproxy_set_param("throttle_rtp_max_packets", "@cfg_get.rtp_proxy.throttle_rtp_max_packets");
+		iptrtpproxy_set_param("throttle_rtcp_max_packets", "@cfg_get.rtp_proxy.throttle_rtcp_max_packets");
+
+		if (isflagset(FLAG_REVERSE_DIR)) {
+			eval_push("0");  # callee -> caller
+		} else { 
+			eval_push("1");  # caller -> callee
+		}
+		if (!iptrtpproxy_alloc("@eval.pop[-1]")) {  
+			t_reply("500", "RTP proxy error");
+			drop;
+		}
+		# we need save in dialog as non-confirmed sess_ids (not only in transaction) because
+		# UPDATE in non-confirmed INVITE and ACK to 200OK are considered as separate transactions.
+		if (@iptrtpproxy.session_ids != "")
+			$rtp_dlg_sess_ids2 = @iptrtpproxy.session_ids;
+		else
+			del_attr("$rtp_dlg_sess_ids2");
+
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: allocated ids2=%$rtp_dlg_sess_ids2\n");
+		# session_ids are stored in AVP to let commit (on_reply) or rollback (on_failure) allocated sessions
+		$rtp_method_flag = @method;
+
+		append_hf("X-RTP-Proxy: YES\r\n");
+		setflag("FLAG_RTP_PROXY");
+		route("RTPPROXY_SAVE_ATTRS");
+	} 
+	else if (method == "ACK" || method == "PRACK") {
+		# ACK after 200OK is considered as separate transaction
+		# we'll accept RTP sessions, in case of re-INVITE 
+		# we cannot do it in 200OK because old and new streams must
+		# be traversed till ACK
+		# save non-confirmed as confirmed
+		route("RTPPROXY_LOAD_ATTRS");
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: %@method, delete(ids=%$rtp_dlg_sess_ids), protect(ids2=%$rtp_dlg_sess_ids2)\n");
+		if (strempty($rtp_dlg_sess_ids2)) {
+			break; /* do not save, non SDP related re-INVITE or retrasmission which is not eaten by tm module, SDP in ACK is not supported */
+		}
+		iptrtpproxy_set_param("protected_session_ids", "$rtp_dlg_sess_ids2");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids");
+		$rtp_dlg_sess_ids = $rtp_dlg_sess_ids2;
+		del_attr("$rtp_dlg_sess_ids2");
+		route("RTPPROXY_SAVE_ATTRS");
+	}
+	else if (method == "BYE") {  # CANCEL ???
+		# we must delete rtp session in BYE request, otherwise we risk problem of lost 200OK to BYE
+		route("RTPPROXY_LOAD_ATTRS");
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REQUEST: %@method delete(ids=%$rtp_dlg_sess_ids, ids2=%$rtp_dlg_sess_ids2)\n");
+		iptrtpproxy_set_param("protected_session_ids", "");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids2");
+		route("RTPPROXY_REMOVE_ATTRS");
+
+		# $rtp_method_flag = @method;  # nothing to do in onreply to BYE
+	}
+	# rtp_dlg_* have been removed to save memory
+}
+
+route[RTPPROXY_PROCESS_REPLY] {
+	if (!defined $dialog_id || strempty($rtp_method_flag) || strempty(@msg.body)) break;
+	xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY:\n%mb\n");
+
+	if ($rtp_method_flag == "INVITE" && (status=~"18[0-9]" || status=~"2[0-9][0-9]")) {
+		# If RTP proxy was activated and this is a 18x or 2xx reply with a
+		# body, inform RTP proxy.
+
+		# onreply route is called for EACH retransmission, tm does NOT eat anything
+		if (strempty($rtp_last_code) || $rtp_last_code != @msg.res.code) {
+			if (@iptrtpproxy.active_media_num == "0") break; 
+			iptrtpproxy_set_param("codec_set", "@cfg_get.rtp_proxy.codec_set");
+			iptrtpproxy_set_param("remove_codec_mask", "1");
+			if (!iptrtpproxy_authorize_media()) {
+				xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: cannot authorize media\n");
+				drop;
+			}
+			if (@iptrtpproxy.active_media_num == "0") {
+				xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: no acceptable codec\n");
+				drop;
+			}
+ 
+			# (probably) no retransmission
+			route("RTPPROXY_LOAD_ATTRS");
+			$rtp_last_code = @msg.res.code;
+			if (isflagset(FLAG_INIT_DLG) && status=~"18[0-9]") {
+				iptrtpproxy_set_param("learning_timeout", "@cfg_get.rtp_proxy.ringing_timeout");
+			}
+			else {
+				iptrtpproxy_set_param("learning_timeout", "@cfg_get.rtp_proxy.learning_timeout");
+			}	
+			iptrtpproxy_set_param("expiration_timeout", "@cfg_get.rtp_proxy.expiration_timeout");
+			iptrtpproxy_set_param("always_learn", "@cfg_get.rtp_proxy.always_learn");
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: update(ids2=%$rtp_dlg_sess_ids2, learning-timeout: %@iptrtpproxy.learning_timeout) protect(ids=%$rtp_dlg_sess_ids)\n");
+			if (@cfg_get.rtp_proxy.hide_o_addr != "") {
+				xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: INVITE: rewrite o_addr='%@cfg_get.rtp_proxy.hide_o_addr'\n");
+				iptrtpproxy_set_param("o_addr", "@cfg_get.rtp_proxy.hide_o_addr");
+			}
+			if (!isflagset(FLAG_REVERSE_DIR)) {    # revert direction
+				eval_push("0");  # callee -> caller
+			} else { 
+				eval_push("1");  # caller -> callee
+			}
+			iptrtpproxy_set_param("protected_session_ids", "$rtp_dlg_sess_ids");
+			# throttling stuff
+			iptrtpproxy_set_param("throttle_mark", "@cfg_get.rtp_proxy.throttle_mark");
+			iptrtpproxy_set_param("throttle_rtp_max_bytes", "@cfg_get.rtp_proxy.throttle_rtp_max_bytes");
+			iptrtpproxy_set_param("throttle_rtcp_max_bytes", "@cfg_get.rtp_proxy.throttle_rtcp_max_bytes");
+			iptrtpproxy_set_param("throttle_rtp_max_packets", "@cfg_get.rtp_proxy.throttle_rtp_max_packets");
+			iptrtpproxy_set_param("throttle_rtcp_max_packets", "@cfg_get.rtp_proxy.throttle_rtcp_max_packets");
+			if (iptrtpproxy_update("@eval.pop[-1]", "$rtp_dlg_sess_ids2")) {
+				if (status=~"2[0-9][0-9]") {
+					if (isflagset(FLAG_INIT_DLG)) {
+						xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: adjust timeout(ids2=%$rtp_dlg_sess_ids2)\n");
+						if (isflagset(FLAG_REVERSE_DIR)) {
+							eval_push("0");  # callee -> caller
+						} else { 
+							eval_push("1");  # caller -> callee
+						}
+						iptrtpproxy_adjust_timeout("@eval.pop[-1]", "$rtp_dlg_sess_ids2");  # decrease ringing-timeout, a to b direction
+					}
+					# the confirmed session will be adjusted in ACK, because old media (re-INVITE) should be traversed 
+				}
+				if (@iptrtpproxy.session_ids != "") {
+					$rtp_dlg_sess_ids2 = @iptrtpproxy.session_ids; 
+					$rtp_last_sess_ids2 = @iptrtpproxy.session_ids;  # keep it in transaction
+				}
+				else {
+					del_attr("$rtp_dlg_sess_ids2");
+					del_attr("$rtp_last_sess_ids2");
+				}
+			}
+			else {
+				;
+				# drop; ???
+			}
+			route("RTPPROXY_SAVE_ATTRS"); 
+		}
+		else {
+			# we MUST do the same message modifications as in first reply
+			xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: update-retransmission(ids2=%$rtp_last_sess_ids2)\n");
+			if (@cfg_get.rtp_proxy.hide_o_addr != "") {
+				xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: INVITE: retrasmission rewrite o_addr='%@cfg_get.rtp_proxy.hide_o_addr'\n");
+				iptrtpproxy_set_param("o_addr", "@cfg_get.rtp_proxy.hide_o_addr");
+			}
+			if (!isflagset(FLAG_REVERSE_DIR)) {   #  set update SDP only flag & revert direction
+				eval_push("2");  # callee -> caller (bin=10)
+			} else { 
+				eval_push("3");  # caller -> callee (bin=11)
+			}
+			iptrtpproxy_update("@eval.pop[-1]", "$rtp_last_sess_ids2");  # TODO flag to update SDP only
+		}
+	}
+	else if ($rtp_method_flag == "UPDATE" && status=~"2[0-9][0-9]" && $rtp_dlg_sess_ids2!="") {
+		# confirm pending offer (UPDATE is not followed by ACK/PRACK)		
+		route("RTPPROXY_LOAD_ATTRS");
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_REPLY: %@method, delete(ids=%$rtp_dlg_sess_ids), protect(ids2=%$rtp_dlg_sess_ids2)\n");
+		iptrtpproxy_set_param("protected_session_ids", "$rtp_dlg_sess_ids2");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids");
+		$rtp_dlg_sess_ids = $rtp_dlg_sess_ids2;
+		del_attr("$rtp_dlg_sess_ids2");
+		route("RTPPROXY_SAVE_ATTRS");
+	}
+}
+
+route[RTPPROXY_PROCESS_FAILURE] {
+	if (!defined $dialog_id || strempty($rtp_method_flag)) break;
+
+	xlog("L_EDEBUG", "RTPPROXY_PROCESS_FAILURE:\n%mb\n");
+
+	if (isflagset(FLAG_INIT_DLG) && $rtp_method_flag == "INVITE") {
+		route("RTPPROXY_LOAD_ATTRS");
+		#remove all sessions
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_FAILURE: %@method delete(ids=%$rtp_dlg_sess_ids, ids2=%$rtp_dlg_sess_ids2)\n");
+		iptrtpproxy_set_param("protected_session_ids", "");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids");  # early media are considered as confirmed
+		iptrtpproxy_delete("$rtp_dlg_sess_ids2");
+		route("RTPPROXY_REMOVE_ATTRS");
+	} else if ($rtp_method_flag == "INVITE" || $rtp_method_flag == "UPDATE") {
+		route("RTPPROXY_LOAD_ATTRS");
+		#remove only unconfirmed sessiosn
+		xlog("L_EDEBUG", "RTPPROXY_PROCESS_FAILURE: %@method, delete(ids2=%$rtp_dlg_sess_ids2), protect(ids=%$rtp_dlg_sess_ids)\n");
+		iptrtpproxy_set_param("protected_session_ids", "$rtp_dlg_sess_ids");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids2");
+		del_attr("$rtp_dlg_sess_ids2");
+		route("RTPPROXY_SAVE_ATTRS");
+	}
+
+	if ($rtp_dlg_sess_ids2!="") {   # ?????  never TODO: retest
+		# do it if request really failed
+		# if flexroute forwards call in CAPM-PP callback then request is dropped
+		iptrtpproxy_set_param("protected_session_ids", "");
+		iptrtpproxy_delete("$rtp_dlg_sess_ids2");
+		del_attr("$rtp_dlg_sess_ids2");
+	}
+}
+
+route[RTPPROXY_LOAD_ATTRS] {
+	del_attr("$rtp_dlg_sess_ids");
+	del_attr("$rtp_dlg_sess_ids2");
+	load_extra_attrs("rtp_dlg", "$dialog_id");
+	xlog("L_EDEBUG", "RTPPROXY_LOAD_ATTRS: dialog_id=%$dialog_id, method/flag='%@method'/'%$rtp_method_flag', ids=%$rtp_dlg_sess_ids, ids2=%$rtp_dlg_sess_ids2\n");
+}
+
+route[RTPPROXY_SAVE_ATTRS] {
+	xlog("L_EDEBUG", "RTPPROXY_SAVE_ATTRS: dialog_id=%$dialog_id, method='%@method', ids=%$rtp_dlg_sess_ids, ids2=%$rtp_dlg_sess_ids2\n");
+	setavpflag("$f.rtp_dlg_sess_ids", "rtpproxy_dlg");
+	setavpflag("$f.rtp_dlg_sess_ids2", "rtpproxy_dlg");
+	save_extra_attrs("rtp_dlg", "$dialog_id");
+	del_attr("$rtp_dlg_sess_ids");
+	del_attr("$rtp_dlg_sess_ids2");
+}
+
+route[RTPPROXY_REMOVE_ATTRS] {
+	xlog("L_EDEBUG", "RTPPROXY_REMOVE_ATTRS: dialog_id=%$dialog_id, method='%@method'\n");
+	del_attr("$rtp_dlg_sess_ids");
+	del_attr("$rtp_dlg_sess_ids2");
+	remove_extra_attrs("rtp_dlg", "$dialog_id");
+}
+
+
+# END: RTP proxy include section

+ 152 - 0
modules/iptrtpproxy/oob/sip-router-oob-RTPPROXY.cfg.patch

@@ -0,0 +1,152 @@
+--- sip-router-oob.cfg.1st_phase	2011-03-03 14:07:56.000000000 +0100
++++ sip-router-oob.cfg	2011-03-03 14:07:56.000000000 +0100
+@@ -283,18 +283,6 @@
+ #
+ session_timer.min_se = "90" desc "minimum session interval (in s)"
+ 
+-# RTP Proxy options
+-#
+-# Whether to enable or disable the rtp proxy. Possible values are:
+-# "0" -- always disable
+-# "1" -- always enable regardless of whether UAC or UAS is behind NAT
+-# "detect" -- detect whether the UAC or the UAS is behind NAT,
+-#             and enable the rtp proxy when necessary
+-#
+-#DEBCONF-RTP_ENABLE-START
+-rtp_proxy.enabled = "detect" desc "indicates whether the RTP Proxy is enabled or not (0/1/detect)"
+-#DEBCONF-RTP_ENABLE-END
+-
+ # ------------------ Module Loading -----------------------------------------
+ #!ifdef LOCAL_TEST_RUN
+ loadpath "modules:modules_s"
+@@ -456,7 +444,7 @@
+ modparam("rr", "enable_full_lr", 1)
+ 
+ # Limit the length of the AVP cookie to necessary attributes only
+-modparam("rr", "cookie_filter", "(account|rproxy|stimer|dialog_id)")
++modparam("rr", "cookie_filter", "(account|stimer|dialog_id)")
+ 
+ # You probably do not want that someone can simply read and change
+ # the AVP cookie in your Routes, thus should really change this
+@@ -466,7 +454,7 @@
+ # The ftag Route parameter may be used to easily determine if a BYE
+ # is coming from caller or callee, but we prefer shorter messages
+ # Enable when FLAG_REVERSE_DIR is to be used
+-modparam("rr", "append_fromtag", 0)
++modparam("rr", "append_fromtag", 1)
+ 
+ 
+ # -- gflags --
+@@ -526,7 +514,7 @@
+ 
+ # RTP Proxy address
+ #DEBCONF-RTTPPROXY-START
+-modparam("nathelper", "rtpproxy_sock", "udp:127.0.0.1:22222")
++#modparam("nathelper", "rtpproxy_sock", "udp:127.0.0.1:22222")
+ #DEBCONF-RTTPPROXY-END
+ 
+ # TCP keepalives as simple as CRLF
+@@ -665,7 +653,8 @@
+ 	# If this is an initial INVITE (without a To-tag) we might try
+ 	# another target (call forwarding or voicemail) after receiving
+ 	# an error.
+-	if (isflagset(FLAG_INIT_DLG)) {
++	# and also for RTPPROXY processing
++	if (method=="INVITE" || method == "UPDATE") {
+ 		t_on_failure("FAILURE_ROUTE");
+ 	}
+ 
+@@ -679,7 +668,7 @@
+ 
+ 	# Activate the RTP proxy as the second last step because it modifies the
+ 	# body but also sets an dialog AVP cookie.
+-	route(RTPPROXY);
++	route(RTPPROXY_PROCESS_REQUEST);
+ 
+ 	# Insert a Record-Route header into all requests.
+ 	# This has to be done as one of the last steps to include all the
+@@ -874,47 +863,7 @@
+ 	}
+ }
+ 
+-
+-# Activates RTP proxy if necessary.
+-#
+-route[RTPPROXY]
+-{
+-	if (@cfg_get.rtp_proxy.enabled == "0") {
+-		# RTP Proxy is disabled
+-		break;
+-	} else if (@cfg_get.rtp_proxy.enabled == "detect") {
+-		if (!isflagset(FLAG_NAT)) {
+-			# If no NAT is involved we don't have to do here anything.
+-			break;
+-		}
+-	} else if (@cfg_get.rtp_proxy.enabled != "1") {
+-		# This is not a valid setting
+-		xlog("L_ERR", "Unknown option for rtp_proxy.enabled: %@cfg_get.rtp_proxy.enabled\n");
+-		break;
+-	} # else rtp proxy is permanently enabled
+-
+-	# If the message terminates a dialog for which the RTP proxy 
+-	# was turned on, turn it off again.
+-	if ((method == "BYE" && isflagset(FLAG_RTP_PROXY)) ||
+-		(method == "CANCEL")) {
+-		unforce_rtp_proxy();
+-		append_hf("P-RTP-Proxy: Off\r\n");
+-		break;
+-	}
+-
+-	# Turn the RTP proxy on for INVITEs and UPDATEs, if they 
+-	# have a body
+-	if (((method=="INVITE" || method == "UPDATE") && @msg.body != "")
+-	    && !isflagset(FLAG_RTP_PROXY))
+-	{
+-		force_rtp_proxy('r');
+-		append_hf("P-RTP-Proxy: On\r\n");
+-		setflag(FLAG_RTP_PROXY);
+-		$rproxy = 1;
+-		setavpflag($rproxy, "dialog_cookie");
+-	}
+-}
+-
++include_file "sip-router-oob-RTPPROXY.cfg.inc"
+ 
+ # Handling of Route headers
+ #
+@@ -948,11 +897,6 @@
+ 			setflag(FLAG_ACC);
+ 		}
+ 
+-		# Restore the RTP proxy flag if present
+-		if ($rproxy == "1") {
+-			setflag(FLAG_RTP_PROXY);
+-		}
+-
+ 		# Restore Session Timer flag and headers.
+ 		if ( defined $stimer && ($stimer != "0")) {
+ 			route(SESSION_TIMER);
+@@ -1586,6 +1530,7 @@
+ 			}
+ 		}
+ 	} # if (isflagset...
++	route(RTPPROXY_PROCESS_FAILURE);
+ }
+ 
+ 
+@@ -1597,14 +1542,7 @@
+ 	# allow proper routing of in-dialog messages.
+ 	route(UAS_NAT_DETECTION);
+ 
+-	# If RTP proxy was activated and this is a 18x or 2xx reply with a
+-	# body, inform RTP proxy.
+-	if (isflagset(FLAG_RTP_PROXY)
+-	    && status=~"(18[03])|(2[0-9][0-9])"
+-	    && @msg.body != "")
+-	{
+-		force_rtp_proxy('r');
+-	}
++	route(RTPPROXY_PROCESS_REPLY);
+ 
+ 	# Let's check for session timer support.
+ 	if (isflagset(FLAG_SESSIONTIMER) && status =~ "2[0-9][0-9]") {

Some files were not shown because too many files changed in this diff