[asterisk-users] Selective canreinvite in multi-tenant environment

John A. Sullivan III jsullivan at opensourcedevel.com
Thu Aug 27 16:09:02 CDT 2009


On Thu, 2009-08-27 at 14:23 -0400, John A. Sullivan III wrote:
> Hello, all.  In our multi-tenant environment, we would like to be able
> to use the reinvite media redirection within Asterisk for calls within a
> tenant but not between tenants.  We would like inter-tenant calls to be
> fully proxied by the Asterisk server.  I think the answer is, "we
> can't," but I thought I'd ask anyway.
> 
> I'd dearly like to remove the substantial traffic associated with
> intra-tenant traffic from the Asterisk server and reduce the
> intra-tenant latency by doing so.  However, I am very, very hesitant to
> allow our VPN connections to tenants to function as a router between
> tenants allowing one tenant to directly access phones on another tenant
> (that's not as wild as it sounds because of our use of the ISCS project
> - iscs.sourceforge.net).
> 
> Since the tenants are all connecting via VPN, we are using RFC1918
> addresses and no NAT is involved thus the canreinvite=nonat option does
> not help us.  If we set canreinvite=nonat, that will allow for
> intra-tenant direct media but, if one tenant tries to call another via
> SIP, it will redirect the media at the Asterisk level but the packets
> will be dropped at the firewall / router level (or sooner as there may
> be no route to the destination) and the call will connect but with no
> sound.
> 
> Any guidance would be greatly appreciated.  Thanks - John

Ah, I found a way - that'll teach me to say there's something one can't
do in Asterisk! However, it has turned up some unexpected behavior.
First the new problem and then the original solution.

The new problem: The documentation says the L() option prevents
reinvites.  However, this does not appear to be true.  When we dial a
SIP device with canreinvite=nonat using L(30600000) (8.5 hours), we see
the RTP traffic shunted to be directly between the end points.  If we
use something like t instead, the reinvite is not issued and traffic is
handled B2BUA.

The original solution which works well except for the above problem:
I'll explain this at two levels for the sake of those who do not need
all the details.  The inter-tenant calls are done via sip URLs which are
handled differently from the intra-tenant, i.e., within a tenant one
might dial 333 to get Jane whereas, from a different tenant, one would
dial sip:jane at mycompany.com.  The inbound uri handler simply redirects
the dial plan to the dial plan for extension 333.  This in turn calls
our common dialing macro.

We realized we could set a persistent channel variable in the uri
handler, e.g., __DOPTS, set it to some dialing option which prevents
reinvites, and then goto the extension part of the plan.

We've relegated most of the functions handled by these options to the
phones, e.g., transfer, hangup, record, so we didn't want to duplicate
functionality.  For that reason, we elected to use the L() option except
it does not seem to work as mentioned above :-(

Here are the gory details for those who actually need this functionality
rather than those just curious.

There are several issues involved.  They are mainly provoked by the
capabilities or lack thereof of the firewalls.  Some of our clients have
firewalls which are SIP aware and can handle the RTP port assignment as
part of the SIP packet flow.  Others are not SIP aware and thus must
allow high UDP ports to be open for the RTP traffic.

For those with SIP aware firewalls, we do not really need this fix.
Allowing inter-tenant traffic is no more dangerous than allowing inbound
SIP calls to phones since we can restrict it to the SIP port.  Of
course, even that is too insecure for many installations.

For those without SIP aware firewalls, we have two issues.  The first is
security - we do not want to open a raft of high UDP ports for RTP
traffic which could possible be exploited for other purposes.  The
second is NAT.  If they attempted a direct RTP conversation rather than
passing through the NAT aware Asterisk setup, the RTP traffic will not
be properly NAT'd.  Hmm . . . on second thought, that is probably not
true as I do not believe the RTP traffic contains embedded IP address
information in the data portion of the packet and the SIP signalling is
still being handled by Asterisk.  I'll have to play with that one.

In any event, we have to worry about both inbound and outbound calls.
To prevent inbound calls from being reinvited, we added the dial option
to kill reinvites (which we set in a global macro named KREINVITE) to
the inbound sip url handler.

As one would imagine, to prevent outbound SIP calls from being
reinvited, we added the ${KREINVITE} option to the outbound SIP URL
handler.

The final result looks something like this:

KREINVITE=L(30600000) ; used to kill reinvite by inserting as an option in the dial command - timeout after 8.5 hours (30,600,00 ms)

[macro-common]
; ARG1 = extension to dial
; ARG2 = extension for voice mail
; ARG3 = context for voice mail
; ARG4 = extension for followme (optional)
; ARG5 = Timeout is seconds until voice mail / followme (optional - defaults to 24)
exten => s,1,Set(TM=${IF(${ISNULL(${ARG5})}?24:${ARG5})})
exten => s,n,Dial(${ARG1},${TM},${DOPTS})
exten => s,n,Wait(0.5)
exten => s,n,Goto(s-${DIALSTATUS},1)

[z10in] ; direct inbound SIP dialing
exten => joe,1,Set(__DOPTS=${KREINVITE})
exten => joe,n,Goto(z10pub,613,1)

[dial-uri] ; Always include this last because of its broad matches
exten => _[a-zA-Z0-9].,1,GotoIf($[${SIPDOMAIN}!=pbx.mycompany.com]?:_.,1)
; non-URIs will automatically append @pbx.mycompany.com
; this logic separates mistyped extensions from valid URI attempts
exten => _[a-zA-Z0-9].,n,Macro(uridial,${EXTEN}@${SIPDOMAIN})

exten => _.,1,Answer(0.5)
exten => _.,n,Playback(im-sorry)
exten => _.,n,Wait(0.0.5)
exten => _.,n,Playback(you-dialed-wrong-number)
exten => _.,n,Wait(0.4)
exten => _.,n,Playback(vm-goodbye)
exten => _.,n,Hangup()

[macro-uridial]
exten => s,1,NoOp(Calling remote SIP peer ${ARG1})
exten => s,n,Dial(SIP/${ARG1},60,${KREINVITE})
exten => s,n,Congestion()

Hope this helps someone else.  Improvements, suggestions, and
constructive criticism welcome.  If anyone knows why we are not getting
the expected reinvite prevention from the L() option, please let me
know.  Thanks - John
-- 
John A. Sullivan III
Open Source Development Corporation
+1 207-985-7880
jsullivan at opensourcedevel.com

http://www.spiritualoutreach.com
Making Christianity intelligible to secular society




More information about the asterisk-users mailing list