Summary:ASTERISK-00100: [patch] Cisco-like NAT trick for outbound SIP connections
Reporter:John Todd (jtodd)Labels:
Date Opened:2003-08-14 15:55:30Date Closed:2011-06-07 14:05:29
Versions:Frequency of
Environment:Attachments:( 0) 104_withoutpatch.txt
( 1) 104.txt
( 2) chan_sip.c.1.259.diff
( 3) chan_sip.c.1.274.diff
( 4) sip.conf.diff
Description:Feature request to implement the Cisco-ish NAT workaround tricks for Asterisk, such that on a peer-by-peer basis Asterisk can work when behind a NAT firewall


The Cisco 79xx and ATA-186 devices have an especially nice feature for handling NAT, which is implemented in Asterisk if a SIP peer has "nat=1" set.  When a Via: header is received by a Cisco device, and the first address in this chain seems to have a different address than the device sees on it's local ethernet (and the appropriate settings are configured in the Cisco) then the device will un-register the last request, and re-register but put the IP address in the Via: header in it's outbound transmission.  The Cisco assumes that the Via: header contains the "outside" IP address of a NAT, so it re-registers and makes all further communications with that address.  

Asterisk currently doesn't work well (at all?) behind NAT firewalls talking to SIP proxies that are "upstream" in a REGISTER or INVITE method.  Turning the existing code around and implementing the same trick for outbound SIP REGISTER and INVITE statements might be a quick fix for this.   However, I am probably simplifying things to a large degree.  This is not a vital feature in my opinion, since I am of the religion that servers should be on "real" IP addresses, but with much of the world on NAT address space, this feature may be of considerable use to a large number of potential Asterisk users who are stuck behind cable modems or DSL routers.

Maybe an option in the REGISTER statement that flags the remote server as being behind a NAT server?  I don't know if the existing "nat=1" behavior of SIP peers would need to be modified, but I suspect it would.  This perhaps would be a good time to collapse the "register =>" lines into the same config file areas as the rest of the SIP settings for each peer, so that these kind of global settings would work against all SIP requests to a peer.
Comments:By: scaredycat (scaredycat) 2003-08-15 08:08:59

I would add that if * is going to get anywhere near the home pbx market that this needs to be addressed... There are 1000's of users out there automatically excluded from using * .. not everyone has the benefit of multiple ip addresses to enable them to put * on the outside...

By: scaredycat (scaredycat) 2003-08-16 10:07:02

I did post a possible solution to the mailing list before this bug appeared in here. That was to add another config variable to sip.conf, like EXTERNIP= . If this variable were present in the config then * would re-write the packets so that the Record-Route/Route address was that external ip.

My only concern there is that you could put any ip in there, and if you had an evil twin they could use it maliciously...

edited on: 08-16-03 09:52

By: John Todd (jtodd) 2003-08-16 12:39:57

The "EXTERNIP=" solution is a sub-optimal method for doing this.  What about people whose external IP address changes every few hours?  How about DHCP?  What about those connections which are going through very distant NATs, and you can't tell what IP address you are being given easily?  The only way to solve that is to use the trick similar to what Cisco does with the Via: header, or possibly to use STUN.  I like the Via: header since it works with the perceived IP address that the remote server sends back, not the "assumed" external IP address that STUN replies contain.

By: Mark Spencer (markster) 2003-08-16 15:40:28

Can you provide a sample call flow?

By: scaredycat (scaredycat) 2003-08-17 07:06:32

I agree that the EXTERNIP= is not an optimal solution, but given that it may take some time to get this sorted, at least some people would get the benefit. Yes, ideally go the whole hog, but what about an interim solution - Mind you having seen Mark at work, he'd probably have it all sorted out in 10 minutes :)

By: chriz (chriz) 2003-08-19 18:17:21

make it externalFQDN ... then you can put in an dynamicDNS Service ... or better: let people choose, between externalFQDN and EXTERNIP .... of course asterisk would have to check once in a while wether externalFQDN has changed.
Certainly not an ideal solution, but a pragmatical.

