Freeswitch Activating RTP audio ICE goes to public IP (I want private)

I’ve setup a local freeswitch server to connect calls between users on the local network ONLY. I am using intercom devices (2N) and have successfully got these devices talking to each other (only by using bypass_media=true in the dial plan).

I also need a web client (the client will be on the local network or at least through a vpn) to connect a call to the intercoms. I am using JsSIP to do this. I can register the clients with no problem. When doing sofia status profile internal reg I notice a few differences between my web client and my intercoms:

Call-ID e.g., 1262650078 for intercom but g50d42cverqipsboj5nk9h for webclient
Contact: contains a userID in “” for intercom, does not for webclient. Unless I set the contact_uri in JsSIP the sip address is invalid, if I do set this it is better. Regardless the webclient’s Contact contains fs_nat=yes and a fs_path which is url encoded, contains local ip for domain instead of domain and port (none of which was sent by client explicitly). The intercoms instead show their sip url with ip:port instead of domain and do not contain any fs_path parameter, they instead end with ;transport=TCP;line=99a8be1c82a734a, where line changes with each registration.
Port: Always random with every registration and doesn’t seem to follow any limitation set with rtp-start-port and rtp-end-port.
Status: shows TCP for intercoms and webclients show WSS-NAT.

All this makes sense to me, one thing I notice is that if I use the ip address instead of the domain when registering (in uri parameter not contact_uri parameter) then the logs referencing the user change to sofia/internal/username@ip. I notice that my intercom clients are showing sofia/internal/username@ip:port. I was hoping to enter that in my client’s uri parameter and use bypass_media so that they could go direct like the intercoms do to each other. However, it’s impossible to know the port prior to registration. The javascript does get this information afterwards, is it possible to change the contact_uri without reregistering and getting a new port?

I understand that isn’t the correct way to do this. Without using media_bypass I can’t seem to get any clients to communicate. I should also note, without media_bypass I have no problem connecting calls. All SIP traffic appears to work. From JsSIP I can register, create a call, connect a call, have the intercom answer the call. The java script will even get the ‘addstream’ event to fire, albiet doesn’t seem to contain any data and this only works without media_bypass on.

With media_bypass off, using wireshark, when I connect a call between webclient and intercom, the intercom clearly sends RTP traffic to the freeswitch server. The freeswitch server then sends a STUN request out to the public ip.
stunfail
On the client side I also see the STUN Binding Request going to the freeswitch server but instead of 178 it says 154 for the number.

Another thing to note, is when connecting the call the cli logs the following:


notice the line Activating RTP audio ICE. It is sending to the public IP!
I have seen posts where users show this sending to a private ip. That would be my goal here. If this could send to the client’s private ip I think the RTP packets of the clients will actually reach each other. All clients will be behind a single private ip. So how could this ever go to multiple clients through the internet? In addition, why would I want it to if everything is local. Then you have the added complication of it’s chosen port, once again, it is not respecting the limits set in switch.conf, and it appears random every time. So how could I possibly forward all those ports or know which port needs to be forwarded to what client.

If anyone has any advice on how to get this going locally, it would be a tremendous help. The JsSIP implementation is using google stun servers, not sure if that’s part of the problem or not. I’m really hoping I don’t also have to setup a stun server. I have many limitation here with my implementation, for starters windows only. My freeswitch server is windows and I am not allowed to use any other OS. Also if anybody knows if it’s possible to have bypass_media working with WebRTC, how can I accomplish this? I would love them to just go direct instead (I assume the use of the websocket and browser’s blocking of SIP ports is why this is not doable).

Thanks,

UPDATE

I’ve now tried switching to a verto client, but have similar issues. Except I can’t even connect a call (INCOMPATIBLE_DESTINATION or NORMAL_CLEARING depending on bypass_media). In the no bypass_media case I get

4197 Drop audio Candidate cid: 1 proto: udp type: host addr: 64c94be0-9685-49e2-b2f6-b9496e63c2a9.local:56001 (not an IP address)

which I don’t understand, but looking at the freeswitch logs I can see it once again goes to public ip with random port. I’m abandoning my verto attempts because it looks like it will take too long to even get a call connected, I’m hoping I can sort out my public routing issue with the vanilla JsSIP implementation.

