NetworkExtension does not come up after bringing it down with ifconfig utun down

We are developing a PacketTunnel provider based System Extension that creates a utun device when tunnel settings are supplied and everything works fine. Now, when we try to bring down the utun created by our extension using ifconfig utun down, the extension does not receive packets anymore. So the first question is, is there a way for extension to know that its been marked down? We would want to display a status to our UI to users in case they forget about it.

Next is, when we mark the utun device up next using ifconfig utun up, it still does not receive any packets. It stays down. The only way to start reading the packets is to start the tunnel again where we re-supply the tunnel settings. Is this expected behaviour? Is there anything missing here?

So the first question is, is there a way for extension to know that its been marked down? We would want to display a status to our UI to users in case they forget about it.

Marking an interface up or down with the ifconfig utility on macOS seems like a divergence from the API. To answer your question, I suspect that if you mark the virtual interface as down from the Terminal, nothing will happen in the NEPacketTunnelProvider other than not receiving packets. If you use the stopTunnel API from the container app, the this will give your app the opportunity to react.

Regarding:

Next is, when we mark the utun device up next using ifconfig utun up, it still does not receive any packets. It stays down.

To be honest this doesn't surprise me because of what I mentioned above, the provider and system state are out of sync. There may be some information in the console as to what is happening but in general I would recommend controlling this behavior from either your container app or Network System Extension.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Thanks Matt. In console logs I do not see any error or unexpected log. Here is a screenshot of logs when bringing the interface down: https://ibb.co/7gz331J and here is a screenshot of when bringing it up: https://ibb.co/kyNyTGV

I would recommend controlling this behavior from either your container app or Network System Extension

As per our design, we are providing users with control from container app. However, we noticed some (geeky) users are bringing our virtual interface down, then bringing it back up using ifconfig and reporting that our packet tunnel is not working anymore.

So, if there is a way for our container app or system extension to be notified when the interface is brought down/up, we could react to it by setting the tunnel settings again and bringing up the virtual interface. Is that at all possible? If yes, how?

To be honest this doesn't surprise me because of what I mentioned above, the provider and system state are out of sync. 

When bringing any other physical interface down and back up, it starts working fine in few seconds. So why this issue only with packet tunnel providers? Shouldn't system keep the state in sync like any other interface? Provider itself is not informed about this change anyways.

When bringing any other physical interface down and back up, it starts working fine in few seconds. So why this issue only with packet tunnel providers? Shouldn't system keep the state in sync like any other interface? Provider itself is not informed about this change anyways.

If you observe changes to the VPN via NEVPNStatus does this changes things at all in the container app? Are you able to get updates there?

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

In container app we are observing for both NEVPNConfigurationChange and NEVPNStatusDidChange notifications. But we do not receive any update there. Probably unrelated but only callback or update we receive is in path update handler of NWPathMonitor but path remains satisfied with same interface.

Okay, what about using the SCDynamicStoreRef set of APIs on macOS (note this is not available on iOS) to query the set of device network configurations and look at the status of each configuration?

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

We are not using SCDynamicStore and have very limited understanding of it. Can you let us know how can we use this to get status of utun interface? How can we get a change in status callback and is it possible to use any API to change the status from container app?

Can you let us know how can we use this to get status of utun interface?

This may get you at least half way there. This is all a bit fragile though and would recommend managing anything that involves a custom virtual interface from the Network Extension APIs.

#import <SystemConfiguration/SystemConfiguration.h>

-(void) testDynamicStore {

    SCDynamicStoreCallBack callbackFunc;
    CFDictionaryRef storeOptionsRef;
    SCDynamicStoreRef storeRef;
    
    callbackFunc = callbackFunction;
    
    storeOptionsRef = CFDictionaryCreate(NULL,
                                         (const void * *)&kSCDynamicStoreUseSessionKeys,
                                         (const void * *)&kCFBooleanTrue,
                                         1,
                                         &kCFTypeDictionaryKeyCallBacks,
                                         &kCFTypeDictionaryValueCallBacks);
    
    
    
    storeRef = SCDynamicStoreCreateWithOptions(NULL, CFSTR("SystemConfiguration"), storeOptionsRef,
                        callbackFunction, NULL);
    CFRelease(storeOptionsRef);
    
    /* To get the entire list of options available use a .* regex pattern */
    //CFArrayRef keyList = SCDynamicStoreCopyKeyList(storeRef, CFSTR(".*"));

    /* To get just a path on the utun interface */
    CFArrayRef keyList = SCDynamicStoreCopyKeyList(storeRef, CFSTR("State:/Network/Interface/utun3/IPv6"));

    //...

    NSArray *nsKeyList = (__bridge NSArray*)keyList;
    NSLog(@"NSKey Array: %@", nsKeyList.description);
    if (keyList != NULL && CFArrayGetCount(keyList) > 0) {
        CFArrayRef value = NULL;
        value = (CFArrayRef) SCDynamicStoreCopyValue(storeRef, (CFStringRef) CFArrayGetValueAtIndex(keyList, 0));
        NSArray *nsValue = (__bridge NSArray*)value;
        NSLog(@"NSValue Array: %@", nsValue.description);
        CFRelease(value);
    } else {
        NSLog(@"keyList array did not return anything");
    }
    CFRelease(keyList);
}

static void callbackFunction(SCDynamicStoreRef store, CFArrayRef changedKeys, void * context) {
    
    NSArray *nsChangedKeys = (__bridge NSArray*)changedKeys;
    NSLog(@"NSChanged Values Array: %@", nsChangedKeys.description);
}

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
NetworkExtension does not come up after bringing it down with ifconfig utun down
 
 
Q