瀏覽代碼

Support for IPv6

Added support for IPv6, requires xt_RTPPROXY module supporting IPv6
Tomas Mandys 13 年之前
父節點
當前提交
f8916abf6b
共有 4 個文件被更改,包括 486 次插入164 次删除
  1. 1 1
      modules/iptrtpproxy/Makefile
  2. 329 64
      modules/iptrtpproxy/README
  3. 30 0
      modules/iptrtpproxy/doc/iptrtpproxy.xml
  4. 126 99
      modules/iptrtpproxy/iptrtpproxy.c

+ 1 - 1
modules/iptrtpproxy/Makefile

@@ -22,7 +22,7 @@ NAME=iptrtpproxy.so
 #		-L $(iptdir)/extensions -lext
 # look for libipt_RTPPROXY first under /lib/xtables (default install dir)
 # and if not found in $(iptdir)/extensions (compile dir)
-LIBS = -L $(iptdir)/libiptc -L $(iptdir)/iptables/extensions -lipt_RTPPROXY
+LIBS = -L $(iptdir)/libiptc -L $(iptdir)/iptables/extensions -lxt_RTPPROXY
 LIBS+= -L $(xtablesmoduledir)
 INCLUDES = -I $(iptdir)/linux-2.6/include -I $(kerneldir) -I $(iptdir)/iptables/include
 

+ 329 - 64
modules/iptrtpproxy/README

@@ -5,8 +5,6 @@ Tomas Mandys
    Iptel.org
 
    Copyright © 2007 Tomas Mandys
-   Revision History
-   Revision $Revision$ $Date$
      __________________________________________________________________
 
    1.1. Overview
@@ -15,29 +13,50 @@ Tomas Mandys
 
         1.3.1. config (string)
         1.3.2. switchboard (string)
+        1.3.3. rpc_heartbeat_timeout (int)
+        1.3.4. hostname (string)
+        1.3.5. declare_codec (string)
+        1.3.6. codec_set (string)
 
    1.4. Functions
 
-        1.4.1. iptrtpproxy_alloc(gate_a_to_b, switchboard_id)
+        1.4.1. iptrtpproxy_alloc(gate_a_to_b [, existing_sess_ids])
         1.4.2. iptrtpproxy_update(gate_a_to_b, session_ids)
         1.4.3. iptrtpproxy_adjust_timeout(gate_a_to_b, session_ids)
         1.4.4. iptrtpproxy_delete(session_ids)
-        1.4.5. iptrtpproxy_find(gate_a, gate_b)
-        1.4.6. @iptrtpproxy.session_ids
-        1.4.7. @iptrtpproxy.sdp_ip
-        1.4.8. @iptrtpproxy.switchboard
-        1.4.9. @iptrtpproxy.direction
+        1.4.5. iptrtpproxy_authorize_media()
+        1.4.6. iptrtpproxy_set_param(param, value)
+        1.4.7.
+                iptrtpproxy_set_param("(aggregation/switchboard)_by_sip_ip_(
+                a/b)", sip_ip)
+
+        1.4.8. iptrtpproxy_set_param("protected_session_ids", sess_ids)
+        1.4.9. @iptrtpproxy.session_ids
+        1.4.10. @iptrtpproxy.sdp_ip
+        1.4.11. @iptrtpproxy.o_name
+        1.4.12. iptrtpproxy_set_param("o_name", value)
+        1.4.13. @iptrtpproxy.o_addr
+        1.4.14. @iptrtpproxy.o_ip_ver
+        1.4.15. iptrtpproxy_set_param("o_addr", value)
+        1.4.16. @iptrtpproxy.session_mark
+        1.4.17. iptrtpproxy_set_param("session_mark", value)
+        1.4.18. iptrtpproxy_set_param("codec_set", value)
+        1.4.19. iptrtpproxy_set_param("remove_codec_mask", value)
+        1.4.20. @iptrtpproxy.auth_rights
+        1.4.21. @iptrtpproxy.active_media_num
+        1.4.22. @iptrtpproxy.auth_throttling
 
 1.1. Overview
 
    It provides similar functionality as nathelper but communicates with
    netfilter kernel xt_RTPPROXY module using libipt_RTPPROXY 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. See http://www.2p.cz/en/netfilter_rtp_proxy for more details.
+   library. See http://www.2p.cz/en/netfilter_rtp_proxy All RTP streams
+   are manipulated directly in kernel space, no data are copied from
+   kernel to userspace and back, it reduces load and delay. See
+   http://www.2p.cz/en/netfilter_rtp_proxy for more details.
 
    The ser module is written as light-weighted, there is not implemented
-   any dialog managment as in nathelper, the reason is that such API
+   any dialog managment as in nathelper, the reason is that such an API
    should be provided by core or specialized dialog manager module.
    Because such module is not in CVS, session information may be stored in
    extra attributes of avp_db module and session id itself in record route
@@ -49,6 +68,11 @@ Tomas Mandys
 
    Nathelper may be still used for testing if client is behind the NAT.
 
+   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.
+
    Limitations:
      * only IPv4 addresses are supported.
      * more media streams per session supported
@@ -57,7 +81,7 @@ Tomas Mandys
 
    The following libraries or applications must be installed before
    running SER with this module loaded:
-     * netfilter xt_RTPROXY & libipt_RTPPROXY, see
+     * netfilter xt_RTPPROXY & libipt_RTPPROXY, see
        http://www.2p.cz/en/netfilter_rtp_proxy
 
 Note
@@ -72,56 +96,182 @@ Note
 1.3.1. config (string)
 
    References iptrtpproxy.cfg, see iptrtpproxy_helper. Default value is
-   /etc/iptrtpproxy.cfg.
+   /etc/iptrtpproxy.cfg. If only codec authorization is to be used then
+   /dev/null may be used.
 
 1.3.2. switchboard (string)
 
    References xt_RTPPROXY switchboard for usage by ser module.
 
    The format is:
-  name "=" value * ( ";" name "=" value )
+  "name=" value * ( ";" name "=" value )
+
+  name =  "aggregation" | "sip-addr-"
 
