peacey / split-vpn

A split tunnel VPN script for Unifi OS routers (UDM, UXG, UDR) with policy based routing.
GNU General Public License v3.0
816 stars 56 forks source link

Force local traffic for specific port through VPN #48

Closed clarkewing closed 2 years ago

clarkewing commented 3 years ago

Hi there!

Thanks for the awesome work on this package, I'm using it to set up VoIP (UniFi Talk) through a CG-NAT connection (Starlink) and it's almost working perfectly.

I've managed to get port forwarding working over a Wireguard VPN so inbound traffic works great.

However, I'm struggling a bit more with getting outbound traffic to route through the VPN. Essentially what I'm trying to do is force traffic from/to SIP and signaling ports (5060, 5061, and 6767) to go through the VPN, but leave media traffic intact.

I'm using the following rule:

FORCED_SOURCE_IPV4_PORT="both-localhost-5060,5061,6767 both-192.168.1.1-5060,5061,6767 both-10.0.0.0/8-5060,5061,6767"

Should I be doing something differently?

Also, I'd be happy to sponsor the awesome work you've been doing @peacey, are you planning on setting up GitHub Sponsors?

peacey commented 3 years ago

Hi @clarkewing,

FORCEDSOURCE* is only for routed traffic. If the traffic originates from the UDMP (localhost, UDM IP), then you cannot use that option since that option uses the PREROUTING iptables chain which local traffic does not pass through. We would need a different rule for local traffic forcing for specific ports that uses the OUTPUT iptables chain (which is what local traffic goes through).

Sorry I'm not familiar with how Unifi Talk works from a network standpoint. Can you first explain how do the packets travel for VoIP (from/to a third party device or from/to the UDMP itself) and can you please explain what device is using 192.168.1.1 and 10.0.0.0/8? It would help if you could explain the full process of how packets travel when receiving/making a call from start to finish.

You also said you added port forward rules, were these port forwards for these SIP ports (5060, 5061, 6767) and forwarding to which IP? And you added these via the script's PORTFORWARDS* option?

Once I know how the network packets travel in and out, I can tell you which iptables rules to add for testing (then afterwards I can add a local port forcing option so it's simpler with the config).

Thanks!

clarkewing commented 3 years ago

Thanks for getting back to me so quickly.

To be honest with you, I myself am still trying to figure out how UniFi Talk functions from a network standpoint. My understanding is that phones make requests to the UDMP which then in turn handles SIP communication with the SIP trunk server over ports 5060, 5061, and 6767. I added multiple IP sets to cover all my bases (192.168.1.1 is the UDMP and 10.0.0.0/8 is the subnet housing the phones).

For port forwarding, I'm using the following:

PORT_FORWARDS_IPV4="both-5060-192.168.1.1-5060 both-5061-192.168.1.1-5061 both-6767-192.168.1.1-6767"

My understanding is the following: When making an outbound call, the phone sends packets over port 5060 or 5061 to the UDMP which then sends packets to the SIP server over port 6767 (called the static signaling port). When receiving an inbound call, the SIP server sends packets over port 6767 to the UDMP which then sends packets over port 5060 or 5061 to the phone. The packets over these ports are purely used for signaling (messages which allow the phone/UDMP/SIP server to initiate calls, hang up, etc), other ports are used for actual voice media (and I want to keep those out of the VPN for better latency).

I believe that forcing local traffic over the SIP ports through the VPN should resolve the issues I am experiencing.

Let me know if I can clarify in any way!

peacey commented 3 years ago

Let's try to get outbound calling working first, since that seems the simplest. From my understanding, the only thing we need to do is make local port 6767 go out through the VPN for outbound calling (with your port forward active).

After you bring up the VPN with your current configuration, can you run this to add the rule for testing?

iptables -t mangle -A VPN_OUTPUT -p tcp --dport 6767 -j MARK --set-xmark 0x9
iptables -t mangle -A VPN_OUTPUT -p udp --dport 6767 -j MARK --set-xmark 0x9

These rules will force local traffic with destination port 6767 to go out the VPN. One thing that isn't clear is if 6767 is a source or destination port? I assume it's destination port but if it's a source port you might have to try --sport instead of --dport in the command above.

Also, I'm assuming you didn't change the mark in the VPN.conf but if you did modify the iptables commands with the correct mark.

Let's see if that allows your outbound calls to work.

Also, how come we don't need to forward the media ports?

clarkewing commented 3 years ago

Hi @peacey,

My apologies for taking so long to get back to you. I got caught up in other projects with higher priority.

Those commands work great, I actually have to use --sport as 6767 is a source port but that's perfect. Our outbound calls are working great now!

As for the media ports, we could forward these as well, to have everything go through a common IP, but this poses the issue of introducing more latency, as the RTP media traffic would have to hop through the VPN in addition to the Starlink connection. For the best VoIP experience, we chose to go the route of using Starlink CG-NAT IP for RTP because of that and it is working without issue thanks to symmetric RTP.

clarkewing commented 3 years ago

As for inbound calling, I was able to isolate the issue which causes calls to drop after 30 seconds. However, in order to resolve it, we'd have to be able to a) modify UniFi Talk's underlying FreeSWITCH config (it's hidden away and Ubiquity support has been no help) ; or b) intercept SIP messages and replace the Starlink IP in the body with the VPN IP before transmission (which I don't believe is even possible).

For now, we're going the route of just calling people back if the call exceeds 30 seconds (and looking forward to dedicated IPv6 on Starlink).

peacey commented 2 years ago

Thanks for the update @clarkewing. Glad you could get most of it to work. I'll be closing this issue now since the original problem has been fixed. But feel free to re-open it if you have any more questions!

Have a nice day!