Re: FreeBSD, IPFW and the SIP/VoIP NAT problem

From: Guido Falsi <madpilot_at_FreeBSD.org>
Date: Wed, 27 Sep 2017 13:26:24 +0200
Sorry the top posting but Before anything else I need to state a few points.

First this post is getting quite VoIP and asterisk specific, so please
direct any further replies to me at my email. We're off subject on this
list.

Also before giving specific answers I need to clarify a few details
about SIP/SDP and RTP, since, from your message it looks like you have
some misconceptions about some details. In specific how RTP connection
endpoints are negotiated and how the connections actually happen after
that. (I'll make a general description to be sure everything is clear)

SIP accepts connections on only one port (UDP usually but TCP is also
supported), 5060 by default, but can be changed. This IP:port
combination is what is registered with people wanting to call you, be it
SIP REGISTER on a provider, a DNS SRV record, a blind connection on 5060
to an IP returned by an A record or manual registration via your
provider website. (these are all method I've seen actually used, there
may be others)

After the caller connects via SIP to the called party they negotiate the
connection. The "media channel" is usually negotiated by embedding SDP
packets in the SIP exchange (it's all plain text, you can actually sniff
it from the asterisk console, and in fact it's often necessary to be
done to understand problems).

There is absolutely no rule forcing RTP to be used as the media channel
protocol, there are also others, but for phone calls that's what is
usually negotiated. But RTP is a different protocol whose only relation
to SIP is it being commonly used to actually relay the voice
communication between endpoints.

RTP is strictly defined to use UDP transport and is a monodirectional
protocol, so for VoIP communication you need two RTP channels (or
connections if you prefer), one for inbound and one for outbound.

In the SDP negotiation each party (there no differentiation between
caller and called party) will state where he expects the other party to
send it's audio. Various information will be exchanged, including
protocol audio format but most important for us IP:port.

After negotiation each party performs the connection to the other. Let
me rephrase: Your asterisk will be telling your provider to which
IP:port is should send it's UDP connection for the audio stream, so you
can't control where you are connecting (but that's not a problem, NAT
correctly handles that) you have FULL CONTROL of the UDP port range the
provider MUST use to connect to you (that is unless they are getting out
of their way with a non compliant implementation, in such a case, look
for a better provider).

That said your situation is quite easy to solve without any special
firewall configuration except redirecting a small (50-100) number of UDP
ports of your choice.

In real world situations this is quite handy since you rarely have full
control of the router/firewall and many times all you get is a basic DSL
modem/router with very limited FW/NAT functionality.

On the other hand if you actually need thousands of UDP ports(that is
thousands of simultaneous calls), most probably you also can get a
static IP address for your PBX.

On 09/26/2017 15:29, O. Hartmann wrote:
> On Tue, 26 Sep 2017 11:27:05 +0200
> Guido Falsi <madpilot_at_FreeBSD.org> wrote:
> 
> I already tried to build net/asterisk13 on my AARCH64 jail, but since I'd like
> to have the databases/postgresql96-client aboard and this specific port fails
> to build, I gave up - it is, by the way, the only port (pgsql) as far as I
> know that fails in my poudriere cross compiling jail. I did not get further,
> but I saw that it is supposed to build only for amd64 and armv6.
>  
>>
>> In such a case would you be willing to test port changes on the hardware
>> to actually check it runs?
> 
> I'd like to if the efforts are not to much time consuming - I do not have a
> working Pine64 image anymore, but I have a Pine64 with 2GB RAM at hand. Months
> ago I started playing with cross compiling world/kernel for AARCH64, but I'm
> not familiar with crochet and preparing the SD image - but willing to do. But
> beware: my home box preparing the cross cimpilation is not the fastest!
>  

Actually, I don't have ARM64 hardware available right now and no ARM64
jail right now, maybe in a not to distant future I'll have that and be
able to perform specific tests, but things standing as they are I cannot
state anything definitive on this subject.

> For the moment, the ARM based PBX should perform SoHo tasks - three, up to ten
> lines at maximum. 

So you need at most 1 port for SIP and 20 for RTP...a 50 ports range
will have space to spare.

> 
>>
>> On the other hand if you plan doing a lot of audio transcoding or some
>> video transcoding, load can get up quite fast. Compressed codecs like
>> the "simple" G729 will make your load grow relatively fast even with
>> audio transcoding. Transcoding also lowers call quality so it should
>> anyway be avoided as much as possible.
> 
> Good to know. But the PBX is more like an experiment for "at home's PBX" and,
> if applicable, later for some fellows working scientifically in field and in
> need for some small and neat equipement. Video message/streaming is not so much
> the focus on the first attempts, but if possible, welcome. if not: not so
> tragic.

For 2-3 calls I think even a PCEngine or arm64 will have horsepower to
spare, even for video calls, unless you go too fancy with resolution.

>>
>> Also load can go up if you're doing many disk operations. Monitoring and
>> saving audio for a bunch of calls can be quite heavy on disk resources
>> AND could require additional transcoding.
> 
> There are Linux fellows running Asterisk 13 on raspberry Pi3 very successfully
> and this little box has only 1GB RAM as far as I know. Why should FreeBSD fail?

What do you mean by fail? Having said I don't have such hardware, does
asterisk crash on that hardware? Does calls get lost?

If the only problem that you mean by "fail" are NAT problems with the
media path, that's common in many setups, and there are solutions.

> I know. It is configured that way and it works well with one of my providers,
> which has "ingres" calls via 10000/udp through 30000/udp. By simply doing a
> forwarding of these portranges in the IPFW rules for the NAT section, my
> firewall is open on that range! And this open range grows larger with the
> second provider, which has another range on which its RTP communications is
> attempting to establish ingress calls. 

