Exclude Sockets originated by PacketTunnelProvider

So I want to route *every* packet though my software, so I set packetTunnelProvider default route to attract everything to me.

And then I terminate tcp/udp, parse the payload, and based on the parsing result, some of the sessions I send to my vpn server at a specific IP, but the rest I just open a socket to the destination that the packet was originally going to, and send the data out via that socket.

So for example lets say I get packets to both 1.1.1.1:443 and 2.2.2.2:443 via the packet tunnel provider. I terminate both and decide that I want to send 1.1.1.1:443 to my vpn server, but I send 2.2.2.2:443 on its way by opening a socket to 2.2.2.2:443 and sending the terminated payload out on that socket.

So the thing I am not clear from docs or threads is whether 2.2.2.2:443 will get routed back to my packetTunnelProvider again or not ? In the thread below, @eskimo says that "we specifically go out of our way to ensure that traffic from the packet tunnel provider does not go through any other VPN interface" --> and if thats true, thats *awesome*, thats exactly what I want. But at the same time its written that on macos thats not the case. Can someone confirm if thats the case across all apple OSes please ?

https://developer.apple.com/forums/thread/76711

Android has this call addDisallowedApplication("app-name") where I can add my own app to that list and it will basically do the same as what @eskimo is referring to above. And thats the functionality I am looking for.

The easiest thing to do here is to exclude traffic from your tunnel by using excludedRoutes for NEIPv4Route's if you are able to do that. The next thing to do is only claim a specific set of traffic for the apps that you care about with something like an NEAppRule. Otherwise if you want to send traffic to one server and the rest to another, you may want to look into something like NETransparentProxyProvider. The reason I am answering your question in this way is because examining traffic read from the virtual interface and then routing it outside of the tunnel seems like and odd thing to do. In this instance it seems like you just want to inspect traffic, so why not use a proxy.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Thanks a lot for the response Matt. So here is what I want to do
  1. I do not have any MDM, so I cant use NEAppRule, same goes for AppProxy provider (which is a bummer, I wish NEAppRule or AppProxy is supported without needing MDM)

  2. So having laid out the limitation in 1, I have no other option but to use PacketTunnelProvider. And I do not have a range of IP addresses in hand to allow/disallow - my solution is not IP address based but rather based on the results of my parsing.

I see the existence of the API createTCPConnectionThroughTunnelToEndpoint which as per the thread below has to be used if an NETunnelProvider wants to create a session which gets routed via the tunnel - so the opposite of which would make me think that if I create a "normal" tcp session, it would NOT go via the tunnel, why else would there be a special API to force it via the tunnel ?

https://developer.apple.com/forums/thread/94430

I was also going through the darwin kernel code to see if I can figure something out, but the only thing I could "guess" is that the regular routing table where I add a default route via my tunnel is different from the routing table used by the NETunnelProvider itself (it puts that app in some other scope ?). If its two seperate routing tables, then what I want would work just plain and easy, and I would assume thats what the developers would have done. I was just about to run my program under strace to see what happens underneath.

I wish just the library code for the NETunnelProvider was open source like darwin and one could then peep into it and see what underlying bsd calls these APIs are making. I even tried reaching out to some of the developers to tell me the basics of what happens underneath so I dont have to burn a lot of my time (almost a week now) doing all these googling and wierd experiments to just be able to develop an application in ios - but so far I havent had any luck getting them to answer :)
By the way, I tried this on a mac and it is NOT looping the packets back. That is, EVEN IF I advertise default route from the packetTunnel provider and then I open a plain regular tcp socket from the packetTunnel provider to some random destination (lets say 8.8.8.8), that goes out DIRECTLY, ie WITHOUT getting sucked into the packetTunnel provider app. Have not tried it on an iOS, fingers crossed that it works the same way on iOS.

So in summary, there looks to be some magic / special sauce which makes the route lookups of the packetTunnel provider originated sockets to bypass the standard global routing table - which is good, which is what I hoped for, if apple can confirm that it is a LEGIT behaviour and it will continue to be so architecturally in future, that will be comforting to know!
Routing behavior is very similar, but not the same between iOS and macOS due to things like the cellular interface on iOS. Going back to your original problem; you mentioned that:

So I want to route *every* packet though my software

And then:

And then I terminate tcp/udp, parse the payload, and based on the parsing result, some of the sessions I send to my vpn server at a specific IP, but the rest I just open a socket to the destination that the packet was originally going to, and send the data out via that socket.

This by default is proxy provider action and I would recommend looking into an NEAppProxyProvider instead of a packet tunnel.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Exclude Sockets originated by PacketTunnelProvider
 
 
Q