diff --git a/apps/sbc/SBC.cpp b/apps/sbc/SBC.cpp index 28ceabf7..da753c42 100644 --- a/apps/sbc/SBC.cpp +++ b/apps/sbc/SBC.cpp @@ -26,7 +26,6 @@ /* SBC - feature-wishlist - accounting (MySQL DB, cassandra DB) -- RTP forwarding mode (bridging) - RTP transcoding mode (bridging) - overload handling (parallel call to target thresholds) - call distribution @@ -547,6 +546,38 @@ void SBCDialog::onInvite(const AmSipRequest& req) } } + if (call_profile.rtprelay_enabled) { + DBG("Enabling RTP relay mode\n"); + rtp_relay_enabled = true; + + // force symmetric RTP? + if (!call_profile.force_symmetric_rtp.empty()) { + string force_symmetric_rtp = + replaceParameters(call_profile.force_symmetric_rtp, "force_symmetric_rtp", + REPLACE_VALS); + if (!force_symmetric_rtp.empty() && force_symmetric_rtp != "no" + && force_symmetric_rtp != "0") { + DBG("forcing symmetric RTP (passive mode)\n"); + rtp_relay_force_symmetric_rtp = true; + } + } + // enable symmetric RTP by P-MsgFlags? + if (!rtp_relay_force_symmetric_rtp) { + if (call_profile.msgflags_symmetric_rtp) { + string str_msg_flags = getHeader(req.hdrs,"P-MsgFlags", true); + unsigned int msg_flags = 0; + if(reverse_hex2int(str_msg_flags,msg_flags)){ + ERROR("while parsing 'P-MsgFlags' header\n"); + msg_flags = 0; + } + if (msg_flags & FL_FORCE_ACTIVE) { + DBG("P-MsgFlags indicates forced symmetric RTP (passive mode)"); + rtp_relay_force_symmetric_rtp = true; + } + } + } + } + m_state = BB_Dialing; invite_req = req; @@ -583,11 +614,6 @@ void SBCDialog::onInvite(const AmSipRequest& req) replaceParameters(call_profile.auth_credentials.pwd, "auth_pwd", REPLACE_VALS); } - if (call_profile.rtprelay_enabled) { - DBG("Enabling RTP relay mode\n"); - rtp_relay_enabled = true; - } - // get timer if (call_profile.call_timer_enabled || call_profile.prepaid_enabled) { if (!timersSupported()) { diff --git a/apps/sbc/SBCCallProfile.cpp b/apps/sbc/SBCCallProfile.cpp index 9478b2c4..8c939c90 100644 --- a/apps/sbc/SBCCallProfile.cpp +++ b/apps/sbc/SBCCallProfile.cpp @@ -174,6 +174,8 @@ bool SBCCallProfile::readFromConfiguration(const string& name, refuse_with = cfg.getParameter("refuse_with"); rtprelay_enabled = cfg.getParameter("enable_rtprelay") == "yes"; + force_symmetric_rtp = cfg.getParameter("rtprelay_force_symmetric_rtp"); + msgflags_symmetric_rtp = cfg.getParameter("rtprelay_msgflags_symmetric_rtp") == "yes"; md5hash = ""; if (!cfg.getMD5(profile_file_name, md5hash)){ @@ -212,6 +214,15 @@ bool SBCCallProfile::readFromConfiguration(const string& name, sdpfilter_list.size()); INFO("SBC: RTP relay %sabled\n", rtprelay_enabled?"en":"dis"); + if (rtprelay_enabled) { + if (!force_symmetric_rtp.empty()) { + INFO("SBC: RTP force symmetric RTP: %s\n", + force_symmetric_rtp.c_str()); + } + if (msgflags_symmetric_rtp) { + INFO("SBC: P-MsgFlags symmetric RTP detection enabled\n"); + } + } INFO("SBC: SST %sabled\n", sst_enabled?"en":"dis"); INFO("SBC: SIP auth %sabled\n", auth_enabled?"en":"dis"); @@ -266,7 +277,10 @@ bool SBCCallProfile::operator==(const SBCCallProfile& rhs) const { prepaid_enabled == rhs.prepaid_enabled && reply_translations == rhs.reply_translations && append_headers == rhs.append_headers && - refuse_with == rhs.refuse_with; + refuse_with == rhs.refuse_with && + rtprelay_enabled == rhs.rtprelay_enabled && + force_symmetric_rtp == rhs.force_symmetric_rtp && + msgflags_symmetric_rtp == rhs.msgflags_symmetric_rtp; if (sdpfilter_enabled) { res = res && @@ -331,6 +345,9 @@ string SBCCallProfile::print() const { res += "prepaid_uuid: " + prepaid_uuid + "\n"; res += "prepaid_acc_dest: " + prepaid_acc_dest + "\n"; res += "rtprelay_enabled: " + string(rtprelay_enabled?"true":"false") + "\n"; + res += "force_symmetric_rtp: " + force_symmetric_rtp; + res += "msgflags_symmetric_rtp: " + string(msgflags_symmetric_rtp?"true":"false") + "\n"; + if (reply_translations.size()) { string reply_trans_codes; diff --git a/apps/sbc/SBCCallProfile.h b/apps/sbc/SBCCallProfile.h index fc8ccee7..c983ab26 100644 --- a/apps/sbc/SBCCallProfile.h +++ b/apps/sbc/SBCCallProfile.h @@ -89,6 +89,8 @@ struct SBCCallProfile { string refuse_with; bool rtprelay_enabled; + string force_symmetric_rtp; + bool msgflags_symmetric_rtp; // todo: RTP transcoding mode diff --git a/apps/sbc/etc/symmetricrtp.sbcprofile.conf b/apps/sbc/etc/symmetricrtp.sbcprofile.conf new file mode 100644 index 00000000..5c12c8b6 --- /dev/null +++ b/apps/sbc/etc/symmetricrtp.sbcprofile.conf @@ -0,0 +1,18 @@ +# symmetricrtp SBC profile +# +# This implements a transparent B2BUA which relays RTP and forces +# symmetric RTP on both sides - thus being able to bridge between +# two NATed clients + +# RURI/From/To defaults: transparent +#RURI=$r +#From=$f +#To=$t + +## RTP relay +# enable RTP relaying (bridging): +enable_rtprelay=yes +# force symmetric RTP (start with passive mode): +rtprelay_force_symmetric_rtp=yes +# use symmetric RTP indication from P-MsgFlags flag 2 +#rtprelay_msgflags_symmetric_rtp=yes \ No newline at end of file diff --git a/apps/sbc/etc/transparent.sbcprofile.conf b/apps/sbc/etc/transparent.sbcprofile.conf index b0e9121e..84d446be 100644 --- a/apps/sbc/etc/transparent.sbcprofile.conf +++ b/apps/sbc/etc/transparent.sbcprofile.conf @@ -21,6 +21,14 @@ # use next_hop for replies, too? #next_hop_for_replies=yes +## RTP relay +# enable RTP relaying (bridging): +#enable_rtprelay=yes +# force symmetric RTP (start with passive mode): +#rtprelay_force_symmetric_rtp=yes +# use symmetric RTP indication from P-MsgFlags flag 2 +#rtprelay_msgflags_symmetric_rtp=yes + ## filters: #header_filter=blacklist #header_list=P-App-Param,P-App-Name diff --git a/core/AmB2BSession.cpp b/core/AmB2BSession.cpp index d637c9e6..b15ca805 100644 --- a/core/AmB2BSession.cpp +++ b/core/AmB2BSession.cpp @@ -41,7 +41,8 @@ AmB2BSession::AmB2BSession() : sip_relay_only(true), b2b_mode(B2BMode_Transparent), - rtp_relay_enabled(false) + rtp_relay_enabled(false), + rtp_relay_force_symmetric_rtp(false) { for (unsigned int i=0; i\n", media_index, r_addr.c_str(), it->port); relay_rtp_streams[media_index].setRAddr(r_addr, it->port); + if (sdp.remote_active || rtp_relay_force_symmetric_rtp) { + relay_rtp_streams[media_index].setPassiveMode(true); + } media_index ++; } + if (rtp_relay_force_symmetric_rtp) { + DBG("Symmetric RTP: forced passive mode\n"); + } else if (sdp.remote_active) { + DBG("Symmetric RTP: remote NATed, RTP stream set to passive mode\n"); + } + } bool AmB2BSession::replaceConnectionAddress(const string& content_type, @@ -924,6 +935,8 @@ AmB2BCalleeSession::AmB2BCalleeSession(const AmB2BCallerSession* caller) { a_leg = false; b2b_mode = caller->getB2BMode(); + rtp_relay_enabled = caller->getRtpRelayEnabled(); + rtp_relay_force_symmetric_rtp = caller->getRtpRelayForceSymmetricRtp(); } AmB2BCalleeSession::~AmB2BCalleeSession() { diff --git a/core/AmB2BSession.h b/core/AmB2BSession.h index d5659bd0..6257b2c1 100644 --- a/core/AmB2BSession.h +++ b/core/AmB2BSession.h @@ -217,6 +217,9 @@ class AmB2BSession: public AmSession /** flag to enable RTP relay mode */ bool rtp_relay_enabled; + /** force symmetric RTP */ + bool rtp_relay_force_symmetric_rtp; + /** RTP streams which receive from our side and are used for relaying RTP from the other side */ AmRtpStream relay_rtp_streams[MAX_RELAY_STREAMS]; @@ -242,6 +245,8 @@ class AmB2BSession: public AmSession void disableRtpRelay(); /** link RTP streams of other_session to our streams */ void setupRelayStreams(AmB2BSession* from_session); + bool getRtpRelayEnabled() const { return rtp_relay_enabled; } + bool getRtpRelayForceSymmetricRtp() const { return rtp_relay_force_symmetric_rtp; } }; class AmB2BCalleeSession; diff --git a/core/AmRtpStream.cpp b/core/AmRtpStream.cpp index 61341205..812e2723 100644 --- a/core/AmRtpStream.cpp +++ b/core/AmRtpStream.cpp @@ -342,7 +342,6 @@ int AmRtpStream::send_raw( char* packet, unsigned int length ) return length; } - // returns // @param ts [out] timestamp of the received packet, // in audio buffer relative time @@ -360,31 +359,7 @@ int AmRtpStream::receive( unsigned char* buffer, unsigned int size, if (!rp) return 0; -#ifndef SUPPORT_IPV6 - if(passive) { - // #ifdef SUPPORT_IPV6 - // struct sockaddr_storage recv_addr; - // #else - struct sockaddr_in recv_addr; - rp->getAddr(&recv_addr); - - // symmetric RTP - if ((recv_addr.sin_port != r_saddr.sin_port) - || (recv_addr.sin_addr.s_addr - != r_saddr.sin_addr.s_addr)) { - - string addr_str = get_addr_str(recv_addr.sin_addr); - int port = ntohs(recv_addr.sin_port); - setRAddr(addr_str,port); - DBG("Symmetric RTP: setting new remote address: %s:%i\n", - addr_str.c_str(),port); - // avoid comparing each time sender address - } else { - DBG("Symmetric RTP: remote end sends RTP from advertised address. Leaving passive mode.\n"); - } - passive = false; - } -#endif + handleSymmetricRtp(rp); /* do we have a new talk spurt? */ begin_talk = ((last_payload == 13) || rp->marker); @@ -534,6 +509,33 @@ void AmRtpStream::setRAddr(const string& addr, unsigned short port) #endif } +void AmRtpStream::handleSymmetricRtp(AmRtpPacket* rp) { +#ifndef SUPPORT_IPV6 + if(passive) { + // #ifdef SUPPORT_IPV6 + // struct sockaddr_storage recv_addr; + // #else + struct sockaddr_in recv_addr; + rp->getAddr(&recv_addr); + + // symmetric RTP + if ((recv_addr.sin_port != r_saddr.sin_port) + || (recv_addr.sin_addr.s_addr != r_saddr.sin_addr.s_addr)) { + string addr_str = get_addr_str(recv_addr.sin_addr); + int port = ntohs(recv_addr.sin_port); + setRAddr(addr_str,port); + DBG("Symmetric RTP: setting new remote address: %s:%i\n", + addr_str.c_str(),port); + } else { + DBG("Symmetric RTP: remote end sends RTP from advertised address." + " Leaving passive mode.\n"); + } + // avoid comparing each time sender address + passive = false; + } +#endif +} + void AmRtpStream::init(const vector& sdp_payloads) { if (sdp_payloads.empty()) { @@ -590,6 +592,8 @@ void AmRtpStream::bufferPacket(AmRtpPacket* p) } if (relay_enabled) { + handleSymmetricRtp(p); + if (NULL != relay_stream) { relay_stream->relay(p); } diff --git a/core/AmRtpStream.h b/core/AmRtpStream.h index a3dcb3b2..76afa84e 100644 --- a/core/AmRtpStream.h +++ b/core/AmRtpStream.h @@ -165,6 +165,9 @@ protected: unsigned int current_send_dtmf_ts; int send_dtmf_end_repeat; + /** handle symmetric RTP - if in passive mode, update raddr from rp */ + void handleSymmetricRtp(AmRtpPacket* rp); + void relay(AmRtpPacket* p); public: diff --git a/doc/Readme.sbc.txt b/doc/Readme.sbc.txt index 1941bb91..e027b5af 100644 --- a/doc/Readme.sbc.txt +++ b/doc/Readme.sbc.txt @@ -7,8 +7,8 @@ Overview The SBC application is a highly flexible high-performance Back-to-Back User Agent (B2BUA). It can be employed for a variety of uses, for example topology hiding, From/To modification, enforcing SIP Session Timers, -identity change, SIP authentication. Future uses include accounting, -RTP call bridging, transcoding, call distribution. +identity change, SIP authentication, RTP relaying. Future uses include +accounting, transcoding, call distribution. Features -------- @@ -16,6 +16,7 @@ Features o flexible call profile based configuration o online reload of call profiles o From, To, RURI, Call-ID update + o RTP bridging o Header and message filter o adding arbitrary headers o reply code translation @@ -24,6 +25,7 @@ Features o call timer o prepaid accounting + SBC Profiles ------------ All features are set in an SBC profile, which is configured in a separate @@ -273,6 +275,32 @@ set to transparent, the SDP is parsed and reconstructed (SDP sanity check). Codecs may be filtered out by their payload names in whitelist or blacklist modes. The payload names in the list are case-insensitive (PCMU==pcmu). +RTP relay +--------- +RTP can be bridged through the SBC. Where without rtprelay, A call would go only +with the signaling through the SBC, in rtprelay mode, the connection address in +SDP messages will be replaced to the one of SEMS, such that caller and callee +send RTP media to SEMS. SEMS then relays the RTP packets between the two sides. + +RTP relay can be enabled by setting + enable_rtprelay=yes + +The SBC detects if UAs indicate that they are behind NAT by setting a:direction=active +in SDP, and goes into passive mode until it receives the first packet from the NATed +client, from which it learns the remote address. This mechanism is called "symmetric +RTP". + +Symmetric RTP (starting in passive mode) can also be forced by setting the + rtprelay_force_symmetric_rtp=yes +sbc profile option. Symmetric RTP is enabled if rtprelay_force_symmetric_rtp +evaluates to anything other than "" (empty string), "0" or "no". + +Some ser/sip-router/kamailio/*ser configurations add flag 2 in a header P-MsgFlags +header to the INVITE to indicate forcing of symmetric RTP. With the sbc profile +option + rtprelay_msgflags_symmetric_rtp=yes +the SBC honors this and sets symmetric RTP accordingly. + Adding headers -------------- Additional headers can be added to the outgoing initial INVITE by using the @@ -388,6 +416,7 @@ Example profiles codecfilter - let only some low bitrate codecs pass replytranslate - swap 603 and 488 response code in replies refuse - refuse all calls with 403 Forbidden + symmetricrtp - RTP relay with symmetric RTP for NAT handling Dependencies ------------ @@ -406,12 +435,12 @@ x SIP authentication x session timers x maximum call duration timer - accounting (MySQL DB, cassandra DB) -- RTP forwarding mode (bridging) +x RTP forwarding mode (bridging) - RTP transcoding mode (bridging) - overload handling (parallel call to target thresholds) - call distribution - select profile on monitoring in-mem DB record -- fallback profile +x fallback profile x add headers - bridging between interfaces - rel1xx in non-transparent mode