-  name =  "name" | "*" | ( "ringing-timeout " ) | ( ( "learning-timeout-" | "alw
-ays-learn-") ("a" | "b") )
+   The name 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 * set values for all switchboards.
 
-   The meaning of parameters is described in libipt_RTPROXY and
-   iptrtpproxy documentation. ringing timeout 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 sip-addr is address used by iptrtpproxy_ser_param(by_sip_ip) to
+   find a switchboard for particular connection. If not explicitly
+   configured then RTP switchboard gate address are used for this feature.
 
-   The name" is identifier that will be used by script functions and
-   references switchboard. It's mandatory parameter. More switchboards may
-   be declared. The special name * set values for all switchboards.
+   The aggregation enables to aggregate more switchboards in cluster and
+   to widden bandwidth. Aggregation will take sip-addr from the first
+   switchboard of its.
 
    Example 1. Declare switchboard
         ...
         modparam("iptrtpproxy", "config", "/etc/iptrtpproxy.cfg");
-        modparam("iptrtpproxy", "switchboard", "name=*;learning-timeout-a=10;lea
-rning-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");
+        ...
+
+1.3.3. rpc_heartbeat_timeout (int)
+
+   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 30.
+
+1.3.4. hostname (string)
+
+   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.
+
+1.3.5. declare_codec (string)
+
+   There are basic implicit codecs compiled in module, more codecs may be
+   added by this parameter (one codec per modparam).
+
+1.3.6. codec_set (string)
+
+   Declares new codec set. Codecs are declared for each media type
+   independently.
+
+   The format is:
+  "name=" value * ( ";" name "=" value )
+
+  name =  "media_type" | "rights" | "codecs" | "max_streams" | ( "rtp" | "rtcp"
+) "_" ( "bytes" | "packets" )
+
+  media_types = "audio" | "video" | "application" | "text" | "message" | "data"
+| "control" | "?" | "*"
+
+   The name is the codec set name to be defined.
+
+   The media_type belongs to type at m= SDP line. Question mark means
+   "unknown media" type and asterisk "all media types".
+
+   The max_streams defines how many streams (m= lines) is allowed per
+   media type.
+
+   The rights defines if particular codec is allowed 0, disallowed, i.e.
+   will be removed if bit AND operation with remove_codec_mask is non-zero
+   or its presence will be signallized by @iptrtpproxy.auth_rights (any
+   other value).
+
+   The codecs comma separated list of codecs. Previous media_type&rights
+   will be applied.
+
+   The rtp/rtcp_bytes/packets limits bandwidth per media_type (0 is
+   unlimited). It will override bandwidth limited by
+   iptrtpproxy_set_param("throttle_*").
+
+   Example 2. Declare codec_set
+        ...
+        # 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 bu
+t 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,G72
+8,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_byte
+s=10000;rtcp_bytes=1000");
         ...
 
 1.4. Functions
 
-1.4.1.  iptrtpproxy_alloc(gate_a_to_b, switchboard_id)
+1.4.1.  iptrtpproxy_alloc(gate_a_to_b [, existing_sess_ids])
 
    Parses SDP content and allocates for each RTP media stream one RTP
    proxy session. SDP is updates to reflect allocated sessions.
+   Switchboard/aggregation is set using iptrtpproxy_set_param(by_sip_ip)
+   or iptrtpproxy_set_param("switchboard/aggregation").
+
+   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.
+
+   Proxy may hide caller identity provided at o= line using
+   @iptrtpproxy.o_name/addr and iptrtpproxy_set_param(o_name/addr)
+   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.
      * if gate_a_to_b bit 0 is set then SDP regards to gate-a to gate-b
-       direction. If bit 1 is set then ringing timeout is used instead of
-       learning timeout for particular gate timeout.
-     * switchboard_id is reference to a switchboard with name declared as
-       switchboard modparam. If empty then use switchboard found by
-       iptrtpproxy_find (equal using @iptrtpproxy.switchboard).
+       direction.
+     * protected_session_ids 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.
      * function returns true is a session was created, identifier is
        available via select @iptrtpproxy.session_ids.
 
-   Example 2. iptrtpproxy_alloc usage
+   Example 3. iptrtpproxy_alloc usage
         ...
-        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.agg
+regation_a");
+                }
+                else {
+                        iptrtpproxy_set_param("switchboard_b", "@iptrtpproxy.swi
+tchboard_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;
         ...
 
 1.4.2.  iptrtpproxy_update(gate_a_to_b, session_ids)
@@ -131,11 +281,18 @@ rning-timeout-b=10;ringing-timeout=90");
    e.g. media stream has port zero particular session is released), the
    result of @iptrtpproxy.session_ids should be stored for future
    in-dialog usage.
+
+   The SDP contect is also affected by iptrtpproxy_set_param(o_name/addr)
+   functions. If a stream is deactivated in SDP then Sessions may be
+   deleted unless mentioned in protected_session_ids.
      * if gate_a_to_b bit 0 is set then SDP regards to gate-a to gate-b
-       direction. If bit 1 is set then ringing timeout is used instead of
-       learning timeout for particular gate timeout.
+       direction.
+       if gate_a_to_b 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!
 
-   Example 3. iptrtpproxy_update usage
+   Example 4. iptrtpproxy_update usage
         ...
         # load $sess_ids from dialog
         if (iptrtpproxy_update("0", $sess_ids)) {
@@ -149,12 +306,17 @@ rning-timeout-b=10;ringing-timeout=90");
    Adjust timeout for particular gate. It's useful in "200 OK" decrease
    timeout to learning timeout if INVITE has set (long) ringing timeout.
      * if gate_a_to_b bit 0 is set then it regards to gate-a to gate-b
-       direction. If bit 1 is set then ringing timeout is used instead of
-       learning timeout for particular gate timeout.
+       direction.
 
-   Example 4. iptrtpproxy_adjust_timeout usage
+   Example 5. iptrtpproxy_adjust_timeout usage
         ...
         # 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)) {
         }
         ...
@@ -162,47 +324,150 @@ rning-timeout-b=10;ringing-timeout=90");
 1.4.4.  iptrtpproxy_delete(session_ids)
 
    Delete sessions identified by session_ids. May be used when dialog is
-   being destroyed (BYE) or when INVITE failed in failure route.
+   being destroyed (BYE) or when INVITE failed in failure route. If
+   protected_session_ids list is provided then this set is excluded from
+   sessions to be deleted.
 
-   Example 5. iptrtpproxy_delete usage
+   Example 6. iptrtpproxy_delete usage
         ...
         # load $sess_ids from dialog
         iptrtpproxy_delete($sess_ids);
         ...
 
-1.4.5.  iptrtpproxy_find(gate_a, gate_b)
+1.4.5.  iptrtpproxy_authorize_media()
+
+   Authorizes SDP media according currect codec_set. If bit AND operation
+   between rights in codec set and remove_codec_mask is non zero then such
+   a codec are to be removed. The result may be obtained from
+   @iptrtpproxy.auth_rights which returns max. right which has been
+   applied when processing all codecs of enabled streams.
+   @iptrtpproxy.active_media_num may be used to get info if some media
+   stream is to be throttled.
 
-   Find corresponding switchboard and set @iptrtpproxy.switchboard and
-   @iptrtpproxy.direction.
-     * if gate_a/b switchboard identification
-     * function returns true if switch was found
+   The function MUST NOT be called after iptrtpproxy_alloc/update! But the
+   function may be called several times to authorize using more codec
+   sets.
 
-   Example 6. iptrtpproxy_find usage
+   Example 7. iptrtpproxy_authorize_media usage
         ...
-        if (iptrtpproxy_find("@received.ip", "@next_hop.src_ip")) {
-                if (iptrtpproxy_alloc("1", "")) {
-                          $sess_ids = @iptrtpproxy.session_ids;
+        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: <sip:1.2.3.4>");
+                t_reply("301", "Redirect to transcoder");
+                drop;
 
-                }
         }
         ...
 
-1.4.6.  @iptrtpproxy.session_ids
+1.4.6.  iptrtpproxy_set_param(param, value)
+
+   Set particular parameter needed mainly by
+   iptrtpproxy_alloc/update/adjust_timeout. The paramter value is availble
+   via @iptrtpproxy.<param>.
+     * Supported parameters: expiration_timeout, ttl, learning_timeout,
+       always_flags, aggregation_a, aggregation_b, switchboard_a,
+       switchboard_b, throttle_mark, throttle_rtp_max_bytes,
+       throttle_rtp_max_packets, throttle_rtcp_max_bytes,
+       throttle_rtcp_max_packets.
+
+1.4.7.  iptrtpproxy_set_param("(aggregation/switchboard)_by_sip_ip_(a/b)",
+sip_ip)
+
+   Find corresponding aggregation or switchboard and set
+   @iptrtpproxy.aggregation_a/b or @iptrtpproxy.switchboard_a/b.
+   Switchboards/aggregations are compared against sip-addr, it allow
+   separate SIP and RTP traffic and RTP aggregation.
+     * sip_ip IP to be compared, typically @received.ip or
+       @next_hop.src_ip.
+     * function returns true if switchboard/aggregation was found
+
+1.4.8.  iptrtpproxy_set_param("protected_session_ids", sess_ids)
+
+   Used for reusing sessions in iptrtpproxy_alloc, iptrtpproxy_update and
+   iptrtpproxy_delete.
+
+1.4.9.  @iptrtpproxy.session_ids
 
    Returns sessions allocated/updated in iptrtpproxy_alloc/update.
 
    The format is:
-        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
+
+1.4.10.  @iptrtpproxy.sdp_ip
+
+   Return first rewritten IP provided at SDP c= line.
+
+1.4.11.  @iptrtpproxy.o_name
+
+   Return username from original o= line.
+
+1.4.12.  iptrtpproxy_set_param("o_name", value)
+
+   Username to be rewritten at o= line by iptrtpproxy_alloc/update to hide
+   caller identity. If value is blank then username is left unchanged.
+
+1.4.13.  @iptrtpproxy.o_addr
+
+   Return address from original o= line.
+
+1.4.14.  @iptrtpproxy.o_ip_ver
+
+   Return IP version of address (i.e. 4 or 6) at o= line to set correct
+   address via iptrtpproxy_set_param(o_addr).
+
+1.4.15.  iptrtpproxy_set_param("o_addr", value)
+
+   Address to be rewritten at o= line by iptrtpproxy_alloc/update to hide
+   caller identity. If value is blank then address is left unchanged.
+
+1.4.16.  @iptrtpproxy.session_mark
+
+   Return current session mark value.
+
+1.4.17.  iptrtpproxy_set_param("session_mark", value)
+
+   If set then all session RTP packets will be market by this value. It
+   allow e.g. clone and redirect packet for legal intercept purpose.
+
+1.4.18.  iptrtpproxy_set_param("codec_set", value)
+
+   Codec set for iptrtpproxy_authorize_media. Current codec set may be
+   obtained by @iptrtpproxy.codec_set.
+
+1.4.19.  iptrtpproxy_set_param("remove_codec_mask", value)
+
+   Mask used in iptrtpproxy_authorize_media. Current mask may be obtained
+   by @iptrtpproxy.remove_codec_mask.
 
-1.4.7.  @iptrtpproxy.sdp_ip
+1.4.20.  @iptrtpproxy.auth_rights
 
-   Return first rewritten IP provided in SDP c= line.
+   Result of iptrtpproxy_authorize_media.
 
-1.4.8.  @iptrtpproxy.switchboard
+1.4.21.  @iptrtpproxy.active_media_num
 
-   Switchboard found by iptrtpproxy_find.
+   Returns number of active media streams in SDP.
+   iptrtpproxy_authorize_media may disable some streams, i.e. returned
+   value may change after authorization.
 
-1.4.9.  @iptrtpproxy.direction
+1.4.22.  @iptrtpproxy.auth_throttling
 
-   Direction determined by iptrtpproxy_find. 1..gate A->B, 0..gate B->A
+   Returns 1 if iptrtpproxy_authorize_media detected that some media
+   stream is to be throttled. In this case RTP proxy should be applied.

+ 30 - 0
modules/iptrtpproxy/doc/iptrtpproxy.xml

@@ -593,6 +593,17 @@ Aggregation will take <emphasis>sip-addr</emphasis> from the first switchboard o
 		</para>
 		</section>
 
+		<section id="iptrtpproxy.o_ip_ver">
+		<title>
+			<function>@iptrtpproxy.o_ip_ver</function>
+		</title>
+		<para>
+		Return IP version of address (i.e. <emphasis>4</emphasis> or <emphasis>6</emphasis>) 
+		at <emphasis>o=</emphasis> line to set correct address via
+		<function>iptrtpproxy_set_param(o_addr)</function>.
+		</para>
+		</section>
+
 		<section id="iptrtpproxy_set_param(o_addr)">
 		<title>
 			<function>iptrtpproxy_set_param("o_addr", value)</function>
@@ -603,6 +614,25 @@ Aggregation will take <emphasis>sip-addr</emphasis> from the first switchboard o
 		</para>
 		</section>
 
+		<section id="iptrtpproxy.session_mark">
+		<title>
+			<function>@iptrtpproxy.session_mark</function>
+		</title>
+		<para>
+		Return current session mark value.
+		</para>
+		</section>
+
+		<section id="iptrtpproxy_set_param(session_mark)">
+		<title>
+			<function>iptrtpproxy_set_param("session_mark", value)</function>
+		</title>
+		<para>
+		If set then all session RTP packets will be market by this value. It allow e.g. clone and redirect packet
+		for legal intercept purpose.
+		</para>
+		</section>
+
 		<section id="iptrtpproxy_set_param(codec_set)">
 		<title>
 			<function>iptrtpproxy_set_param("codec_set", value)</function>

+ 126 - 99
modules/iptrtpproxy/iptrtpproxy.c

@@ -104,7 +104,7 @@ struct switchboard_item_stat {
 struct switchboard_item {
 	str name;
 	struct xt_rtpproxy_switchboard_id switchboard_addr;
-	unsigned int sip_ip;
+	struct xt_rtpproxy_addr sip_ip;
 	str hostname;
 	struct host_item *host;
 	unsigned int weight;
@@ -140,7 +140,7 @@ static str sdp_media_types_str[NUM_MEDIA_TYPES] = {
 struct aggregation_item {
 	str name;
 	unsigned int switchboard_count;
-	unsigned int sip_ip;
+	struct xt_rtpproxy_addr sip_ip;
 	struct switchboard_item *(*switchboards)[];
 	struct aggregation_item *next;
 };
@@ -184,6 +184,7 @@ static struct {
 		u_int32_t mark;
 		struct xt_rtpproxy_throttle_stat bandwidth[2];
 	} throttle;
+	int session_mark;
 	struct codec_set_item *codec_set;
 	int remove_codec_mask;
 	int auth_throttling;
@@ -234,7 +235,7 @@ declare_find_function(codec_set)
 static struct switchboard_item* find_switchboard_by_addr(struct xt_rtpproxy_switchboard_id *addr) {
 	struct switchboard_item* p;
 	for (p = switchboards; p; p=p->next) {
-		if (addr->ip == p->switchboard_addr.ip && addr->port == p->switchboard_addr.port) break;
+		if (xt_rtpproxy_addr_test(addr, &p->switchboard_addr) == 3) break;
 	}
 	return p;
 }
@@ -250,10 +251,11 @@ enum {
 	PAR_SWITCHBOARD_BY_SIP_IP_A, PAR_SWITCHBOARD_BY_SIP_IP_B,
 	PAR_SESSION_IDS, PAR_PROTECTED_SESSION_IDS,
 	PAR_SDP_IP, PAR_ACTIVE_MEDIA_NUM,
-	PAR_OLINE_USER, PAR_OLINE_ADDR,
+	PAR_OLINE_USER, PAR_OLINE_ADDR, PAR_OLINE_IP_VER,
 	PAR_THROTTLE_MARK, 
 	PAR_THROTTLE_RTP_MAX_BYTES, PAR_THROTTLE_RTP_MAX_PACKETS, 
 	PAR_THROTTLE_RTCP_MAX_BYTES, PAR_THROTTLE_RTCP_MAX_PACKETS,
+	PAR_SESSION_MARK,
 	PAR_CODEC_SET, PAR_AUTH_RIGHTS, PAR_REMOVE_CODEC_MASK,
 	PAR_AUTH_THROTTLING
 };
@@ -284,12 +286,14 @@ static struct {
 	{"sdp_ip", PAR_SDP_IP, PAR_READ|PAR_INT},
 	{"active_media_num", PAR_ACTIVE_MEDIA_NUM, PAR_READ|PAR_INT},
 	{"o_user", PAR_OLINE_USER, PAR_READ|PAR_WRITE|PAR_STR},
+	{"o_ip_ver", PAR_OLINE_IP_VER, PAR_READ|PAR_INT},
 	{"o_addr", PAR_OLINE_ADDR, PAR_READ|PAR_WRITE|PAR_STR},
 	{"throttle_mark", PAR_THROTTLE_MARK, PAR_READ|PAR_WRITE|PAR_INT},
 	{"throttle_rtp_max_bytes", PAR_THROTTLE_RTP_MAX_BYTES, PAR_READ|PAR_WRITE|PAR_INT},
 	{"throttle_rtp_max_packets", PAR_THROTTLE_RTP_MAX_PACKETS, PAR_READ|PAR_WRITE|PAR_INT},
 	{"throttle_rtcp_max_bytes", PAR_THROTTLE_RTCP_MAX_BYTES, PAR_READ|PAR_WRITE|PAR_INT},
 	{"throttle_rtcp_max_packets", PAR_THROTTLE_RTCP_MAX_PACKETS, PAR_READ|PAR_WRITE|PAR_INT},
+	{"session_mark", PAR_SESSION_MARK, PAR_READ|PAR_WRITE|PAR_INT},
 	{"codec_set", PAR_CODEC_SET, PAR_READ|PAR_WRITE|PAR_STR},
 	{"remove_codec_mask", PAR_REMOVE_CODEC_MASK, PAR_READ|PAR_WRITE|PAR_INT},
 	{"auth_rights", PAR_AUTH_RIGHTS, PAR_READ|PAR_INT},
@@ -464,13 +468,14 @@ struct sdp_codec {
 
 struct sdp_session {
 	str oline_user_s;
+	char oline_ip_ver;
 	str oline_addr_s;
 	str cline_ip_s;
 	unsigned int media_count;
 	struct {
 		int active;  /* if SDP has been parsed correctly, has a IP (even 0.0.0.0), port!=0 and has supported params */
 		unsigned short port;
-		unsigned int ip;
+		struct xt_rtpproxy_addr addr;
 		str ip_s;
 		str port_s;
 		enum sdp_media_type media_type;
@@ -481,23 +486,26 @@ struct sdp_session {
 	} media[MAX_MEDIA_NUMBER];
 };
 
-static unsigned int s2ip4(str *s) {
-	struct in_addr res;
-	char c2;
-	c2 = s->s[s->len];
-	s->s[s->len] = '\0';
-	if (!inet_aton(s->s, &res)) {
-		s->s[s->len] = c2;
-		return 0;
+static int s2ip(str *s, struct xt_rtpproxy_addr *addr) {
+        char c2;
+        c2 = s->s[s->len];
+        s->s[s->len] = '\0';
+	addr->ver = (strchr(s->s, ':') != NULL)?6:4;
+	if (inet_pton((addr->ver==6)?AF_INET6:AF_INET, s->s, &addr->ip) <= 0 ) {
+		addr->ver = 0;
 	}
 	s->s[s->len] = c2;
-	return res.s_addr;
+	return addr->ver;
 }
 
-static void ip42s(unsigned int ip, str *s) {
-	struct in_addr ip2 = { ip };
-	s->s = inet_ntoa(ip2);
-	s->len = strlen(s->s);
+static void ip2s(struct xt_rtpproxy_addr *addr, str *s) {
+	static char buf[INET6_ADDRSTRLEN+1];
+	s->s = buf;
+	if (addr->ver == 0 || !inet_ntop((addr->ver==6)?AF_INET6:AF_INET, &addr->ip, buf, sizeof(buf))) {
+		s->len = 0;
+	} else {
+		s->len = strlen(s->s);
+	}
 }
 
 #define is_alpha(_c) (((_c) >= 'a' && (_c) <= 'z') || ((_c) >= 'A' && (_c) <= 'Z') || ((_c) >= '0' && (_c) <= '9') || ((_c) == '_') || ((_c) == '-'))
@@ -551,8 +559,9 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 	str line, lvalue, body;
 	int sess_fl, i, cline_count, codec_count;
 	char ltype, savec;
-	unsigned int cline_ip;
+	struct xt_rtpproxy_addr cline_ip;
 	enum send_rec_modifier sess_send_rec_modifier;
+	char cline_ver;
 
 	static struct sdp_codec codecs[MAX_CODEC_NUMBER];
 
@@ -631,7 +640,7 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 	sess->media_count = 0;
 	sess->cline_ip_s.s = NULL;  /* make gcc happy */
 	sess->cline_ip_s.len = 0;
-	cline_ip = 0;
+	cline_ip.ver = 0;
 	cline_count = 0;
 	codec_count = 0;
 	memset(&codecs, 0, sizeof(codecs));
@@ -649,12 +658,12 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 				sess_fl = 1;
 				break;
 			case 'o': 
-				/* originator & session description: o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address> */
+				/* originator & session description: o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address> 
+				   o=assistant 971731711378798081 0 IN IP6 2001:db8::20 */
 				if (sess_fl != 1) {
 					ERR(MODULE_NAME": parse_sdp_content: o= line is not in session section\n"); 
 					return -1;
 				}
-				for (i=0; i<6; i++) 
 				if (sess->oline_addr_s.s) {
 					ERR(MODULE_NAME": parse_sdp_content: only one o= line allowed\n"); 
 					return -1;
@@ -664,11 +673,20 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 				if (!sess->oline_user_s.len) goto invalid_o;
 				sess->oline_user_s.s = lvalue.s;
 				lvalue.s = eat_space_end(cp, lend);
-				for (i=0; i<4; i++) {
-						cp = eat_token_end(lvalue.s, lend);
-						if (cp-lvalue.s == 0) goto invalid_o;
-						lvalue.s = eat_space_end(cp, lend);
+				for (i=0; i<3; i++) {
+					cp = eat_token_end(lvalue.s, lend);
+					if (cp-lvalue.s == 0) goto invalid_o;
+					lvalue.s = eat_space_end(cp, lend);
 				}
+				cp = eat_token_end(lvalue.s, lend);
+				if (cp-lvalue.s != 3 || memcmp(lvalue.s, "IP", 2) != 0 ||
+				    !(lvalue.s[2] == '4' || lvalue.s[2] == '6')
+				){   /* IP4 or IP6 */
+					goto invalid_o;
+				}
+				sess->oline_ip_ver = lvalue.s[2]-'0';
+				lvalue.s = eat_space_end(cp, lend);
+
 				cp = eat_token_end(lvalue.s, lend);
 				sess->oline_addr_s.len = cp-lvalue.s;
 				if (!sess->oline_addr_s.len) goto invalid_o;
@@ -678,7 +696,9 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 				ERR(MODULE_NAME": parse_sdp_content: invalid o= line '%.*s'\n", (int) (lend-line.s), line.s);
 				return -1;
 			case 'c':
-				/* Connection Data: c=<network type> <address type> <connection address>, ex. c=IN IP4 224.2.17.12/127 */
+				/* Connection Data: 
+				   c=<network type> <address type> <connection address>, ex. c=IN IP4 224.2.17.12/127
+				   c=IN IP6 2001:db8::20 */
 				switch (sess_fl) {
 					case 0:
 						ERR(MODULE_NAME": parse_sdp_content: c= line is not in session section\n");
@@ -704,9 +724,12 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 						cp = eat_space_end(cp, lend);
 						lvalue.s = cp;
 						cp = eat_token_end(cp, lend);
-						if (cp-lvalue.s != 3 || memcmp(lvalue.s, "IP4", 3) != 0) {
+						if (cp-lvalue.s != 3 || memcmp(lvalue.s, "IP", 2) != 0 ||
+						    !(lvalue.s[2] == '4' || lvalue.s[2] == '6')
+						) {   /* IP4 or IP6 */
 							goto invalidate;
 						}
+						cline_ver = lvalue.s[2];
 						cp = eat_space_end(cp, lend);
 						lvalue.s = cp;
 						cp = eat_token_end(cp, lend);
@@ -717,11 +740,17 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 						}
 						if (sess_fl == 1) {
 							sess->cline_ip_s = lvalue;
-							cline_ip = s2ip4(&lvalue);
+							s2ip(&lvalue, &cline_ip);
+							if (cline_ip.ver != cline_ver) {
+								goto invalidate;
+							}
 						}
 						else {
-							sess->media[sess->media_count-1].ip = s2ip4(&lvalue);
-							sess->media[sess->media_count-1].active = sess->media[sess->media_count-1].port != 0;  /* IP may by specified by hostname */
+							s2ip(&lvalue, &sess->media[sess->media_count-1].addr);
+							if (sess->media[sess->media_count-1].addr.ver != cline_ver) {
+								goto invalidate;
+							}
+							sess->media[sess->media_count-1].active = sess->media[sess->media_count-1].addr.port != 0;  /* IP may by specified by hostname */
 							sess->media[sess->media_count-1].ip_s = lvalue;
 						}
 						break;
@@ -788,7 +817,9 @@ static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
 						if (name2enum(&lvalue, &supported_protocols) >= 0) {
 							sess->media[sess->media_count-1].active = sess->cline_ip_s.len != 0;  /* IP may by specified by hostname */
 							sess->media[sess->media_count-1].ip_s = sess->cline_ip_s;
-							sess->media[sess->media_count-1].ip = cline_ip;
+							sess->media[sess->media_count-1].addr.ver = cline_ip.ver;
+							sess->media[sess->media_count-1].addr.ip6 = cline_ip.ip6;
+
 						}
 						/* get payload types */
 						sess->media[sess->media_count-1].codecs = (struct sdp_codec (*)[]) (codecs + codec_count);
@@ -973,8 +1004,13 @@ static int prepare_lumps(struct sip_msg* msg, str* position, str* s) {
 static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_session *sdp_sess, struct ipt_session *ipt_sess) {
 	int i, j;
 	str s;
+	struct xt_rtpproxy_addr null_addr;
 	/* we must apply lumps for relevant c= and m= lines */
 	global_params.sdp_ip.len = 0;
+	memset(&null_addr, 0, sizeof(null_addr));
+	null_addr.ver = 4;
+// TODO: 4 or 6 ??
+
 	for (i=0; i<sdp_sess->media_count; i++) {
 		if (sdp_sess->media[i].active) {
 			if (ipt_sess->sdp_media[i] < 0) {
@@ -989,9 +1025,9 @@ static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_s
 				/* takes 1st ip to be rewritten, for aux purposes only */
 				global_params.sdp_ip = sdp_sess->media[i].ip_s;
 			}
-			if (sdp_sess->media[i].ip != 0) {  /* we won't update 0.0.0.0 to anything because such a UA cannot receive. The session may be allocated and reused (unless expires) */
+			if (sdp_sess->media[i].addr.ver != 0) {  /* we won't update 0.0.0.0 to anything because such a UA cannot receive. The session may be allocated and reused (unless expires) */
 				/* apply lump for ip address in c= line */
-				ip42s(ipt_sess->sessions[ipt_sess->sdp_media[i]].dir[!gate_a_to_b].switchboard.addr.ip, &s);
+				ip2s(&ipt_sess->sessions[ipt_sess->sdp_media[i]].dir[!gate_a_to_b].switchboard.addr, &s);
 				if (prepare_lumps(msg, &sdp_sess->media[i].ip_s, &s) < 0)
 					return -1;
 			}
@@ -1004,7 +1040,7 @@ static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_s
 	}
 	/* do topo hiding if all media are disabled for c= line then set address 0.0.0.0 to hide UA location */
 	for (i=0; i<sdp_sess->media_count; i++) {
-		if (sdp_sess->media[i].ip && (!sdp_sess->media[i].active || ipt_sess->sdp_media[i] < 0)) { /* not affected but previous loop */
+		if (sdp_sess->media[i].addr.ver && (!sdp_sess->media[i].active || ipt_sess->sdp_media[i] < 0)) { /* not affected but previous loop */
 			if (sdp_sess->media[i].ip_s.s == sdp_sess->cline_ip_s.s) {  /* the c= may be shared */
 				for (j=0; j<i; j++) {
 					if (sdp_sess->media[i].ip_s.s == sdp_sess->media[j].ip_s.s) {
@@ -1019,7 +1055,7 @@ static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_s
 				}
 			}
 			/* apply lump for ip address in c= line */
-			ip42s(0, &s);
+			ip2s(&null_addr, &s);
 			if (prepare_lumps(msg, &sdp_sess->media[i].ip_s, &s) < 0)
 				return -1;
 		cline_fixed2:
@@ -1029,12 +1065,12 @@ static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_s
 	/* do topo hiding of session c= lines which are not used in any media section */
 	if (sdp_sess->cline_ip_s.len > 0) {		
 		for (i=0; i<sdp_sess->media_count; i++) {
-			if (sdp_sess->media[i].ip && sdp_sess->media[i].ip_s.s == sdp_sess->cline_ip_s.s) {
+			if (sdp_sess->media[i].addr.ver && sdp_sess->media[i].ip_s.s == sdp_sess->cline_ip_s.s) {
 				goto cline_fixed3;
 			}
 		}
 		/* apply lump for ip address in c= line */
-		ip42s(0, &s);
+		ip2s(&null_addr, &s);
 		if (prepare_lumps(msg, &sdp_sess->cline_ip_s, &s) < 0)
 			return -1;		
 	cline_fixed3:
@@ -1232,7 +1268,7 @@ inline static void fill_in_session(int flags, int media_idx, struct sdp_session
 	for (j=0; j<2; j++) {
 		if (sdp_sess) {
 			in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_ADDR;
-			in_session->dir[GATE_A_TO_B(flags)].stream[j].source.ip = sdp_sess->media[media_idx].ip;
+			in_session->dir[GATE_A_TO_B(flags)].stream[j].source = sdp_sess->media[media_idx].addr;
 			in_session->dir[GATE_A_TO_B(flags)].stream[j].source.port = sdp_sess->media[media_idx].port+j;
 		}
 		if (global_params.learning_timeout >= 0) {
@@ -1262,6 +1298,12 @@ inline static void fill_in_session_throttle(int flags, int media_idx, struct xt_
 			in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_THROTTLE_MARK;
 		}
 	}
+	if (global_params.session_mark > 0) {
+		for (j=0; j<2; j++) {			
+			in_session->dir[GATE_A_TO_B(flags)].stream[j].mark = global_params.session_mark;
+			in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_MARK;
+		}
+	}
 	for (j=0; j<2; j++) {
 		if (global_params.codec_set && 
 		    (global_params.codec_set->media_types[global_sdp_sess.media[media_idx].media_type].throttle.bandwidth[j].packets > 0 ||
@@ -1321,8 +1363,8 @@ ERR("RTPPROXY_DEBUG: xt_RTPPROXY_update_sessions(sess#:%d, sdp#:%d, XT_RTPPROXY_
 			for (j = 0; j < i; j++) {
 				/* if two media streams have equal source address than we will allocate only one ipt session */
 				if (global_sdp_sess.media[j].active) {
-					if (global_sdp_sess.media[i].ip == global_sdp_sess.media[j].ip && global_sdp_sess.media[i].port == global_sdp_sess.media[j].port) {
-						if (global_sdp_sess.media[i].ip != 0) {
+					if (xt_rtpproxy_addr_test(&global_sdp_sess.media[i].addr, &global_sdp_sess.media[j].addr)==3) {
+						if (global_sdp_sess.media[i].addr.ver != 0) {
 							ipt_sess.sdp_media[i] = ipt_sess.sdp_media[j];
 							goto cont;						
 						} else if (i < global_params.protected_sess.sdp_media_count) {
@@ -1330,7 +1372,7 @@ ERR("RTPPROXY_DEBUG: xt_RTPPROXY_update_sessions(sess#:%d, sdp#:%d, XT_RTPPROXY_
 							k = global_params.protected_sess.sdp_media[i];
 							l = global_params.protected_sess.sdp_media[j];
 							if ((global_params.protected_sess.sessions[k].sh.flags & XT_RTPPROXY_SOCKOPT_FLAG_NOT_FOUND) == 0 &&
-							    global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == global_params.protected_sess.sessions[l].dir[GATE_A_TO_B(flags)].stream[0].source.ip &&
+							    global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == global_params.protected_sess.sessions[l].dir[GATE_A_TO_B(flags)].stream[0].source.ver &&
 							global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port == global_params.protected_sess.sessions[l].dir[GATE_A_TO_B(flags)].stream[0].source.port) {
  
 								/* if ip == 0, for example phone goes on-hold we'll take IP from protected sessions if possible */
@@ -1359,12 +1401,12 @@ ERR("RTPPROXY_DEBUG: protected.sess media:%d -> sess:%d, flags: %d\n", i, k, glo
 							can't tricle. It would not force new RTP session allocation. 
 							So we won't update SDP c= line but get sees_id from protected sess to reuse it when on-hold terminates.
 							But when on-hold is too long and session expires then new session will be allocated */
-ERR("DEBUG_RTPPROXY: module: CMP %x=%x & %d=%d\n", global_sdp_sess.media[i].ip, global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip, global_sdp_sess.media[i].port, global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port);
-							if ((global_sdp_sess.media[i].ip == 0 || 
-							     global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == 0 ||
-							     global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == global_sdp_sess.media[i].ip) &&
+ERR("DEBUG_RTPPROXY: module: CMP %x=%x & %d=%d\n", global_sdp_sess.media[i].addr.ip, global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip, global_sdp_sess.media[i].addr.port, global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port);
+							if ((global_sdp_sess.media[i].addr.ver == 0 || 
+							     global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ver == 0 ||
+							     (xt_rtpproxy_addr_test(&global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source, &global_sdp_sess.media[i].addr) & 1)) &&
 							    /* global_sdp_sess.media[i].port always because active && */  
-							    global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port == global_sdp_sess.media[i].port) {
+							    global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port == global_sdp_sess.media[i].addr.port) {
 								/* keep all reused sess at the beginning of list, i.e. make slot */
 ERR("RTPPROXY_DEBUG: REUSE!\n");
 								for (j=ipt_sess.session_count; j > 0; j--) {	
@@ -1392,7 +1434,7 @@ ERR("RTPPROXY_DEBUG: REUSE!\n");
 				}
 
 			}
-			if (global_sdp_sess.media[i].ip == 0) {
+			if (global_sdp_sess.media[i].addr.ver == 0) {
 				switch (global_sdp_sess.media[i].send_rec_modifier) {
 				case sdpaattr_sendonly:
 				case sdpaattr_sendrecv: /* it's error because it cannot receive anything but client are weird */
@@ -1665,7 +1707,7 @@ static int rtpproxy_update(struct sip_msg* msg, char* _flags, char* _session_ids
 			for (j = i+1; j < global_sdp_sess.media_count; j++) {
 				if (ipt_sess.sdp_media[j] >= 0 && global_sdp_sess.media[j].active) {
 					/* if two media streams have equal source address XOR have equal session */
-					if ( (global_sdp_sess.media[i].ip == global_sdp_sess.media[j].ip && global_sdp_sess.media[i].port == global_sdp_sess.media[j].port) ^
+					if ( (xt_rtpproxy_addr_test(&global_sdp_sess.media[i].addr, &global_sdp_sess.media[j].addr) == 3) ^
 						 (ipt_sess.sdp_media[i] == ipt_sess.sdp_media[j]) ) {
 						ERR(MODULE_NAME": rtpproxy_update: media (%d,%d) violation number\n", i, j);
 						return -1;
@@ -1874,7 +1916,7 @@ static int rtpproxy_authorize_media(struct sip_msg* msg, char* _dummy1, char* _d
 
 static int rtpproxy_set_param(struct sip_msg* msg, char* _idx, char* _value) {
 	int idx, dir;
-	unsigned int ip;
+	struct xt_rtpproxy_addr ip;
 	idx = PTR2INT(_idx);
 	union {
 		str s;
@@ -1910,12 +1952,12 @@ static int rtpproxy_set_param(struct sip_msg* msg, char* _idx, char* _value) {
 			break;
 		case PAR_SWITCHBOARD_BY_SIP_IP_A:
 		case PAR_SWITCHBOARD_BY_SIP_IP_B:
-			ip = s2ip4(&u.s);
+			s2ip(&u.s, &ip);
 			for (global_params.switchboard[dir] = switchboards; 
 			     global_params.switchboard[dir]; 
 			     global_params.switchboard[dir] = global_params.switchboard[dir]->next) {
 
-				if (ip == global_params.switchboard[dir]->sip_ip) {
+				if (xt_rtpproxy_addr_test(&ip, &global_params.switchboard[dir]->sip_ip)) {
 					global_params.aggregation[dir] = NULL;	/* invalidate aggregation */
 					return 1;
 				}
@@ -1928,18 +1970,18 @@ static int rtpproxy_set_param(struct sip_msg* msg, char* _idx, char* _value) {
 			break;
 		case PAR_AGGREGATION_BY_SIP_IP_A:
 		case PAR_AGGREGATION_BY_SIP_IP_B:
-			ip = s2ip4(&u.s);
+			s2ip(&u.s, &ip);
 			for (global_params.aggregation[dir] = aggregations; 
 			     global_params.aggregation[dir]; 
 			     global_params.aggregation[dir] = global_params.aggregation[dir]->next) {
 
-				if (ip == global_params.aggregation[dir]->sip_ip) {
+				if (xt_rtpproxy_addr_test(&ip, &global_params.aggregation[dir]->sip_ip)) {
 					global_params.switchboard[dir] = NULL;	/* invalidate switchboard */
 					return 1;
 				}
 			}
 			return -1;
-	    case PAR_THROTTLE_MARK:
+		case PAR_THROTTLE_MARK:
 			global_params.throttle.mark = u.i;
 			break;
 		case PAR_THROTTLE_RTP_MAX_BYTES:
@@ -1951,6 +1993,10 @@ static int rtpproxy_set_param(struct sip_msg* msg, char* _idx, char* _value) {
 			global_params.throttle.bandwidth[param_list[idx].id==PAR_THROTTLE_RTCP_MAX_PACKETS].packets = u.i;
 			break;
 
+		case PAR_SESSION_MARK:
+			global_params.session_mark = u.i;
+			break;
+
 		case PAR_CODEC_SET:
 			if (!(global_params.codec_set = find_codec_set(&u.s, NULL)))
 				return -1;
@@ -2067,6 +2113,10 @@ static int sel_rtpproxy(str* res, select_t* s, struct sip_msg* msg) {  /* dummy
 			if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
 			u.s = global_sdp_sess.oline_addr_s;
 			break;
+		case PAR_OLINE_IP_VER:
+			if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
+			u.i = global_sdp_sess.oline_ip_ver;
+			break;
 		case PAR_SESSION_IDS:
 			u.s = global_params.session_ids;
 			break;
@@ -2081,6 +2131,9 @@ static int sel_rtpproxy(str* res, select_t* s, struct sip_msg* msg) {  /* dummy
 		case PAR_THROTTLE_RTCP_MAX_PACKETS:
 			u.i = global_params.throttle.bandwidth[param_list[idx].id==PAR_THROTTLE_RTCP_MAX_PACKETS].packets;
 			break;
+		case PAR_SESSION_MARK:
+			u.i = global_params.session_mark;
+			break;
 		case PAR_CODEC_SET:
 			if (global_params.codec_set)
 				u.s = global_params.codec_set->name;
@@ -2140,8 +2193,7 @@ static int cfg_parse_addr(void* param, cfg_parser_t* st, unsigned int flags) {
 	val.s = buff;
 	val.len = sizeof(buff)-1;
 	if (cfg_parse_str(&val, st, CFG_EXTENDED_ALPHA|CFG_STR_STATIC) < 0) return -1;
-	*(uint32_t*)param  = s2ip4(&val);
-	if (*(uint32_t*)param == 0) {
+	if (!s2ip(&val, (struct xt_rtpproxy_addr*)param)) {
 		ERR(MODULE_NAME": parse_addr: bad ip address '%.*s'\n", STR_FMT(&val));
 		return -1;
 	}
@@ -2248,6 +2300,7 @@ static int safe_parsed_values() {
 			si->name = parse_config_vals.name;			
 			si->next = (*prev_si);
 			(*prev_si) = si;
+			PROC_DEFAULT(switchboard.addr.ver, 0);
 			PROC_DEFAULT(switchboard.addr.ip, 0);
 			PROC_DEFAULT(switchboard.addr.port, 0);
 			if (parse_config_vals.parsed.switchboard.host.len) {
@@ -2270,8 +2323,9 @@ static int safe_parsed_values() {
 			hi->name = parse_config_vals.name;
 			hi->next = (*prev_hi);
 			(*prev_hi) = hi;
-			PROC_DEFAULT(host.addr, 0);
-			PROC_DEFAULT(host.port, 0);
+			PROC_DEFAULT(host.addr.ver, 0);
+			PROC_DEFAULT(host.addr.ip, 0);
+			PROC_DEFAULT(host.addr.port, 0);
 			PROC_DEFAULT(host.request_size, XT_RTPPROXY_RPC_DEFAULT_REQUEST_SIZE);
 			PROC_DEFAULT(host.reply_size, XT_RTPPROXY_RPC_DEFAULT_REPLY_SIZE);
 			PROC_DEFAULT(host.total_timeout, XT_RTPPROXY_RPC_DEFAULT_TOTAL_TIMEOUT);
@@ -2292,7 +2346,7 @@ static cfg_option_t section_switchboard_options[] = {
 /* 1st pass */
 	{NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default},
 /* 2nd pass */
-	{"addr", .f = cfg_parse_addr, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.switchboard.addr.ip},
+	{"addr", .f = cfg_parse_addr, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.switchboard.addr},
 	{"port", .f = cfg_parse_uint16, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.switchboard.addr.port},
 	{"host", .f = cfg_parse_str, .flags = CFG_CASE_SENSITIVE|CFG_STR_PKGMEM, .param = &parse_config_vals.parsed.switchboard.host},
 	{NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default}
@@ -2309,7 +2363,7 @@ static cfg_option_t section_host_options[] = {
 	{NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default},
 /* 2nd pass */
 	{"rpc-addr", .f = cfg_parse_addr, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.addr},
-	{"rpc-port", .f = cfg_parse_uint16, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.port},
+	{"rpc-port", .f = cfg_parse_uint16, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.addr.port},
 	{"rpc-proto", .f = cfg_parse_enum, .flags = CFG_CASE_SENSITIVE, .param = protos},
 	{"rpc-request-size", .f = cfg_parse_int, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.request_size},
 	{"rpc-reply-size", .f = cfg_parse_int, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.reply_size},
@@ -2332,9 +2386,6 @@ static cfg_option_t section_dummy_options[] = {
 static int parse_section_name(void* param, cfg_parser_t* st, unsigned int flags) {
 	cfg_token_t t;
 	int ret, fl;
-	str tok;
-	char buf[MAX_TOKEN_LEN];
-
 	ret = safe_parsed_values();
 	if (ret != 0) return ret;
 
@@ -2357,31 +2408,8 @@ static int parse_section_name(void* param, cfg_parser_t* st, unsigned int flags)
 	if (ret != 0) return ret;
 	if (t.type != ':') 
 		goto skip;
-	
-	/* we need override cfg_parse_section() because of possible dash '-' in section name */
-	tok.s = buf;
-	tok.len = 0;
-	while (1) {
-		ret = cfg_get_token(&t, st, 0);
-		if (ret != 0) return ret;
-		if (t.type == ']')
-			break;
-		if (tok.len+t.val.len >= sizeof(buf)-1) goto skip;
-		memcpy(tok.s+tok.len, t.val.s, t.val.len);
-		tok.len += t.val.len;
-		
-	}
-	tok.s[tok.len] = '\0';
-
-	ret = cfg_eat_eol(st, 0);
-	if (ret != 0) {
-		return ret;
-	}
-
-	parse_config_vals.name.s = pkg_malloc(tok.len+1);
-	if (!parse_config_vals.name.s) return -1;
-	memcpy(parse_config_vals.name.s, tok.s, tok.len+1);
-	parse_config_vals.name.len = tok.len;
+	ret = cfg_parse_section(&parse_config_vals.name, st, CFG_STR_PKGMEM);
+	if (ret != 0) return ret;
 
 	if (fl==iptrtpproxy_default) {
 		if (parse_config_vals.name.len == (sizeof(SWITCHBOARD_PREFIX)-1) && strncmp(parse_config_vals.name.s, SWITCHBOARD_PREFIX, parse_config_vals.name.len) == 0) {
@@ -2453,7 +2481,7 @@ static int parse_iptrtpproxy_cfg() {
 			ERR(MODULE_NAME"parse_iptrtpproxy_cfg: host '%.*s' not found.\n", STR_FMT(&si->hostname));
 			return -1;
 		}
-		si->sip_ip = si->switchboard_addr.ip;
+		si->sip_ip = si->switchboard_addr;
 	}
 	return 0;
 }
@@ -2543,10 +2571,10 @@ static int mod_init(void) {
 		if (!si->stat) return E_OUT_OF_MEM;
 		memset(si->stat, 0, sizeof(*si->stat));
 
-		ip42s(si->switchboard_addr.ip, ips+0);
+		ip2s(&si->switchboard_addr, ips+0);
 		strncpy(buf1, ips[0].s, sizeof(buf1)-1);
 		ips[0].s = buf1;
-		ip42s(si->sip_ip, ips+1);
+		ip2s(&si->sip_ip, ips+1);
 
 		INFO(MODULE_NAME": mod_init: switchboard_name=%.*s;addr=%.*s;port=%d;sip-addr=%.*s;hostname=%.*s\n", 
 			STR_FMT(&si->name),
@@ -2559,7 +2587,7 @@ static int mod_init(void) {
 	}
 	for (ai = aggregations; ai; ai=ai->next) {
 		str ips[1];
-		ip42s(ai->sip_ip, ips+0);
+		ip2s(&ai->sip_ip, ips+0);
 		INFO(MODULE_NAME": mod_init: aggregation '%.*s';sip-addr=%.*s\n", 
 			STR_FMT(&ai->name),
 			STR_FMT(ips+0)
@@ -2574,17 +2602,17 @@ static int mod_init(void) {
 		if (!hi->stat) return E_OUT_OF_MEM;
 		memset(hi->stat, 0, sizeof(*hi->stat));
 
-		ip42s(hi->rpc_params.addr, &ips);
+		ip2s(&hi->rpc_params.addr, &ips);
 		INFO(MODULE_NAME": mod_init: host_name=%.*s;rpc-addr=%.*s;rpc-port=%d;rpc-proto=%d,request-size=%d,reply-size=%d,total-timeout=%d,udp-retry-timeout=%d\n", 
 			STR_FMT(&hi->name),
 			STR_FMT(&ips),
-			hi->rpc_params.port,
+			hi->rpc_params.addr.port,
 			hi->rpc_params.proto,
 			hi->rpc_params.request_size,
 			hi->rpc_params.reply_size,
 			hi->rpc_params.total_timeout,
 			hi->rpc_params.udp_retry_timeout
-		); 
+		);
 	}
 
 		if (!reg_codecs) {
@@ -2850,7 +2878,7 @@ static int declare_switchboard_param(modparam_t type, void* val) {
 						for (i=0; i<ai->switchboard_count; i++) {
 							if ((*ai->switchboards)[i] == si) goto aggr_found;
 						}
-						if (!ai->sip_ip) {
+						if (!ai->sip_ip.ver) {
 							ai->sip_ip = si->sip_ip;
 						}
 						ai->switchboards = pkg_realloc(ai->switchboards, sizeof((*ai->switchboards)[0])*(ai->switchboard_count+1));
@@ -2861,8 +2889,7 @@ static int declare_switchboard_param(modparam_t type, void* val) {
 						break;
 					}
 					case par_SipAddr:
-						si->sip_ip = s2ip4(&val);
-						if (si->sip_ip == 0) {
+						if (!s2ip(&val, &si->sip_ip)) {
 							goto err_E_CFG;
 						}
 						break;