By: Mark Spencer (markster) 2003-08-20 02:03:31

How about someone just provide a sample call flow where we get the info we're supposed to use?

By: John Todd (jtodd) 2003-08-20 03:20:15

I don't think any of the DNS or hardcoded IP address methods are the correct answer, and will probably do more to confuse issues.

Here is the method that the Cisco ATA and 7960 devices use to automatically figure out what their "external" IP addresses are, outside the NAT behind which they appear to be sitting from the perspective of a registration server.  Note that the ability to turn this feature on and off is important, I think, and should be on a per-REGISTER line.


In release 2.14, you may leave the NAT IP address at the default value
of "0" or "" and let the ATA automatically scan the Via header
for a "received=" parameter when a message is received. The parameter,
if present, would indicate to the Cisco ATA 186 that it is operating
behind a firewall.

The Cisco ATA 186 will then proceed as follows:

  * If the "received=" parameter is in an INVITE response, the current
    INVITE is canceled and a new INVITE is sent with the new IP
    address extracted from the "received=<NAT IP address>" parameter
    in the Contact and SDP headers.

  In addition, the ATA will cancel all previous registrations and
  re-register with the new IP address in the Contact header. This step
  is performed only if there is no on-going registration; in other
  words, only if registration is currently in an idle state.

  * If the "received=" parameter is in a REGISTER response as a result
    of a REGISTER command, the ATA will cancel all previous
    registrations and re-register with the new IP address extracted
    from the "received=<NAT IP address>" parameter in the Contact header.

  For the Cisco ATA 186 to automatically detect its presence behind a
  NAT, the SIP proxy server or remote user agent server MUST include
  the "received=" parameter in the Via header in the responses to the
  Cisco ATA 186 if it detects that the source address and port do not
  match those in the Via header.

JT's notes:  Simplified, this means that if the UA (in our case, Asterisk) sees a reply back from a REGISTER request, and the IP address in the first Via: header has a recieved= setting that is DIFFERENT from any of the IP addresses on local interfaces, then this particular registration server must be on the other side of a NAT.  The value following "received=" is the IP address at which the registration server believes us to be.  Thus, un-register the old address (which we know to be bogus) and re-register with the "outside" address that we got from the received= header.  I'm just saying what the Cisco docs say above, so I'll stop now.  ;-)

By: scaredycat (scaredycat) 2003-08-20 06:14:20

Just as a note, someone who I'd been helping out said that functioning behind NAT [at least register] did appear to work when they used the Debian version grabbed by using apt-get. How old is that version? Perhaps taking a look at that may help establish what has changed in chan_sip to break it...

Just a thought...


edited on: 08-20-03 05:57

By: Mark Spencer (markster) 2003-08-20 21:13:25

Okay again, can someone *please* provide an example of the ATA registering through NAT with this behavior for me to look at as an example?

By: scaredycat (scaredycat) 2003-08-21 05:24:05


I don't have an ata186, but I have asked a user who does to send me a) the ata186 log b) a capture using ethereal. I can send you the log now and the ethereal log shortly.. Can I have your email addy...

[the ata186 is BIG so I don;t want to paste it here...]


edited on: 08-21-03 05:07

By: zoa (zoa) 2003-09-21 13:03:03

i think the sipphone.com server (with the grandstream phones) has a similar solution.
If i use 2 phones on the same nat, they will invite each other.
If i use 2 phones on different nats, they will not invite each other.

By: Olle Johansson (oej) 2003-09-29 08:11:05

To start from a different angle:
Xten and my Cisco ATA is able to use an "outbound SIP proxy" for all outgoing SIP requests. Like a WEB proxy, the SIP client when configured to use a proxy, sends all requests to the proxy, regardless of domain and anything - it's up to the proxy. A proper term would be a SIP proxy proxy.. :-)

I would like to be able to configure an default outbound SIP proxy for ALL outbound SIP connections or one per SIP peer. For Free World Dialup I use fwdnat.pulver.com:5082 where a I believe Jasomi proxy keeps the NAT session open by regurlarly sending SIP Option requests to the client. This is my guess of
how FWD handles a client inside the NAT.

