Post

Replies

Boosts

Views

Activity

RTF_CLONING flag in the default route when VPN connection is active
Hey! We are investigating a problem with routes when the VPN is active on macOS, and I wonder if this is expected behavior or a bug that should be registered. I tested six different VPN providers on macOS 14.5 to ensure this problem does not only affect our product; I found out all VPN providers were affected. When a user connects to the VPN and the app calls setTunnelNetworkSettings, the operating system creates a new default route in the system: $ netstat -rn -f inet Routing tables Internet: Destination Gateway Flags Netif Expire default link#50 UCSg utun23 <---- the new default route is added here default 192.168.0.1 UGScIg en10 ... ... A quick look into the netstat manual reveals the meaning of the flags for the route (UCSg) U - RTF_UP Route usable C - RTF_CLONING Generate new routes on use S - RTF_STATIC Manually added g - RTF_GLOBAL Route to a destination of the global internet (policy hint) We are concerned about the C flag (RTF_CLONING) in particular. Let's experiment to show why we are interested in this flag. Upon connecting to the VPN, execute the following commands: $ netstat -rn -f inet | grep 185.15.59.224 # empty output $ ping wkipedia.org PING wikipedia.org (185.15.59.224): 56 data bytes 64 bytes from 185.15.59.224: icmp_seq=0 ttl=58 time=38.798 ms 64 bytes from 185.15.59.224: icmp_seq=1 ttl=58 time=37.941 ms ^C $ netstat -rn -f inet | grep 185.15.59.224 185.15.59.224 link#50 UHW3Ig utun23 10 Notice the netstat command is executed without root privileges. A new route added (thanks to the RTF_CLONING flag) to the routing table means that the unprivileged user might easily track which websites were visited by another user, even if the latter uses a VPN service for the connection. I spent some time experimenting but didn't find a way to call setTunnelNetworkSettings to add a default route without the RTF_CLONING flag. Do you know if this is the expected behavior of the operating system, or should we register a bug for that behavior? Personally, I think it affects the user's rights to privacy quite heavily, especially because the netstat command might be executed by a regular user, does not need any priviliges to run.
1
0
364
Aug ’24
Frequent sleep and wake events in the Tunnel Provider
Hi! We are investigating the power consumption of our VPN app on iOS. We noticed that while the VPN tunnel is started, we frequently experience sleep and wake events. Depending on the device it varies somewhere between 60 - 600 times during an overnight test where the device is just laying around and doing absolutely nothing. I looked into the system logs, and the wake reason is always this one: 2024-03-01 03:09:00.836588+0200 0x35e96 Default 0x0 50 7 wifid: (WiFiPolicy) [com.apple.WiFiPolicy:] System wake reason: SMC.OutboxNotEmpty smc.70070000 baseband I googled what OutboxNotEmpty means, but I only found several macOS-related topics. Interestingly, when I leave the same phone without a VPN running, I don't see the above log even once during an overnight test. I also tested a different VPN app and saw the above log in the system logs. This makes me think this is either some iOS feature I don't understand or some bug that causes frequent wakeups. I'd appreciate any feedback if this is a known issue or if we need to do something differently within our tunnel implementation.
3
1
595
Mar ’24
Rare issue with Network Extension and VPN profile on iOS platform
Hi! We are developing VPN software for the iOS platform, and our customers report a rare issue that we cannot reproduce. We seek any advice about the root cause of such a problem. On every update, we notice an increased number of customer reports saying that the tunnel process is in a "connecting" loop, and to break the loop the customer has to remove the VPN profile from the settings. As none of our testers could reproduce the issue, we have minimal knowledge to work on. What we know so far: The OnDemand rules cause the tunnel process to be restarted in the loop The tunnel process does not start at all. We have logs from our customers, and we know that the application tries to start an extension, but the extension does not start at all. Something in the operating system prevents the extension from starting. The issue reappears on every app update. My theory so far is that the profile gets broken during an update process, but we have no means of confirming that. Is this a known issue? Any advice on how could we reproduce the problem? Thank you in advance for any tips!
9
1
1.3k
Feb ’24
Unexpected behavior of `AdditionalRoutes` property in DynamicStore
Hello, I experienced a strange (and in my opinion unexpected) behavior from DynamicStore/configd. In our application we setup the routes in the system by setting AdditionalRoutes property on a specific interface to route part of the network traffic through it. The routes are set properly, but I noticed that the they are not cleared once removed from AdditionalRoutes. After a while I figured, that the problem lies in the DestinationAddress I set in AdditionalRoutes. I was using the following configuration: var newRoutes: [[String: NSObject]] = [ ["DestinationAddress": "10.0.0.1" as NSObject, "SubnetMask": "255.0.0.0" as NSObject ] ] and it resulted in a new route: 10 link#16 UCS en0 ! which was not cleared when AdditionalRoutes were reset to the original value. When I changed the DestinationAddress to: var newRoutes: [[String: NSObject]] = [ ["DestinationAddress": "10.0.0.0" as NSObject, "SubnetMask": "255.0.0.0" as NSObject ] ] both, setting and clearing routes works as expected. The only difference is changing the DestinationAddress from 10.0.0.1 to 10.0.0.1. In my opinion this incosistent behavior. Although I can understand that the system might reject 10.0.0.1 as a valid DestinationAddress for creating routes, I don't think it's correct behavior to accept such address, but never clear the routes. The full source code which might be used to verify my claims: import Foundation import SystemConfiguration let en0ServiceIPv4 = "State:/Network/Service/***/IPv4" as CFString let store = SCDynamicStoreCreate(nil, "dseditor" as CFString, nil, nil)! let originalValue = SCDynamicStoreCopyValue(store, en0ServiceIPv4) as! [String: NSObject] var newValue = originalValue print("AdditionalRoutes: \(String(describing: originalValue["AdditionalRoutes"]))") var newRoutes: [[String: NSObject]] = [ ["DestinationAddress": "10.0.0.1" as NSObject, "SubnetMask": "255.0.0.0" as NSObject ] ] newValue["AdditionalRoutes"] = newRoutes as NSObject print("newValue: \(newValue)") var result = SCDynamicStoreSetValue(store, en0ServiceIPv4, newValue as CFPropertyList) print("set new value: \(result)") sleep(3) result = SCDynamicStoreSetValue(store, en0ServiceIPv4, originalValue as CFPropertyList) print("restore old value: \(result)") Naturally, the en0ServiceIPv4 needs to be changed and the program needs to be run as root. Can you please share your thoughts, if this is an OS bug or expected behavior? If it is expected, what is the reasoning behind it?
4
0
764
Oct ’23
How to utilize NSXPCListener/Connection for IPC on iOS
Hello, I'm struggling to understand how I can use NSXPC API to communicate between two processes on iOS. The problem which I want to solve is how to pass some information from VPN tunnel to the VPN app. I'm looking at the XPC documentation and for iOS I can create NSXPCListener only with the following functions: service() anonymous() Both return NSXPCListener instance. But the question remains, how to connect to such an instance? In NSXPConnection API I see only one option for iOS: init(listenerEndpoint: NSXPCListenerEndpoint) Other constructors accepting serviceName or machServiceName are available only form macOS. I feel like the problem of communicating between two processes should be easy, but surprisingly I did find it quite difficult on iOS. Can you please advice if this is a proper API for my use case? Or maybe is there some better one, which I'm not aware of? If this is a proper API, how should I create a connection between two apps? How to pass a listener from one process to the other?
5
0
973
Jul ’23
VPN Tunnel needs to be recreated on network change
I am developing a VPN app that uses Network Extension and Wireguard protocol on Ventura (13.4). I am testing the following scenario: Connect my Mac to WiFi (en0 = index 16) and to an iPad (via USB-C) which works as hotspot (en11 = index 26). Make sure in System Settings that WiFi is before iPad hotspot in Service order Connect to the VPN client (utun3 = index 28 is created) Disable WiFi When I disable WiFi I lose access to the internet. To regain the internet access on a new interface I need to restart the tunnel completely. From a user perspective, on a Mac it seems fine, but on an iOS device it feels like suboptimal solution - the user might frequently move between WiFi/cellular connectivity and we need to reestablish VPN connection every time the default interface changes (I tested that scenario as well and it behaves exactly the same as on macOS). So far I have implemented a workaround to avoid such reconnection, and in our tunnel implementation I use good, old bind to rebind our tunnel socket (UDP) to the primary interface (en0/en11). I checked the NECP logs for more details, and in a scenario, when I don't use the custom bind, I see that some rules are created which seem to be skipped: 2023-06-27 08:53:25.277090+0200 0x43e4a5 Error 0x0 0 0 kernel: necp_socket_find_policy_match_with_info_locked: DATA-TRACE <SOCKET <private>>: MATCHED POLICY - proto 17 port <local 64002/64002 remote 0/0> <drop-all order 9001> <pid=73678 Application 66744 Real Application 66744 BoundInterface 26> (policy id=29157 session_order=2002 policy_order=10802 result=IP_TUNNEL) 2023-06-27 08:53:25.277097+0200 0x43e4a5 Default 0x0 0 0 kernel: necp_socket_find_policy_match: Socket Policy: <private> (BoundInterface 26 Proto 17) Policy 29157 Skip 10494 Result 6 Parameter 26 My application PID is 73678 and iPad hotspot network interface (en11) is 26. To me it looks like the packets which are supposed go to the en11 interface are skipped. Is this expected/desired behavior, that while changing network interfaces, the tunnel should be completely restarted?
3
0
733
Jun ’23