Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Handle all interfaces, including VPN #229

Merged
merged 1 commit into from

2 participants

@iriz

I have rewritten the code to look at Network Services rather than directly at interfaces. As far as I have tested, it now correctly handles VPN connections and Thunderbolt network adapters.

@dustinrue dustinrue merged commit 3738c33 into dustinrue:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 27, 2013
  1. @iriz
This page is out of date. Refresh to see the latest.
Showing with 81 additions and 62 deletions.
  1. +81 −62 Source/NetworkLinkEvidenceSource.m
View
143 Source/NetworkLinkEvidenceSource.m
@@ -4,6 +4,7 @@
//
// Created by Mark Wallis on 25/07/07.
// Tweaks by David Symonds on 25/07/07.
+// Changed by Ingvar Nedrebo 27/03/13
//
#import <SystemConfiguration/SystemConfiguration.h>
@@ -25,6 +26,7 @@ static void linkChange(SCDynamicStoreRef store, CFArrayRef changedKeys, void *i
@implementation NetworkLinkEvidenceSource {
NSLock *lock;
NSMutableArray *interfaces;
+ BOOL didSleep;
// To get network services
SCPreferencesRef prefs;
@@ -40,6 +42,7 @@ - (id)init
lock = [[NSLock alloc] init];
interfaces = [[NSMutableArray alloc] init];
+ didSleep = NO;
return self;
}
@@ -52,40 +55,39 @@ - (void)dealloc
[super dealloc];
}
+
- (NSArray *)enumerate
{
- // Get all interfaces from the Network Services prefs. This way
- // we capture interfaces that not currently present, such as
- // Thunderbolt network adapters.
- NSArray * services = (NSArray *)SCNetworkServiceCopyAll(prefs);
- NSMutableSet * inters = [NSMutableSet set];
+ NSArray * services = [(NSArray *)SCNetworkServiceCopyAll(prefs) autorelease];
+
+ // For some connections, we get several Services with different ID
+ // but same name (e.g. 'Ethernet'), presumably because they have
+ // different parameters. Enumerate by name here, because we're only
+ // interested in whether an interface is active, and not which specific
+ // Service caused it.
+
+ NSMutableDictionary * serviceState = [NSMutableDictionary dictionary];
NSEnumerator *e = [services objectEnumerator];
SCNetworkServiceRef service;
-
- while (service = (SCNetworkServiceRef) [e nextObject]) {
- SCNetworkInterfaceRef inter = SCNetworkServiceGetInterface(service);
- NSString *name = (NSString *) SCNetworkInterfaceGetBSDName(inter);
- if (name) {
- [inters addObject:name];
- }
+ while (service = (SCNetworkServiceRef) [e nextObject])
+ {
+ NSString * serviceName = (NSString *) SCNetworkServiceGetName(service);
+ if ([serviceState[serviceName] boolValue])
+ continue;
+
+ NSString * serviceID = (NSString *) SCNetworkServiceGetServiceID(service);
+ BOOL isActive = [self isProtocol:@"IPv4" activeForService:serviceID];
+ if (!isActive)
+ isActive = [self isProtocol:@"IPv6" activeForService:serviceID];
+ serviceState[serviceName] = @(isActive);
}
- [services release];
-
- NSMutableArray * subset = [NSMutableArray array];
- for (NSString * name in inters) {
- NSString * key = [NSString stringWithFormat:@"State:/Network/Interface/%@/Link", name];
- // Default state is 'inactive'. That way we capture a transition even if the link
- // disappeared completely, e.g. Thunderbolt adapter unplugged.
- NSString * opt = [NSString stringWithFormat:@"-%@", name];
- CFDictionaryRef current = SCDynamicStoreCopyValue(store, (CFStringRef)key);
- if (current) {
- if (CFDictionaryGetValue(current, CFSTR("Active")) == kCFBooleanTrue)
- opt = [NSString stringWithFormat:@"+%@", name];
- CFRelease(current);
- }
- [subset addObject:opt];
- }
+ NSMutableArray *subset = [NSMutableArray array];
+ for (NSString * name in serviceState)
+ {
+ NSString * opt = [serviceState[name] boolValue] ? @"+" : @"-";
+ [subset addObject:[NSString stringWithFormat:@"%@%@", opt, name]];
+ }
return subset;
}
@@ -100,6 +102,12 @@ - (void)doFullUpdate:(id)sender
[lock unlock];
}
+- (void) goingToSleep:(id)arg
+{
+ [super goingToSleep:arg];
+ didSleep = YES;
+}
+
- (void)start
{
if (running)
@@ -117,20 +125,25 @@ - (void)start
dispatch_queue_t queue = dispatch_queue_create("ControlPlane.NetworkLink", NULL);
SCDynamicStoreSetDispatchQueue(store, queue);
- // Notify on link changes for all interfaces
- NSArray * patterns = @[ @"State:/Network/Interface/[[:alnum:]]+/Link" ];
+ // Notify on IPv4/IPv6 updates on all services
+ NSArray * patterns = @[ @"State:/Network/Service/[^/]+/IPv." ];
SCDynamicStoreSetNotificationKeys(store, NULL, (CFArrayRef) patterns);
// TODO: catch errors
- // For retrieving network service later
+ // For retrieving network services later
prefs = SCPreferencesCreate(NULL, CFSTR("ControlPlane"), NULL);
- dispatch_async(queue, ^{
+ // Delay initial scan if we're waking from sleep, otherwise we'll get false
+ // negatives for interfaces that takes time to become active (e.g. slow DHCP)
+ NSTimeInterval delay = didSleep ? 15.0 : 0.0;
+ dispatch_time_t scanTime = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
+ dispatch_after(scanTime, queue, ^{
[self doFullUpdate:nil];
});
- CFRelease(queue); // retained by 'store'
+ CFRelease(queue); // retained by 'store'
+ didSleep = NO;
running = YES;
}
@@ -160,10 +173,10 @@ - (BOOL)doesRuleMatch:(NSDictionary *)rule
{
BOOL match = NO;
- NSString *iface = [rule valueForKey:@"parameter"];
+ NSString *service = [rule valueForKey:@"parameter"];
[lock lock];
- match = [interfaces containsObject:iface];
+ match = [interfaces containsObject:service];
[lock unlock];
return match;
@@ -178,46 +191,38 @@ - (NSArray *)getSuggestions
{
NSMutableArray *arr = [NSMutableArray array];
NSArray *all = [(NSArray *)SCNetworkServiceCopyAll(prefs) autorelease];
- // The above returns all services in all locations, and in random order,
- // so weed out duplicates and sort at the end to make more presentable.
+
+ // See comments in -enumerate:
NSMutableSet * alreadySeen = [NSMutableSet set];
NSEnumerator *en = [all objectEnumerator];
SCNetworkServiceRef service;
while ((service = (SCNetworkServiceRef) [en nextObject])) {
- SCNetworkInterfaceRef inter = SCNetworkServiceGetInterface(service);
- NSString *dev = (NSString *) SCNetworkInterfaceGetBSDName(inter);
- if (!dev)
- continue;
- if ([alreadySeen containsObject:dev])
+ NSString *name = (NSString *) SCNetworkServiceGetName(service);
+ if ([alreadySeen containsObject:name])
continue;
- NSString *name = (NSString *) SCNetworkInterfaceGetLocalizedDisplayName(inter);
- if (!name)
- name = [NSString stringWithFormat:@"%@?", dev];
+
NSString *activeDesc = [NSString stringWithFormat:
- NSLocalizedString(@"%@ (%@) link active", @"In NetworkLinkEvidenceSource"), dev, name];
+ NSLocalizedString(@"%@ link active", @"In NetworkLinkEvidenceSource"), name];
NSString *inactiveDesc = [NSString stringWithFormat:
- NSLocalizedString(@"%@ (%@) link inactive", @"In NetworkLinkEvidenceSource"), dev, name];
- NSString *activeParam = [NSString stringWithFormat:@"+%@", dev];
- NSString *inactiveParam = [NSString stringWithFormat:@"-%@", dev];
-
- // Don't include interfaces that we couldn't enumerate
- if (![interfaces containsObject:activeParam] && ![interfaces containsObject:inactiveParam])
- continue;
-
+ NSLocalizedString(@"%@ link inactive", @"In NetworkLinkEvidenceSource"), name];
+ NSString *activeParam = [NSString stringWithFormat:@"+%@", name];
+ NSString *inactiveParam = [NSString stringWithFormat:@"-%@", name];
+
[arr addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- @"NetworkLink", @"type",
- activeParam, @"parameter",
- activeDesc, @"description", nil]];
+ @"NetworkLink", @"type",
+ activeParam, @"parameter",
+ activeDesc, @"description", nil]];
[arr addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- @"NetworkLink", @"type",
- inactiveParam, @"parameter",
- inactiveDesc, @"description", nil]];
+ @"NetworkLink", @"type",
+ inactiveParam, @"parameter",
+ inactiveDesc, @"description", nil]];
+
- [alreadySeen addObject:dev];
+ [alreadySeen addObject:name];
}
- [arr sortUsingDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES] ]];
+ [arr sortUsingDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] ]];
return arr;
}
@@ -225,4 +230,18 @@ - (NSString *) friendlyName {
return NSLocalizedString(@"Active Network Adapter", @"");
}
+// A service is considered active if IPv4 or IPv6 device name has been assigned
+- (BOOL) isProtocol:(NSString *)protocol activeForService:(NSString *)serviceID
+{
+ BOOL isActive = NO;
+ NSString * key = [NSString stringWithFormat:@"State:/Network/Service/%@/%@", serviceID, protocol];
+ CFDictionaryRef protoDict = SCDynamicStoreCopyValue(store, (CFStringRef)key);
+ if (protoDict)
+ {
+ isActive = CFDictionaryContainsKey(protoDict, CFSTR("InterfaceName"));
+ CFRelease(protoDict);
+ }
+ return isActive;
+}
+
@end
Something went wrong with that request. Please try again.