This is probably ACL related. “NO candidate ACL defined, Defaulting to wan.auto” Check in freeswitch/conf/vanilla/autoload_configs/verto.conf.xml at master · signalwire/freeswitch · GitHub for different candidate ACL settings

Hi, Thanks for responding!

I have tried the commands acl <ip_address> <type.auto> for each of the 4 candidates in my verto config for both the webclients private and the lan’s public ip addresses. the private’s first true is localnet.auto and the public’s first true is wan_v4 (which you would expect).

      <param name="apply-candidate-acl" value="localnet.auto"/>
      <param name="apply-candidate-acl" value="wan_v4.auto"/>
      <param name="apply-candidate-acl" value="rfc1918.auto"/>
      <param name="apply-candidate-acl" value="any_v4.auto"/>

This is in my default-v4 profile in verto config. So if it’s not hitting true for any of these does that mean the ip it checks is not either the private or the public ip of the webclient? Is there a variable I can output during the dialplan to troubleshoot what it’s comparing here? You can probably tell I’m very new to FS, I started last week and on a tight deadline to get these calls working.

Thanks,

UPDATE

I’ve explored this feature more. I have created an acl named “localnet” as follows:

<list name="localnet" default="allow">
    <node type="allow" cidr="192.168.1.0/24"/>
</list>

When I test this with the acl 192.168.1.155 localnet, it returns true. Inside internal.xml I changed the apply-inbound-acl to localnet. I have left local-network-acl to localnet.auto. In verto config I have removed all apply-candidate-acl except for localnet.auto. I have tried replacing this with just localnet. All cases result with a log that says set(outside_call=true) and my dialplan finishes with nothing reached.

I will also show the following debug logs after the NO Candidate error:

dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [WARNING] switch_core_media.c:4155 NO candidate ACL defined, Defaulting to wan.auto
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4211 Save audio Candidate cid: 1 proto: udp type: host addr: 192.168.1.107:56014
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4211 Save audio Candidate cid: 1 proto: udp type: srflx addr: <publicip>:56014
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4256 Searching for rtp candidate.
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4265 Choose rtp candidate, index 1, 71.203.154.90:56014
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4000 sofia/internal/webclient1@idsoftware.us choosing family v4
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4276 Choose same candidate, index 0, for rtcp based on rtcp-mux attribute <publicip>:56014
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4328 setting remote audio ice addr to index 1 <publicip>:56014 based on candidate
dca0f89c-f563-47f4-a510-7cf8e993a197 2024-11-19 14:55:11.106504 94.92% [DEBUG] switch_core_media.c:4363 Setting remote rtcp audio addr to <publicip>:56014 based on candidate

This shows it is receiving my webclients private ip, yet it still decides to forward it to public.

I can’t comment on the ACL stuff as I have never figured out their full effect; a matching ACL does seem to affect how registration is performed, I think.

Anyway, the FreeSWITCH dialplan can test on any channel variable. For your testing you might find it helpful to create a dialplan extension that includes
<action application="info"/>
and then call it, which will dump out every last channel variable that FS knows about on that call; be ready to monitor via fs_cli in real time. Find out what you need and go from there.

HTH

Bote

Thanks for the reply,

I’m aware of the very useful info application and have used it extensively. Thanks for the tip though!

I’m starting to think that ACL is not my issue and that maybe I shouldn’t even be using a STUN server in my JsSIP config.

Can anyone answer me this: Is it possible to do direct traffic from WebRTC client to SIP client through local network?

FINAL UPDATE

I could not get this working locally. To get my webclient to connect I had to use my own turn server. (I used a windows version of coturn). The turn server had to listen on a port through my public ip which I needed to forward on my router. I then had to solve a JsSIP problem which would take 40 seconds to connect. Using JsSIP’s “icecandidate” event once I received a candidate with protocol “tcp” (my 3rd and final candidate) I executed the candidate.ready() method. Connections now occur right away but all stun traffic is going through the internet! I tried to get my turn server going locally but it would not function this way.