To summarize:
* Add an option for a default "outbound SIP proxy"
* Or, if the default proxy isn't configured in [general], an option for the client/peer configuration so that we can configure this for Free World Dialup when we're using Asterisk as our SIP client to FWD.

By: John Todd (jtodd) 2003-09-29 19:43:13

oej: I don't think this should replace the relatively easy to implement SIP Via: header examination and re-registration.  I think that a "sip proxy proxy" would be best requested as a separate feature request.  Can you please copy your notes to a new ticket?  I like the idea and it may solve SOME instances where * is behind a NAT, but it is truly a different request.  It just moves the responsibility for being "smart" to a different server somewhere else.

By: Olle Johansson (oej) 2003-10-05 15:13:17

jtodd: Done. See http://bugs.digium.com/bug_view_page.php?bug_id=0000359

By: ww (ww) 2003-10-30 14:35:41.000-0600

I just uploaded my patch for this issue. Sorry, it doesn't use STUN or
similar magic, just static config in sip.conf. I just realized that it
may not get the VIA header right though -- I didn't touch that part of
the code.

By: x martinp (martinp) 2003-11-03 16:01:52.000-0600

externip keyword has been implemented in sip.conf in general section.
Check the asterisk/configs/sip.conf.sample

Hope that's enough for this.

By: John Todd (jtodd) 2003-11-05 03:19:43.000-0600

I have re-opened this bug, since the resolution is, while partially effective, not anything close to what is required for a real behind-NAT solution.

