Hi Matt @meaton , can you clarify on step 4 what the connection.startVPNTunnel(.....) object is? You kind of just pulled it out of nowhere in your explanation.
Also, can you point me to a good code example of setting up an app with a network extension for packet tunneling? I'm new to MacOS development and haven't created a VPN before, so any help you can point me to would be greatly appreciated!
Post
Replies
Boosts
Views
Activity
I've added the connection.startVPNTunnel method to my container app code after lines to create, load and set the tunnel protocol, and save preferences. However, I still cannot activate the NEPacketTunnelProvider. The error message I get when running the app is Error starting the VPN tunnel: Error Domain=NEVPNErrorDomain Code=1 "(null)" and Unable to save tunnel manager preferences. How can I get the packet tunnel provider to start running and showing me packets coming and going through the network? (I don't want to use the NEFilterData/PacketProvider because I want to use the Per-App VPN NEPacketTunnelProvider.) My code is below:
NETunnelProviderManager *tunnelProviderManager = [NETunnelProviderManager forPerAppVPN];
tunnelProviderManager.localizedDescription = @"TunnelProviderManager";
[tunnelProviderManager loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Unable to load tunnel manager: %s", error.description.UTF8String);
} else {
NETunnelProviderProtocol *tunnelProtocol = [NETunnelProviderProtocol new];
tunnelProtocol.providerBundleIdentifier = @".........";
tunnelProtocol.serverAddress = @"127.0.0.1";
tunnelProtocol.includeAllNetworks = true;
tunnelProviderManager.protocolConfiguration = tunnelProtocol;
tunnelProviderManager.enabled = true;
}
}];
[tunnelProviderManager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"Unable to save tunnel manager preferences");
} else {
NSLog(@"saved tunnel manager preferences");
}
}];
NSError *error = nil;
[tunnelProviderManager.connection startVPNTunnelAndReturnError:&error];
if (error) {
NSLog(@"Error starting the VPN tunnel: %@", error);
} else {
NSLog(@"Started the VPN tunnel");
}
Please give me guidance!
Thank you @eskimo and @meaton!! It seems like my tunnel is started and I can see the IPv4Settings in System Settings > Network > VPN & Filters.
I am curious as to where I can see the log that gets output from the network extension. I have tried using os_log.Logger, NSLog, and print statements, but when I checked the Console app on my MacOS, I don't see the entries. Right now the entry is just to tell me the startTunnel method has been entered, but eventually I want to use it to see that I can capture packets and read the packets before they go on their merry way. I've also tried writing out to a temp file instead of the log, but I also couldn't find the file after running the app. Please see my code snippet of the startTunnel method below:
let logger = Logger(subsystem: "com.yourcompany.yourapp", category: "NetworkExtension")
logger.info("Inside startTunnel method")
NSLog("Inside startTunnel method")
print("Inside startTunnel method")
// Add code here to start the process of connecting the tunnel.
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") // the dest endpoint for tunnel traffic
let ipv4Settings = NEIPv4Settings(addresses: ["1.2.3.4"], subnetMasks: ["255.255.255.255"])
let includedRoute = NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "0.0.0.0") // all routes should go through the tunnel
ipv4Settings.includedRoutes = [includedRoute]
networkSettings.ipv4Settings = ipv4Settings
setTunnelNetworkSettings(networkSettings) { error in
if let error = error {
NSLog("Error occurred in setTunnelNetworkSettings: \(error.localizedDescription)")
} else {
NSLog("setTunnelNetworkSettings complete")
completionHandler(nil)
}
}
completionHandler(nil) // notify the system that the tunnel is ready
Please advise.
@eskimo, because of the issue mentioned in this thread: https://developer.apple.com/forums/thread/692846. And also because I was able to parse the network packets so I would like to work more with the IP packets to look at network events.
(Also, is there a way to remove the green checkmark that I clicked on Herry12's reply by accident?)
Thanks, @eskimo. I've done as you suggested and submitted the bug report and enhancement requests. I also submitted another one for an API to examine open sockets to connect packets with processes. The bug numbers are below. In the meantime, I'll try other things that I can think of and hope that Apple sees value in my suggestions and take action soon. :-)
FB12894500 - for the checkmark.
FB12894964 - for the filter data provider control option.
FB12895137 - for the filter packet provider process association.
FB12895360 - for the socket API to connect IP packets with processes.
I am still not able to see log entries from the network extension in the Console app. I've enabled the Info messages under the Action menu, and I've read through the post "Your Friend the System Log". I also read the linked post, "Recording Private Data", but it doesn't look like I've tagged anything as private in my log entries. When I toggle click between Include Info Messages and Include Debug Messages in the Action menu of the Console app, I don't see anything changing even the slightest. Is that expected or is there something wrong with my Console app?
I also tried having the extension write out to file at the start of the startTunnel method with the function below (which works in my test project), but I don't see the file getting created when I run my app. Is the network extension unable to write out to file?
func writeToFile() {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
// Handle error
fatalError("Documents directory not found.")
}
let fileURL = documentsDirectory.appendingPathComponent("example.txt")
let text = "Hello, world!"
if let data = text.data(using: .utf8) {
do {
try data.write(to: fileURL)
print("Data written to file successfully.")
} catch {
print("Error writing data to file: \(error)")
}
}
}
Oh my goodness, I can see the log entries in the Console app now! Hallelujah and thank goodness!
For other newbies to macOS dev like me, here's a few tips I hope would help in following @eskimo's recommendation:
To run your test app from Finder after running it from Xcode, select Xcode "Product" menu and then select "Show Build Folder in Finder". You can double-click on your built target in the Finder popup to run it.
To see the log entries in the Console app, make sure that you click on "Start streaming" when you open up the Console app. Then you can use the search bar to filter for your logs. I kept looking for the system extension logs under Reports > system.log to no avail because it's under Devices > device_name once you start streaming log messages.
Still working on write to file....
While still working on write to file, I'm running into this issue now. Error Domain=NSCocoaErrorDomain Code=513 UserInfo={NSFilePath=<private>, NSUnderlyingError=0x600000c782a0 {Error Domain=NSPOSIXErrorDomain Code=1}}.
I read the thread https://developer.apple.com/forums/thread/63075 and added .noFileProtection to my write method call, but that didn't fix the issue. Since the network extension works as root to write to file and I want to write to /private/tmp/example.txt, why am I seeing this error?