As explained in the start of the message you are misunderstanding how
things work here. That's the range you must use to connect to them, but,
unless they are using non conforming SIP/SDP and RTP implementations,
YOU choose the port range they must connect to you.

Specifically with asterisk that range is defined in rtp.conf:

[general]
rtpstart=10000
rtpend=10050

(choose whatever you like, just avoid the low ports below 1024)

Only problem is in SDP asterisk sends a IP:port combination, putting you
own machine IP in there. That means it's putting there the LAN IP.

If your IP address is static this is easy to resolve, in pjsip.conf you
have the directives:

external_media_address=1.2.3.4
external_signaling_address=1.2.3.4
local_net=172.16.0.0/12

you can put in your transports, and sip.conf has:

localnet=172.16.0.0/12
externaddr = 1.2.3.4


With these you are telling asterisk to always announce the "1.2.3.4" IP
when asking for inbound connections to IPs outside the local net.

With dynamic IPs things are more tricky. There is no easy way to put the
correct dynamic address in those variables and changing tham on the fly
will require an asterisk restart(which is not really a big problem since
by changing IP you already lost any ongoing calls and invalidated any
existing registration).

The dynamic IP case is where STUN TURN and ICE become very handy.

ICE is quite handy, you just enable it, configure a STUN and, in
available, TURN server, and it augments the SDP communication with extra
information. It can make things work out of the box in quite complicated
situations, even with no redirections on the firewall, but it requires
support by both parties, so it could not be an option.

But, having configured rtp as above and redirected that range on the
firewall, not all is lost.

You should check the res_stun_monitor module and it's configuration.
There are free stun servers around the internet, so you should be able
to use this even if your upstream does not provide it.

I don't think it is supported by the pjsip module so in that case you
should use the old sip module.

>> Take a look at:
>>
>> net/kamailio - It is really a SIP proxy, but can parse SIP/SDP packets
>> and modify their content on the fly, allowing you to play neat tricks.
>> Requires some knowledge of the protocol and work though. (maybe you can
>> get him to punch holes in the firewall, but I have not checked if it's
>> possible)
>>
>> net/rtpproxy: this is more specific and maybe your best bet. Beware of
>> the load of proxying RTP in userland though.
> 
> Yes, I'm aware of the problemacy regarding NAT and RTP. The problem is the
> pinholing of IPFW. 

Creating dynamic holes in IPFW is not necessary in 99% of times.

As stated if you have low traffic you can just redirect a small range,
if you have high traffic you should be able to afford a dedicated IP.

Again remember that YOU control the inbound port range for RTP.

Forwarding an high range is not really that risky either, those ports
will be anyway closed on the asterisk box until it's expecting some
connection, the only problem is that forwarding them causes load on the
firewall and doing that with dynamic rules exposes you to DoS via
dynamic rule exhaustion. So especially with an high range you should use
static FW rules, not the other way round.

Also note that in a professional settings there rarely is available a
firewall with hole punching functionality on the client premises. People
who actually need and have that CAN afford an extra IP usually.

Not of lesser importance, there is no way to look at SIPS (SIP+TLS, so
encrypted packets) content without terminating the SIP connection, that
is you can't just watch inside the packets on the fly is using secure
communication.

That said if you really want to go the dynamic FW hole punching even if
it has no professional use except edge cases and is quite more
complicated, error prone and easy to break you should really check out
the two ports i Suggested, especially kamailio.

Kamailio is a SIP proxy, It will terminate your provider SIP connection
and perform all the negotiations(so it can work with secure connections
too), giving you a language in which you can check what is negotiated
and even change it. I'm not sure but I think it will also allow you to
launch scripts during it's negotiations. Such scripts could perform the
actual hole punching.

Kamailio will NEVER touch the RTP stream, so it will connect to asterisk
via SIP informing it of the incoming call and your provider while
keeping sending SIP packets to kamailio, will directly connect to
asterisk and asterisk directly to your provider for the RTP streams.

This is all perfectly standard and catered for by the SIP protocol
> 
> All correct and I'd like to go with static rules as far as I can go (on the
> other hand, I do not know how IPFW is going to be forced into using dynamic
> rulesets on this specific matter ...).
> 
> But I think the trivial concern here is the worst case in the RTP/NAT scenario:
> how to pinhole (or punchhole?) the IPFW? As I said above, one provider
> expects/send RTP from 10000/udp-30000/udp. It is easy to tell asterisk to use
> 10000-30000 via config rtp.conf. Again: as I understand outgoing connections,
> IPFW is opened "statefully" on the desired port and with symmetric RTP, media
> data flow on both ends on the same ports. But how is the incoming/ingress case
> handled? By forwarding the SIP signalling (and this means, the IPF is
> inherently opened for this port), incoming call request contains the desired
> RTP ports. In the stateful firewall case, asterisk now should try to contact
> the other side exactly via those both ports. But one of those ports is supposed
> to be the receiving port, so the asterisk could never establish a stable
> outgoing connection - and here is the problem, how to open IPFW (without
> ALG/inspecting) - or any stateful firewall? Either my understanding is
> completely bullshit or ...

Again: look at kamailio, it speaks SIP and can be used to do the inspection.

Otherwise, for the inspection solution, there is nothing ready that I'm
aware of in FreeBSD. There isn't code in IPFW able to parse SIP/SDP, nor
there is a netgraph module with such ability. You should write it
yourself. But I think they are not really needed.


I hope this helps you understand better what is going on and what are
your options. Also gives you material to study further maybe.

-- 
Guido Falsi <madpilot_at_FreeBSD.org>
Received on Wed Sep 27 2017 - 09:26:30 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:41:13 UTC