The externip= flag is not really sufficient, and is terribly crude at best.  At worst, it prevents use of SIP phones.   See the note by Robert L Mathews (http://lists.digium.com/pipermail/asterisk-users/2003-November/025798.html)

The "netmask" option (missing in the resolution on this bug) is also a kludge, though it gives slightly more un-brokenness.  William Waites' patch (http://lists.digium.com/pipermail/asterisk-dev/2003-October/002150.html) has promise if I can't manage to explain the right way to solve the problem below.

However, NEITHER of these methods are anywhere nearly as flexible and elegant as using the Via: received= headers to determine what the remote SIP peer thinks of the IP address you're coming from.   Using the Via: headers allows EVERY SIP endpoint to be behind a DIFFERENT address masquerade, through any number of levels of NAT.  This permits you to have as many "inside" and "outside" NAT situations as you want, it removes ALL configuration except for "nat=yes" or "nat=no", and is compliant with the way that almost every other SIP platform on the planet expects to handle NAT (Grandstream, Cisco, SER, Vocal, etc.)  Just read the notes, and it should become clear.

For reference, I include an extremely short sip debug of a REGISTER example, which was successful, of an ATA-186 to Asterisk.   You'll note that while the ATA thinks it's address is, it can ASCERTAIN that it's external address is as it appears to the Asterisk server, because the received= parameter in the Via: line is set to the PERCEIVED address that Asterisk sees the requests coming from.  This should be the case both on INVITEs and on REGISTERs, so the client will do the right thing.

Now, interestingly, the ATA-186 doesn't do what I think Asterisk should do.  I think Asterisk, upon receiving a received= address that is different than it's  actual interface address, should cancel the previous registration that it just sent out and re-register with the received= address in the Contact: field. = My * server  = my NAT gateway outside address   = internal address of ATA-186

Sip read:
Via: SIP/2.0/UDP
From: <sip:2204@;user=phone>;tag=3733975406
To: <sip:2204@;user=phone>
Call-ID: 3219922777@
Contact: <sip:2204@;user=phone;transport=udp>;expires=90
User-Agent: Cisco ATA 186  v2.16 ata18x (030401a)
Content-Length: 0

9 headers, 0 lines
Using latest request as basis request
Sending to : 5060 (non-NAT)
Transmitting (NAT):
SIP/2.0 100 Trying
Via: SIP/2.0/UDP;received=
From: <sip:2204@;user=phone>;tag=3733975406
To: <sip:2204@;user=phone>;tag=as150bfac7
Call-ID: 3219922777@
User-Agent: Asterisk PBX
Contact: <sip:2204@>
Content-Length: 0

Transmitting (NAT):
SIP/2.0 407 Proxy Authentication Required
Via: SIP/2.0/UDP;received=
From: <sip:2204@;user=phone>;tag=3733975406
To: <sip:2204@;user=phone>;tag=as150bfac7
Call-ID: 3219922777@
User-Agent: Asterisk PBX
Contact: <sip:2204@>
Proxy-Authenticate: Digest realm="asterisk", nonce="3d064228"
Content-Length: 0

Sip read:
Via: SIP/2.0/UDP
From: <sip:2204@;user=phone>;tag=3733975406
To: <sip:2204@;user=phone>
Call-ID: 3219922777@
Contact: <sip:2204@;user=phone;transport=udp>;expires=90
User-Agent: Cisco ATA 186  v2.16 ata18x (030401a)
Proxy-Authorization: Digest username="2204",realm="asterisk",nonce="3d064228",uri="sip:",response="38424065bff76d9e7e99896979b41cf8"
Content-Length: 0

10 headers, 0 lines
Using latest request as basis request
Sending to : 5060 (NAT)
Transmitting (NAT):
SIP/2.0 100 Trying
Via: SIP/2.0/UDP;received=
From: <sip:2204@;user=phone>;tag=3733975406
To: <sip:2204@;user=phone>;tag=as150bfac7
Call-ID: 3219922777@
User-Agent: Asterisk PBX
Contact: <sip:2204@>
Content-Length: 0

Transmitting (NAT):
SIP/2.0 200 OK
Via: SIP/2.0/UDP;received=
From: <sip:2204@;user=phone>;tag=3733975406
To: <sip:2204@;user=phone>;tag=as150bfac7
Call-ID: 3219922777@
User-Agent: Asterisk PBX
Expires: 90
Contact: <sip:2204@>;expires=90
Date: Wed, 05 Nov 2003 08:56:09 GMT
Content-Length: 0

11 headers, 2 lines
Reliably Transmitting:
NOTIFY sip:2204@ SIP/2.0
Via: SIP/2.0/UDP;branch=z9hG4bK26e6cd47
From: "asterisk" <sip:asterisk@>;tag=as19531f11
To: <sip:2204@>
Contact: <sip:asterisk@>
Call-ID: 4087e3c04a9085bb25cf816c754597ad@
CSeq: 102 NOTIFY
User-Agent: Asterisk PBX
Event: message-summary
Content-Type: application/simple-message-summary
Content-Length: 36

Messages-Waiting: no
Voicemail: 0/8
(NAT) to
Sip read:
SIP/2.0 200 OK
Via: SIP/2.0/UDP;branch=z9hG4bK26e6cd47
From: "asterisk" <sip:asterisk@>;tag=as19531f11
To: <sip:2204@>;tag=3733975406
Call-ID: 4087e3c04a9085bb25cf816c754597ad@
CSeq: 102 NOTIFY
Server: Cisco ATA 186  v2.16 ata18x (030401a)
Content-Length: 0

8 headers, 0 lines

By: ww (ww) 2003-11-05 11:23:54.000-0600

Just an aside, I haven't analysed that trace in any depth yet,
but something to keep in mind. From RFC1918:

  "Indirect references to such addresses should be contained within the
  enterprise. Prominent examples of such references are DNS Resource
  Records and other information referring to internal private
  addresses. In particular, Internet service providers should take
  measures to prevent such leakage."

Though RFC1918 was written before SIP, it would appear to be another
example of such leakage.

I would think that, in an ideal world, no RFC1918 addresses should be
visible ANYWHERE in a SIP packet that is out on the public Internet.
That is, not in the VIA header, not in the Call-ID, and certainly not
in the To/From/Contact headers.

But a strict implementation like this would be very difficult to say
the least.

Just a few thoughts...

edited on: 11-05-03 11:29

By: ww (ww) 2003-11-16 15:56:02.000-0600

FYI, you should have recieved the disclaimer from NTG Clarity and my
patch may now be imported if desired.

By: ww (ww) 2003-11-26 12:48:39.000-0600

Here is a version of the patch pulled up to -current
CVS (1.249)

By: Leif Madsen (lmadsen) 2003-11-27 15:46:33.000-0600

When applied to chan_sip.c verison 1.249 this patch works great behind a Linux based NAT/FW router.  All I had to do was to open the firewall ports and forward to the internal box.

Will give this patch a shot on a newer version of chan_sip.c to verify it works with the newest CVS as well.

By: Leif Madsen (lmadsen) 2003-11-27 17:32:09.000-0600

Just tried with chan_sip.c version 1.258 and still works fine.

By: Mark Spencer (markster) 2003-11-27 22:42:09.000-0600

Please maintain syntactic compatibility with externip

how about:


fair enough?

By: ww (ww) 2003-11-27 23:45:13.000-0600

ok, here you go. new diff

By: Leif Madsen (lmadsen) 2003-12-03 03:28:28.000-0600

Just patched with the new 1.259 patch and everything is working great here.  Someone else on the mailing list also said it's working for them as well.  I really can't see why this can't go into CVS now.

By: lleto (lleto) 2003-12-03 18:41:12.000-0600

That was me. I just patched chan_sip.c version 1.259 with the new diff. Changed the sip.conf and it is still working fine. Except that I have a one way voice issue with my SIP clients. The clients (Sjphone and SIPPS) are able to register at * and when I make a call to a remote number via chan_i4l I'm able to receive sound. I know it's not the mic, because DIAX running on the same PC is fine (both sending and receiving audio). Probably it's my config or something, I've posted my sip.conf to the list.

By: balaji (balaji) 2003-12-27 02:56:13.000-0600

i tried this on 1.265 and it failed with the following error.

chan_sip.o(.text+0x16337): undefined reference to `ast_softhangup'
chan_sip.o(.text+0x1636c): undefined reference to `ast_log'
chan_sip.o(.text+0x163ab): undefined reference to `pthread_cancel'
chan_sip.o(.text+0x163be): undefined reference to `pthread_kill'
chan_sip.o(.text+0x163d1): undefined reference to `pthread_join'
chan_sip.o(.text+0x16418): undefined reference to `ast_log'
chan_sip.o(.text+0x164b8): undefined reference to `ast_log'
collect2: ld returned 1 exit status
make: *** [chan_sip.so] Error 1

By: tangent (tangent) 2004-01-03 20:20:27.000-0600

I see that this patch hasn't made it into cvs yet since it needs 'more testing'... I applied the chan_sip.c.1.259.patch against todays CVS and it's working great for me. One happy tester here :)

By: zond (zond) 2004-01-06 20:25:30.000-0600

I would greatly appreciate if anyone interested in SIP and NAT had a look at my mail http://lists.digium.com/pipermail/asterisk-dev/2004-January/002628.html and sent any comments to either zond@troja.ath.cx or to the asterisk-dev list.

By: zond (zond) 2004-01-06 20:46:49.000-0600

By the way - I must say that I agree completely with jtodd regarding the beauty of the Via-header solution. Why even consider using inferior solutions when the Via-solution is so much more elegant?

Automatic detection of NAT by any device is a Good Thing (tm) and as far as I can see it should even be the default behaviour (even though I havent thought that through completely yet).

By: mochouinard (mochouinard) 2004-01-07 14:30:41.000-0600

Ok problem with this, Once I installed all the # I dial from my SIP always dial the same number !!!

Also the phone still doesn't work being a NAT.

By: ww (ww) 2004-01-11 02:14:20.000-0600

new patch, still against 1.259. had an extra section unintentionally.

By: Brian West (bkw918) 2004-01-12 19:40:15.000-0600

New paches doesn't break the hold button on the 7960 so its good! :)

By: jerjer (jerjer) 2004-01-12 19:43:39.000-0600


By: ww (ww) 2004-01-12 23:33:35.000-0600

config file example