SIP "Contact" header behind NAT


#1

I have an Asterisk system set up inside of a NAT. I am trying to communicate with clients on the outside who have public IP’s. I can get all of the SIP messages to work correctly by setting all of the NAT params in sip.conf, but I am still experiencing one-way audio problems from the client to *.

I think that this has something to do with the fact that the “Contact” header in the SIP messages that * is sending still have my internal (192.168.2.x) address in it.

Does anyone know anything about this header and how I can get * to set it to the public IP value?

Any suggestions would be appreciated.

Thanks!


#2

You can debug SIP from Asterisk or you can use Ethereal to debug it at the phone end. Are the phones behind NAT? Do you have the appropriate ports open on your firewall?


#3

Try investigating rtp.c ast_rtp_read function, at the begining there is recvfrom call to get aproriate address whom to send to.


#4

I saw that call in rtp.c, but I am not sure that it what I am looking for. I am referring to the actual Contact: header in the SIP message. This may be what I am looking at here, but I can’t seem to grasp the concept. I assumed it was in chan_sip.c, and I was looking at the ast_sip_ouraddrfor function. To me, it looks like this is where the server is comparing IP addresses to known loacal subnets, and replacing it’s internal IP with it’s external. It just looks like this should be doing the same to the Contact and it doesn’t appear to be.

My question is, “Is this how it is supposed to work?” I always assumed that a NATed Asterisk would send out contact information as extension@public.tld.com so that it’s internal IP would never be revealed to the real world.

Is this thinking incorrect. If not, why would we ever need to send the private IP to an outside phone, since it cannot reach the server using that IP.

Thanks for pointing me to that code, soltys. I was having trouble finding where it was hidden.


#5

Yes that the way it supposed to work, when nat is enabled the address of the calee is taken from the response, after that it supposed to be put into rtp structure, but it’s not, I’m telling you, put a few debug notes in ast_rtp_read, you’ll see what’s going on and how :smile:

PS. There is one ‘catch’ inside that code, I assume it’s there on purpose. Carefully study where the rtp are send to when nat is enabled. You should find it easly, if you put a few debugging notes :smile:


#6

I see what you are looking at in rtp.c, and it may explain some oter behavior that I had noticed, but it still doesn’t look like it explains the SIP problems.

Since the SIP message appears to be built before ever getting into rtp.c, I can’t see how this affects the SIP portion of the call. I see code there to handle clients that are NATed, but I still am not grasping the SIP problem.

In my case, because the SIP messages are populated with the wrong IP, the far-end never sends any RTP to me at all, but can receive it just fine.

Am I still missing something?


#7

Try investigating lines that are close to this comment
/* Ignore if the other side hasn’t been given an address yet. */
and are related to returned null_frame, this is the cause, I hope that’t clear enough. I can’t help you more, you should see the problem.
And if that’s not it, well, you have to search sth. else. Maybe do some tcpdumping, and chceck the rtp flow.


#8

So, you’re telling me that there are RTP packets there, but they don’t make it into the call path?

If that’s the case, how do I fix it?

P.S. I’ll do a tcpdump to ensure this is what you are saying, but I think I am hearing you right.


#9

Try moving lines:
if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
return &null_frame;
after if (rtp->nat) {…}
because the first condition returns null_frame, and rtp pockets are not send.
Try changing this, if it’s not the case, I can’t help you. My problem was with the described situation. After those chagnes were made everything worked OK, but I’m currently working with my mgcp.


#10

My problem is still that RTP packets are being sent, but the far-end (client) doesn’t send any. I did some debugging on the client end and found that, upon recipt of the SIP messages, it tries to send RTP to my private address instead of the public one.

It makes sense, because my public IP never shows up in any SIP messages to the client. It is literally like Asterisk is ignoring the externip and localnet entries in sip.conf.

I see where you had the problem, and hopefully I won’t run into the same thing once I do start getting RTP from the far end, but for now I send RTP just fine, and the far end hears it.

I have decided that I am going to try to recompile everything to see if somehow one of those functions got mucked up. I’ll keep you posted on what I find.

It’s driving me crazy because I know this is working for other people.

Oh, and one question, if anyone knows the answer: When localnet is set in sip.conf, which IP in the SIP message does Asterisk look at to determine the IP of the far end? Is it just where the UDP packet comes from, or does it actually look inside the SIP headers to get this?


#11

Okay. I have finally gotten to the heart of my problem.

The SDP section of the IAM has the following values:

v=0
o=root 2498 2498 IN IP4 192.168.2.200
s=session
c=IN IP4 192.168.2.200
t=0 0
m=audio 12714 RTP/AVP 0 8 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=silenceSupp:off

The origin and connection fields have my private IP and they need to contain the public IP. Why does Asterisk use the private IP even though I have set up externip and localnet?

This is causing the far end to try to set the RTP up to the wrong IP, one that it cannot reach.

Any help would be greatly appreciated.


#12

Here are the lines from chan_sip.c that control how this message is built:

snprintf(v, sizeof(v), “v=0\r\n”);
snprintf(o, sizeof(o), “o=root %d %d IN IP4 %s\r\n”, p->sessionid, p->sessionversion, ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
snprintf(s, sizeof(s), “s=session\r\n”);
snprintf(c, sizeof©, “c=IN IP4 %s\r\n”, ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
snprintf(t, sizeof(t), “t=0 0\r\n”);

    ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
    ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));

I guess I am confused, because, to me, this looks like it is always getting the IP of the interface itself, and I don’t see anywhere where the “externip” would be substituted.

Sorry this is getting to be such a long post, but I am starting to wonder why everyone isn’t having this problem, so I figured I’d keep putting my findings here.

-Trey


#13

Done! Finally.

Explain this one.

When I had externip in a seperate .conf file and just did an #include in sip.conf, it didn’t work.

I just realized that I had never tried putting it in sip.conf directly, and it worked fine.

I hate computers…

Thanks to everyone for all their help. Make a note of this in your Asterisk@Home notebook, because this is the way that THEY recommend setting it up.

Peace!