]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/SCNetworkSet.c
configd-1061.40.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkSet.c
index 1b29b1818a19da277e4e9a00180ccd3a34aeb0ab..bfcf499e614a4e9e2c0592070ac49ff67927aa66 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -17,7 +17,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <CoreFoundation/CFRuntime.h>
-#include <SystemConfiguration/SystemConfiguration.h>
 #include "SCNetworkConfigurationInternal.h"
-#include <SystemConfiguration/SCValidation.h>
-#include <SystemConfiguration/SCPrivate.h>
 
 #include <pthread.h>
 
@@ -69,15 +66,19 @@ __SCNetworkSetCopyDescription(CFTypeRef cf)
 {
        CFAllocatorRef          allocator       = CFGetAllocator(cf);
        CFMutableStringRef      result;
-       SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)cf;
+       SCNetworkSetRef         set             = (SCNetworkSetRef)cf;
+       SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
 
        result = CFStringCreateMutable(allocator, 0);
-       CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkSet %p [%p]> {"), cf, allocator);
+       CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkSet %p [%p]> {"), set, allocator);
        CFStringAppendFormat(result, NULL, CFSTR("id = %@"), setPrivate->setID);
        CFStringAppendFormat(result, NULL, CFSTR(", prefs = %p"), setPrivate->prefs);
        if (setPrivate->name != NULL) {
                CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), setPrivate->name);
        }
+       if (!__SCNetworkSetExists(set)) {
+               CFStringAppendFormat(result, NULL, CFSTR(", REMOVED"));
+       }
        CFStringAppendFormat(result, NULL, CFSTR("}"));
 
        return result;
@@ -157,9 +158,9 @@ __SCNetworkSetCreatePrivate(CFAllocatorRef      allocator,
                return NULL;
        }
 
+       /* initialize non-zero/NULL members */
        setPrivate->setID       = CFStringCreateCopy(NULL, setID);
        setPrivate->prefs       = CFRetain(prefs);
-       setPrivate->name        = NULL;
 
        return setPrivate;
 }
@@ -168,70 +169,56 @@ __SCNetworkSetCreatePrivate(CFAllocatorRef      allocator,
 #pragma mark -
 
 
-static Boolean
-_serviceIsPPP(SCNetworkServiceRef service)
+static int
+_serviceOrder(SCNetworkServiceRef service)
 {
        SCNetworkInterfaceRef   interface;
-       CFStringRef             interfaceType;
 
        interface = SCNetworkServiceGetInterface(service);
-       if (interface == NULL) {
-               return FALSE;
+       if ((interface == NULL) || _SCNetworkServiceIsVPN(service)) {
+               return 100000;  // if unknown or VPN interface, sort last
        }
 
-       interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
-       return CFEqual(interfaceType, kSCNetworkInterfaceTypePPP);
+       return __SCNetworkInterfaceOrder(interface);
 }
 
 
-static Boolean
-_serviceIsVPN(SCNetworkServiceRef service)
+static CFIndex
+_serviceOrder_clear(CFMutableArrayRef order, CFStringRef serviceID)
 {
-       SCNetworkInterfaceRef   interface;
-       CFStringRef             interfaceType;
-
-//     if (!_serviceIsPPP(service)) {
-//             return FALSE;
-//     }
-
-       interface = SCNetworkServiceGetInterface(service);
-//     if (interface == NULL) {
-//             return FALSE;
-//     }
-
-       interface = SCNetworkInterfaceGetInterface(interface);
-       if (interface == NULL) {
-               return FALSE;
-       }
-
-       interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
-       return (CFEqual(interfaceType, kSCNetworkInterfaceTypeL2TP) ||
-               CFEqual(interfaceType, kSCNetworkInterfaceTypePPTP));
-}
-
+       CFIndex f;      // # of serviceID's found
+       CFIndex i;
+       CFIndex n;
 
-static int
-_serviceOrder(SCNetworkServiceRef service)
-{
-       SCNetworkInterfaceRef   interface;
+       f = 0;
+       i = 0;
+       n = CFArrayGetCount(order);
+       while (i < n) {
+               CFStringRef     thisServiceID   = CFArrayGetValueAtIndex(order, i);
+
+               if (CFEqual(thisServiceID, serviceID)) {
+                       // remove the serviceID
+                       CFArrayRemoveValueAtIndex(order, i);
+                       n--;
+                       f++;
+                       continue;
+               }
 
-       interface = SCNetworkServiceGetInterface(service);
-       if (interface == NULL) {
-               return 100000;  // sort last
+               i++;    // move to the next serviceID
        }
 
-       return __SCNetworkInterfaceOrder(interface);
+       return f;
 }
 
 
 static void
 _serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service)
 {
-       CFIndex                 i;
        CFIndex                 n;
        CFMutableArrayRef       newOrder;
        CFArrayRef              order;
-       CFStringRef             serviceID;
+       CFStringRef             serviceID       = SCNetworkServiceGetServiceID(service);
+       CFIndex                 serviceOrder    = _serviceOrder(service);
        SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
        CFIndex                 slot;
 
@@ -241,66 +228,46 @@ _serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service)
        } else {
                newOrder = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
        }
-       n = CFArrayGetCount(newOrder);
+       assert(newOrder != NULL);
 
-       serviceID = SCNetworkServiceGetServiceID(service);
-       if (CFArrayContainsValue(newOrder, CFRangeMake(0, n), serviceID)) {
-               // if serviceID already present
-               goto done;
+       n = _serviceOrder_clear(newOrder, serviceID);
+       if (n > 0) {
+               SC_log(LOG_ERR, "SCNetworkSetAddService() w/service already in ServiceOrder\n  service = %@\n  matched = %ld",
+                      service,
+                      n);
+               _SC_crash_once("SCNetworkSetAddService() w/service already in ServiceOrder", NULL, NULL);
        }
 
-       if (_serviceIsPPP(service) && !_serviceIsVPN(service)) {
-               int     serviceOrder;
-
-               /*
-                * we add [non-VPN] PPP interfaces to the head of the
-                * service order but ensure that "modem" devices are
-                * in front of "Bluetooth" & "IrDA" devices.
-                */
-               slot = 0;
-
-               serviceOrder = _serviceOrder(service);
-
-               for (i = 0; i < n; i++) {
-                       int                     slotOrder;
-                       CFStringRef             slotServiceID;
-                       SCNetworkServiceRef     slotService;
-
-                       slotServiceID = CFArrayGetValueAtIndex(newOrder, i);
-                       if (!isA_CFString(slotServiceID)) {
-                               // if bad prefs
-                               continue;
-                       }
-
-                       slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID);
-                       if (slotService == NULL) {
-                               // if serviceID not valid
-                               continue;
-                       }
+       slot = 0;
+       n = CFArrayGetCount(newOrder);
+       for (CFIndex i = 0; i < n; i++) {
+               int                     slotOrder;
+               SCNetworkServiceRef     slotService;
+               CFStringRef             slotServiceID;
+
+               slotServiceID = CFArrayGetValueAtIndex(newOrder, i);
+               if (!isA_CFString(slotServiceID)) {
+                       // if bad prefs
+                       continue;
+               }
 
-                       if (_serviceIsPPP(slotService)) {
-                               // if  PPP service
-                               slotOrder = _serviceOrder(slotService);
-                               if (serviceOrder >= slotOrder) {
-                                       // add the service *after* this one
-                                       slot = i + 1;
-                               }
-                       }
+               slotService = SCNetworkServiceCopy(setPrivate->prefs, slotServiceID);
+               if (slotService == NULL) {
+                       // if serviceID not valid
+                       continue;
+               }
 
-                       CFRelease(slotService);
+               slotOrder = _serviceOrder(slotService);
+               if (serviceOrder >= slotOrder) {
+                       // add the service *after* this one
+                       slot = i + 1;
                }
-       } else {
-               /*
-                * non-PPP interfaces are added to the end of the list.
-                */
-               slot = n;
+
+               CFRelease(slotService);
        }
 
        CFArrayInsertValueAtIndex(newOrder, slot, serviceID);
        (void) SCNetworkSetSetServiceOrder(set, newOrder);
-
-    done :
-
        CFRelease(newOrder);
 
        return;
@@ -310,6 +277,7 @@ _serviceOrder_add(SCNetworkSetRef set, SCNetworkServiceRef service)
 static void
 _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service)
 {
+       CFIndex                 n;
        CFMutableArrayRef       newOrder;
        CFArrayRef              order;
        CFStringRef             serviceID;
@@ -318,22 +286,17 @@ _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service)
        if (order == NULL) {
                return;
        }
-
-       serviceID = SCNetworkServiceGetServiceID(service);
-
        newOrder = CFArrayCreateMutableCopy(NULL, 0, order);
-       while (TRUE) {
-               CFIndex i;
 
-               i = CFArrayGetFirstIndexOfValue(newOrder,
-                                               CFRangeMake(0, CFArrayGetCount(newOrder)),
-                                               serviceID);
-               if (i == kCFNotFound) {
-                       break;
-               }
+       serviceID = SCNetworkServiceGetServiceID(service);
 
-               CFArrayRemoveValueAtIndex(newOrder, i);
+       n = _serviceOrder_clear(newOrder, serviceID);
+       if (n > 1) {
+               SC_log(LOG_ERR, "SCNetworkSetRemoveService() w/multiple instances of service in ServiceOrder\n  service = %@\n  count = %ld",
+                      service,
+                      n);
        }
+
        (void) SCNetworkSetSetServiceOrder(set, newOrder);
        CFRelease(newOrder);
 
@@ -345,9 +308,159 @@ _serviceOrder_remove(SCNetworkSetRef set, SCNetworkServiceRef service)
 #pragma mark SCNetworkSet APIs
 
 
+#define DEFAULT_SET_NAME       CFSTR("Automatic")
 #define        N_QUICK 16
 
 
+static CFStringRef
+copy_default_set_name(Boolean loc)
+{
+       CFStringRef             name;
+       static CFStringRef      non_localized   = NULL;
+       static CFStringRef      localized       = NULL;
+
+       if (!loc) {
+               static dispatch_once_t  once;
+
+               dispatch_once(&once, ^{
+                       CFBundleRef     bundle;
+
+                       bundle = _SC_CFBundleGet();
+                       if (bundle != NULL) {
+                               non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
+                                                                                  CFSTR("DEFAULT_SET_NAME"),
+                                                                                  DEFAULT_SET_NAME,
+                                                                                  NULL);
+                       }
+               });
+               name = non_localized;
+       } else {
+               static dispatch_once_t  once;
+
+               dispatch_once(&once, ^{
+                       CFBundleRef     bundle;
+
+                       bundle = _SC_CFBundleGet();
+                       if (bundle != NULL) {
+                               localized = CFBundleCopyLocalizedString(bundle,
+                                                                       CFSTR("DEFAULT_SET_NAME"),
+                                                                       DEFAULT_SET_NAME,
+                                                                       NULL);
+                       }
+               });
+               name = localized;
+       }
+
+       if (name == NULL) {
+               // if bundle or localized names not available
+               name = DEFAULT_SET_NAME;
+       }
+
+       CFRetain(name);
+       return name;
+}
+
+
+#define        PREVENT_DUPLICATE_SERVICE_NAMES
+#ifdef PREVENT_DUPLICATE_SERVICE_NAMES
+static CFStringRef
+copy_next_name(CFStringRef name)
+{
+       CFArrayRef              components;
+       CFIndex                 n;
+       CFMutableArrayRef       newComponents;
+       SInt32                  suffix  = 2;
+
+       if (name == NULL) {
+               return NULL;
+       }
+
+       components = CFStringCreateArrayBySeparatingStrings(NULL, name, CFSTR(" "));
+       if (components != NULL) {
+               newComponents = CFArrayCreateMutableCopy(NULL, 0, components);
+               CFRelease(components);
+       } else {
+               newComponents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               CFArrayAppendValue(newComponents, name);
+       }
+
+       n = CFArrayGetCount(newComponents);
+       if (n > 1) {
+               CFStringRef     str;
+
+               str = CFArrayGetValueAtIndex(newComponents, n - 1);
+               suffix = CFStringGetIntValue(str);
+               if (suffix++ > 0) {
+                       CFArrayRemoveValueAtIndex(newComponents, n - 1);
+               } else {
+                       suffix = 2;
+               }
+       }
+
+       name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), (int)suffix);
+       CFArrayAppendValue(newComponents, name);
+       CFRelease(name);
+
+       name = CFStringCreateByCombiningStrings(NULL, newComponents, CFSTR(" "));
+       CFRelease(newComponents);
+
+       return name;
+}
+
+
+static Boolean
+ensure_unique_service_name(SCNetworkServiceRef service)
+{
+       SCNetworkInterfaceRef   interface;
+       CFStringRef             name;
+       Boolean                 ok      = TRUE;
+
+       interface = SCNetworkServiceGetInterface(service);
+
+       name = SCNetworkServiceGetName(service);
+       if (name != NULL) {
+               CFRetain(name);
+       }
+
+       while (TRUE) {
+               CFStringRef     newName;
+
+               ok = SCNetworkServiceSetName(service, name);
+               if (ok) {
+                       break;
+               }
+
+               if (SCError() != kSCStatusKeyExists) {
+                       SC_log(LOG_INFO, "could not update service name for \"%@\": %s",
+                             SCNetworkInterfaceGetLocalizedDisplayName(interface),
+                             SCErrorString(SCError()));
+                       break;
+               }
+
+               newName = copy_next_name(name);
+               if (newName == NULL) {
+                       SC_log(LOG_INFO, "could not create unique name for \"%@\": %s",
+                             SCNetworkInterfaceGetLocalizedDisplayName(interface),
+                             SCErrorString(SCError()));
+                       break;
+               }
+
+               // try again with the "new" name
+               if (name != NULL) {
+                       CFRelease(name);
+               }
+               name = newName;
+       }
+
+       if (name != NULL) {
+               CFRelease(name);
+       }
+
+       return ok;
+}
+#endif // PREVENT_DUPLICATE_SERVICE_NAMES
+
+
 Boolean
 SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
 {
@@ -364,46 +477,36 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
                return FALSE;
        }
 
-       if (!isA_SCNetworkService(service)) {
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
                _SCErrorSet(kSCStatusInvalidArgument);
                return FALSE;
        }
 
-#define PREVENT_DUPLICATE_SERVICE_NAMES
-#ifdef  PREVENT_DUPLICATE_SERVICE_NAMES
-       CFStringRef     name;
-
-       name = SCNetworkServiceGetName(service);
-       if (name != NULL) {
-               CFArrayRef      services;
-
-               services = SCNetworkSetCopyServices(set);
-               if (services != NULL) {
-                       CFIndex i;
-                       CFIndex n;
-
-                       n = CFArrayGetCount(services);
-                       for (i = 0; i < n; i++) {
-                               CFStringRef             otherName;
-                               SCNetworkServiceRef     otherService;
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "SCNetworkSetAddService() w/removed set\n  set = %@\n  service = %@",
+                      set,
+                      service);
+               _SC_crash_once("SCNetworkSetAddService() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+       }
 
-                               otherService = CFArrayGetValueAtIndex(services, i);
-                               otherName = SCNetworkServiceGetName(otherService);
-                               if ((otherName != NULL) && CFEqual(name, otherName)) {
-                                       /*
-                                        * if a service with the same "name" is
-                                        * already a member of the set.
-                                        */
-                                       CFRelease(services);
-                                       _SCErrorSet(kSCStatusKeyExists);
-                                       return FALSE;
-                               }
-                       }
-               }
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkSetAddService() w/removed service\n  set = %@\n  service =  %@",
+                      set,
+                      service);
+               _SC_crash_once("SCNetworkSetAddService() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
 
-               CFRelease(services);
+       // make sure that we do not add an orphaned network service if its
+       // associated interface is a member of a bond or bridge.
+       interface = SCNetworkServiceGetInterface(service);
+       if ((interface != NULL) &&
+           __SCNetworkInterfaceIsMember(servicePrivate->prefs, interface)) {
+               _SCErrorSet(kSCStatusKeyExists);
+               return FALSE;
        }
-#endif // PREVENT_DUPLICATE_SERVICE_NAMES
 
 //#define PREVENT_DUPLICATE_SETS
 #ifdef  PREVENT_DUPLICATE_SETS
@@ -441,7 +544,7 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
        // get the [deep] interface configuration settings
        interface = SCNetworkServiceGetInterface(service);
        if (interface != NULL) {
-               interface_config = __SCNetworkInterfaceCopyDeepConfiguration(interface);
+               interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
        }
 
        // create the link between "set" and the "service"
@@ -453,6 +556,21 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
                                                              servicePrivate->serviceID,        // service
                                                              NULL);                            // entity
        ok = SCPreferencesPathSetLink(setPrivate->prefs, path, link);
+#ifdef PREVENT_DUPLICATE_SERVICE_NAMES
+       if (ok) {
+               // We use the interface cache here to not reach into the
+               // IORegistry for every service we go through
+               _SCNetworkInterfaceCacheOpen();
+               ok = ensure_unique_service_name(service);
+               _SCNetworkInterfaceCacheClose();
+
+               if (!ok) {
+                       // if we could not ensure a unique name, remove the (just added)
+                       // link between the "set" and the "service"
+                       (void) SCPreferencesPathRemoveValue(setPrivate->prefs, path);
+               }
+       }
+#endif // PREVENT_DUPLICATE_SERVICE_NAMES
        CFRelease(path);
        CFRelease(link);
        if (!ok) {
@@ -461,14 +579,21 @@ SCNetworkSetAddService(SCNetworkSetRef set, SCNetworkServiceRef service)
 
        // push the [deep] interface configuration into all sets which contain this service.
        if (interface != NULL) {
-               __SCNetworkInterfaceSetDeepConfiguration(interface, interface_config);
+               __SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
        }
 
        // add service to ServiceOrder
        _serviceOrder_add(set, service);
 
+       // mark set as no longer "new"
+       setPrivate->established = TRUE;
+
     done :
 
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkSetAddService(): %@, %@", set, service);
+       }
+
        if (interface_config != NULL)   CFRelease(interface_config);
        return ok;
 }
@@ -496,34 +621,12 @@ SCNetworkSetCopy(SCPreferencesRef prefs, CFStringRef setID)
        }
 
        setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
-       return (SCNetworkSetRef)setPrivate;
-}
-
-
-static Boolean
-_SCNetworkServiceExistsForInterface(CFArrayRef services, SCNetworkInterfaceRef interface)
-{
-       CFIndex i;
-       CFIndex n;
-
-       n = isA_CFArray(services) ? CFArrayGetCount(services) : 0;
-       for (i = 0; i < n; i++) {
-               SCNetworkServiceRef     service;
-               SCNetworkInterfaceRef   service_interface;
-
-               service = CFArrayGetValueAtIndex(services, i);
-
-               service_interface = SCNetworkServiceGetInterface(service);
-               while (service_interface != NULL) {
-                       if (CFEqual(interface, service_interface)) {
-                               return TRUE;
-                       }
+       assert(setPrivate != NULL);
 
-                       service_interface = SCNetworkInterfaceGetInterface(service_interface);
-               }
-       }
+       // mark set as "old" (already established)
+       setPrivate->established = TRUE;
 
-       return FALSE;
+       return (SCNetworkSetRef)setPrivate;
 }
 
 
@@ -535,7 +638,7 @@ SCNetworkSetContainsInterface(SCNetworkSetRef set, SCNetworkInterfaceRef interfa
 
        services = SCNetworkSetCopyServices(set);
        if (services != NULL) {
-               found = _SCNetworkServiceExistsForInterface(services, interface);
+               found = __SCNetworkServiceExistsForInterface(services, interface);
                CFRelease(services);
        }
 
@@ -578,14 +681,16 @@ SCNetworkSetCopyAll(SCPreferencesRef prefs)
                        SCNetworkSetPrivateRef  setPrivate;
 
                        if (!isA_CFDictionary(vals[i])) {
-                               SCLog(TRUE,
-                                     LOG_INFO,
-                                     CFSTR("SCNetworkSetCopyAll(): error w/set \"%@\"\n"),
-                                     keys[i]);
+                               SC_log(LOG_INFO, "error w/set \"%@\"", keys[i]);
                                continue;
                        }
 
                        setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, keys[i]);
+                       assert(setPrivate != NULL);
+
+                       // mark set as "old" (already established)
+                       setPrivate->established = TRUE;
+
                        CFArrayAppendValue(array, (SCNetworkSetRef)setPrivate);
                        CFRelease(setPrivate);
                }
@@ -599,6 +704,80 @@ SCNetworkSetCopyAll(SCPreferencesRef prefs)
 }
 
 
+CFArrayRef /* of SCNetworkInterfaceRef's */
+SCNetworkSetCopyAvailableInterfaces(SCNetworkSetRef set)
+{
+       CFMutableArrayRef       available;
+       CFMutableSetRef         excluded        = NULL;
+       int                     i;
+       CFArrayRef              interfaces;
+       CFIndex                 n_interfaces;
+       CFIndex                 n_exclusions    = 0;
+       SCPreferencesRef        prefs;
+       SCNetworkSetPrivateRef  setPrivate;
+
+       setPrivate = (SCNetworkSetPrivateRef)set;
+       prefs = setPrivate->prefs;
+
+       interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs);
+       n_interfaces = CFArrayGetCount(interfaces);
+       if (n_interfaces == 0) {
+               return interfaces;
+       }
+
+       if (prefs != NULL) {
+               CFArrayRef      bridges = NULL;
+
+               excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+
+#if    !TARGET_OS_IPHONE
+               CFArrayRef      bonds   = NULL;
+
+               bonds = SCBondInterfaceCopyAll(prefs);
+               if (bonds != NULL) {
+                       __SCBondInterfaceListCollectMembers(bonds, excluded);
+                       CFRelease(bonds);
+               }
+#endif /* !TARGET_OS_IPHONE */
+
+               bridges = SCBridgeInterfaceCopyAll(prefs);
+               if (bridges != NULL) {
+                       __SCBridgeInterfaceListCollectMembers(bridges, excluded);
+                       CFRelease(bridges);
+               }
+
+               n_exclusions = CFSetGetCount(excluded);
+       }
+
+       if (n_exclusions == 0) {
+               if (excluded != NULL) {
+                       CFRelease(excluded);
+               }
+
+               return interfaces;
+       }
+
+       available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+       for (i = 0; i < n_interfaces; i++) {
+               SCNetworkInterfaceRef   interface;
+
+               interface = CFArrayGetValueAtIndex(interfaces, i);
+               if (CFSetContainsValue(excluded, interface)) {
+                       // if excluded
+                       continue;
+               }
+
+               CFArrayAppendValue(available, interface);
+       }
+
+       CFRelease(interfaces);
+       CFRelease(excluded);
+
+       return available;
+}
+
+
 SCNetworkSetRef
 SCNetworkSetCopyCurrent(SCPreferencesRef prefs)
 {
@@ -620,8 +799,12 @@ SCNetworkSetCopyCurrent(SCPreferencesRef prefs)
                path = SCPreferencesPathKeyCreateSet(NULL, setID);
                if (CFEqual(path, currentID)) {
                        setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
+                       assert(setPrivate != NULL);
+
+                       // mark set as "old" (already established)
+                       setPrivate->established = TRUE;
                } else {
-                       SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkSetCopyCurrent(): preferences are non-conformant"));
+                       SC_log(LOG_NOTICE, "SCNetworkSetCopyCurrent(): preferences are non-conformant");
                }
                CFRelease(path);
        }
@@ -675,11 +858,9 @@ SCNetworkSetCopyServices(SCNetworkSetRef set)
                        link = SCPreferencesPathGetLink(setPrivate->prefs, path);
                        CFRelease(path);
                        if (link == NULL) {
-                               SCLog(TRUE,
-                                     LOG_INFO,
-                                     CFSTR("SCNetworkSetCopyServices(): service \"%@\" for set \"%@\" is not a link\n"),
-                                     keys[i],
-                                     setPrivate->setID);
+                               SC_log(LOG_INFO, "service \"%@\" for set \"%@\" is not a link",
+                                      keys[i],
+                                      setPrivate->setID);
                                continue;        // if the service is not a link
                        }
 
@@ -692,14 +873,31 @@ SCNetworkSetCopyServices(SCNetworkSetRef set)
                                                                                      serviceID,        // service
                                                                                      NULL);            // entity
                                if (CFEqual(path, link)) {
-                                       SCNetworkServicePrivateRef      servicePrivate;
-
-                                       servicePrivate = __SCNetworkServiceCreatePrivate(NULL,
-                                                                                        setPrivate->prefs,
-                                                                                        serviceID,
-                                                                                        NULL);
-                                       CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate);
-                                       CFRelease(servicePrivate);
+                                       CFDictionaryRef entity;
+                                       CFStringRef     interfacePath;
+                                       Boolean         skip            = FALSE;
+
+                                       interfacePath = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                    // allocator
+                                                                                                      serviceID,               // service
+                                                                                                      kSCEntNetInterface);     // entity
+                                       entity = SCPreferencesPathGetValue(setPrivate->prefs, interfacePath);
+                                       CFRelease(interfacePath);
+
+                                       if (__SCNetworkInterfaceEntityIsPPTP(entity)) {
+                                               SC_log(LOG_INFO, "PPTP services are no longer supported");
+                                               skip = TRUE;
+                                       }
+
+                                       if (!skip) {
+                                               SCNetworkServicePrivateRef      servicePrivate;
+
+                                               servicePrivate = __SCNetworkServiceCreatePrivate(NULL,
+                                                                                                setPrivate->prefs,
+                                                                                                serviceID,
+                                                                                                NULL);
+                                               CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate);
+                                               CFRelease(servicePrivate);
+                                       }
                                }
                                CFRelease(path);
                        }
@@ -718,6 +916,8 @@ SCNetworkSetRef
 SCNetworkSetCreate(SCPreferencesRef prefs)
 {
        CFArrayRef              components;
+       CFDictionaryRef         entity;
+       Boolean                 ok;
        CFStringRef             path;
        CFStringRef             prefix;
        CFStringRef             setID;
@@ -732,16 +932,94 @@ SCNetworkSetCreate(SCPreferencesRef prefs)
        }
 
        components = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
-       CFRelease(path);
-
        setID = CFArrayGetValueAtIndex(components, 2);
        setPrivate = __SCNetworkSetCreatePrivate(NULL, prefs, setID);
+       assert(setPrivate != NULL);
        CFRelease(components);
 
+       // mark set as "new" (not yet established)
+       setPrivate->established = FALSE;
+
+       // establish the set in the preferences
+       entity = CFDictionaryCreate(NULL,
+                                   NULL, NULL, 0,
+                                   &kCFTypeDictionaryKeyCallBacks,
+                                   &kCFTypeDictionaryValueCallBacks);
+       ok = SCPreferencesPathSetValue(prefs, path, entity);
+       CFRelease(path);
+       CFRelease(entity);
+       if (!ok) {
+               CFRelease(setPrivate);
+               setPrivate = NULL;
+       }
+
+       if (setPrivate != NULL) {
+               SC_log(LOG_DEBUG, "SCNetworkSetCreate(): %@", setPrivate);
+       }
+
        return (SCNetworkSetRef)setPrivate;
 }
 
 
+SCNetworkSetRef
+_SCNetworkSetCreateDefault(SCPreferencesRef prefs)
+{
+       CFStringRef     model;
+       Boolean         ok              = TRUE;
+       SCNetworkSetRef set;
+       CFStringRef     setName         = NULL;
+
+       set = SCNetworkSetCopyCurrent(prefs);
+       if (set != NULL) {
+               SC_log(LOG_NOTICE, "creating default set w/already existing set");
+               CFRelease(set);
+               _SCErrorSet(kSCStatusKeyExists);
+               return NULL;
+       }
+
+       // create a new ("Automatic") set
+       set = SCNetworkSetCreate(prefs);
+       if (set == NULL) {
+               SC_log(LOG_NOTICE, "could not create \"new\" set: %s",
+                      SCErrorString(SCError()));
+               goto done;
+       }
+
+       setName = copy_default_set_name(TRUE);
+       ok = SCNetworkSetSetName(set, setName);
+       CFRelease(setName);
+       if (!ok) {
+               // if we could not save the new set's "name"
+               SC_log(LOG_NOTICE, "could not save the new set's name: %s",
+                      SCErrorString(SCError()));
+               goto done;
+       }
+
+       ok = SCNetworkSetSetCurrent(set);
+       if (!ok) {
+               // if we could not make this the "current" set
+               SC_log(LOG_NOTICE, "could not establish new set as current: %s",
+                      SCErrorString(SCError()));
+//             goto done;
+       }
+
+       model = SCPreferencesGetValue(prefs, MODEL);
+       if (model == NULL) {
+               model = _SC_hw_model(FALSE);
+               SCPreferencesSetValue(prefs, MODEL, model);
+       }
+
+    done :
+
+       if (!ok && (set != NULL)) {
+               SCNetworkSetRemove(set);
+               CFRelease(set);
+               set = NULL;
+       }
+       return set;
+}
+
+
 CFStringRef
 SCNetworkSetGetSetID(SCNetworkSetRef set)
 {
@@ -759,7 +1037,6 @@ SCNetworkSetGetSetID(SCNetworkSetRef set)
 CFStringRef
 SCNetworkSetGetName(SCNetworkSetRef set)
 {
-       CFBundleRef             bundle;
        CFDictionaryRef         entity;
        CFStringRef             path;
        SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
@@ -786,33 +1063,20 @@ SCNetworkSetGetName(SCNetworkSetRef set)
                }
        }
 
-       bundle = _SC_CFBundleGet();
-       if (bundle != NULL) {
-               if (setPrivate->name != NULL) {
-                       CFStringRef     non_localized;
-
-                       non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
-                                                                          CFSTR("DEFAULT_SET_NAME"),
-                                                                          CFSTR("Automatic"),
-                                                                          NULL);
-                       if (non_localized != NULL) {
-                               if (CFEqual(setPrivate->name, non_localized)) {
-                                       CFStringRef     localized;
-
-                                       // if "Automatic", return localized name
-                                       localized = CFBundleCopyLocalizedString(bundle,
-                                                                               CFSTR("DEFAULT_SET_NAME"),
-                                                                               CFSTR("Automatic"),
-                                                                               NULL);
-                                       if (localized != NULL) {
-                                               CFRelease(setPrivate->name);
-                                               setPrivate->name = localized;
-                                       }
-                               }
+       if (setPrivate->name != NULL) {
+               CFStringRef     non_localized;
 
-                               CFRelease(non_localized);
-                       }
+               non_localized = copy_default_set_name(FALSE);
+               if (CFEqual(setPrivate->name, non_localized)) {
+                       CFStringRef     localized;
+
+                       // if "Automatic", return localized name
+                       localized = copy_default_set_name(TRUE);
+                       CFRelease(setPrivate->name);
+                       setPrivate->name = localized;
                }
+
+               CFRelease(non_localized);
        }
 
        return setPrivate->name;
@@ -858,6 +1122,24 @@ SCNetworkSetGetTypeID(void)
 }
 
 
+#if    TARGET_OS_IPHONE
+static Boolean
+isDefaultSet(SCNetworkSetRef set)
+{
+       CFStringRef     defaultName;
+       Boolean         isDefault       = FALSE;
+       CFStringRef     setName;
+
+       defaultName = copy_default_set_name(TRUE);
+       setName = SCNetworkSetGetName(set);
+       isDefault = _SC_CFEqual(setName, defaultName);
+       CFRelease(defaultName);
+
+       return isDefault;
+}
+#endif // TARGET_OS_IPHONE
+
+
 Boolean
 SCNetworkSetRemove(SCNetworkSetRef set)
 {
@@ -871,13 +1153,35 @@ SCNetworkSetRemove(SCNetworkSetRef set)
                return FALSE;
        }
 
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "SCNetworkSetRemove() w/removed set\n  set = %@", set);
+               _SC_crash_once("SCNetworkSetRemove() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+       }
+
+#if    TARGET_OS_IPHONE
+       if (isDefaultSet(set) && (geteuid() != 0)) {
+               SC_log(LOG_ERR, "SCNetworkSetRemove() failed, cannot remove set : %@", set);
+               _SC_crash("The \"Automatic\" network set cannot be removed", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+#endif // TARGET_OS_IPHONE
+
        currentPath = SCPreferencesGetValue(setPrivate->prefs, kSCPrefCurrentSet);
        path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
        if (!isA_CFString(currentPath) || !CFEqual(currentPath, path)) {
                ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
+       } else {
+               SC_log(LOG_DEBUG, "SCNetworkSetRemove() failed, currently active: %@", setPrivate->setID);
+               _SCErrorSet(kSCStatusInvalidArgument);
        }
        CFRelease(path);
 
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkSetRemove(): %@", set);
+       }
+
        return ok;
 }
 
@@ -889,6 +1193,7 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
        CFArrayRef                      interface_config        = NULL;
        Boolean                         ok;
        CFStringRef                     path;
+       int                             sc_status               = kSCStatusOK;
        SCNetworkServicePrivateRef      servicePrivate          = (SCNetworkServicePrivateRef)service;
        SCNetworkSetPrivateRef          setPrivate              = (SCNetworkSetPrivateRef)set;
 
@@ -897,7 +1202,24 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
                return FALSE;
        }
 
-       if (!isA_SCNetworkService(service)) {
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "SCNetworkSetRemoveService() w/removed set\n  set = %@\n  service = %@",
+                      set,
+                      service);
+               _SC_crash_once("SCNetworkSetRemoveService() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkSetRemoveService() w/removed service\n  set = %@\n  service = %@",
+                      set,
+                      service);
+               _SC_crash_once("SCNetworkSetRemoveService() w/removed service", NULL, NULL);
                _SCErrorSet(kSCStatusInvalidArgument);
                return FALSE;
        }
@@ -908,10 +1230,10 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
        // get the [deep] interface configuration settings
        interface = SCNetworkServiceGetInterface(service);
        if (interface != NULL) {
-               interface_config = __SCNetworkInterfaceCopyDeepConfiguration(interface);
+               interface_config = __SCNetworkInterfaceCopyDeepConfiguration(set, interface);
                if (interface_config != NULL) {
                        // remove the interface configuration from all sets which contain this service.
-                       __SCNetworkInterfaceSetDeepConfiguration(interface, NULL);
+                       __SCNetworkInterfaceSetDeepConfiguration(set, interface, NULL);
                }
        }
 
@@ -921,19 +1243,23 @@ SCNetworkSetRemoveService(SCNetworkSetRef set, SCNetworkServiceRef service)
                                                                 servicePrivate->serviceID,
                                                                 NULL);
        ok = SCPreferencesPathRemoveValue(setPrivate->prefs, path);
-       CFRelease(path);
        if (!ok) {
-               goto done;
+               sc_status = SCError();  // preserve the error
        }
+       CFRelease(path);
 
        // push the [deep] interface configuration [back] into all sets which contain the service.
        if (interface_config != NULL) {
-               __SCNetworkInterfaceSetDeepConfiguration(interface, interface_config);
+               __SCNetworkInterfaceSetDeepConfiguration(set, interface, interface_config);
+               CFRelease(interface_config);
        }
 
-    done :
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkSetRemoveService(): %@, %@", set, service);
+       } else {
+               _SCErrorSet(sc_status);
+       }
 
-       if (interface_config != NULL)     CFRelease(interface_config);
        return ok;
 }
 
@@ -950,9 +1276,21 @@ SCNetworkSetSetCurrent(SCNetworkSetRef set)
                return FALSE;
        }
 
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "SCNetworkSetSetCurrent() w/removed set\n  set = %@", set);
+               _SC_crash_once("SCNetworkSetSetCurrent() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
        path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
        ok = SCPreferencesSetValue(setPrivate->prefs, kSCPrefCurrentSet, path);
        CFRelease(path);
+
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkSetSetCurrent(): %@", set);
+       }
+
        return ok;
 }
 
@@ -960,8 +1298,10 @@ SCNetworkSetSetCurrent(SCNetworkSetRef set)
 Boolean
 SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
 {
-       CFBundleRef             bundle          = NULL;
        CFDictionaryRef         entity;
+#if    TARGET_OS_IPHONE
+       Boolean                 isDefaultName   = FALSE;
+#endif // TARGET_OS_IPHONE
        CFStringRef             localized       = NULL;
        CFStringRef             non_localized   = NULL;
        Boolean                 ok              = FALSE;
@@ -973,6 +1313,15 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
                return FALSE;
        }
 
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "SCNetworkSetSetName() w/removed set\n  set = %@\n  name = %@",
+                      set,
+                      name != NULL ? name : CFSTR("<NULL>"));
+               _SC_crash_once("SCNetworkSetSetName() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
        if ((name != NULL) && !isA_CFString(name)) {
                _SCErrorSet(kSCStatusInvalidArgument);
                return FALSE;
@@ -981,28 +1330,42 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
        // if known, compare against localized name
 
        if (name != NULL) {
-               bundle = _SC_CFBundleGet();
-               if (bundle != NULL) {
-                       non_localized = _SC_CFBundleCopyNonLocalizedString(bundle,
-                                                                          CFSTR("DEFAULT_SET_NAME"),
-                                                                          CFSTR("Automatic"),
-                                                                          NULL);
-                       if (non_localized != NULL) {
-                               if (CFEqual(name, non_localized)) {
-                                       localized = CFBundleCopyLocalizedString(bundle,
-                                                                               CFSTR("DEFAULT_SET_NAME"),
-                                                                               CFSTR("Automatic"),
-                                                                               NULL);
-                                       if (localized != NULL) {
-                                               name = localized;
-                                       }
-                               }
-                       }
+               non_localized = copy_default_set_name(FALSE);
+               if (CFEqual(name, non_localized)) {
+                       localized = copy_default_set_name(TRUE);
+                       name = localized;
+#if    TARGET_OS_IPHONE
+                       isDefaultName = TRUE;
+#endif // TARGET_OS_IPHONE
                }
+#if    TARGET_OS_IPHONE
+               else {
+                       localized = copy_default_set_name(TRUE);
+                       isDefaultName = CFEqual(name, non_localized);
+               }
+#endif // TARGET_OS_IPHONE
+       }
+
+#if    TARGET_OS_IPHONE
+       if (!isDefaultName && isDefaultSet(set) && (geteuid() != 0)) {
+               // if we are trying to change the name of the "Automatic" set
+               SC_log(LOG_ERR, "SCNetworkSetSetName() failed, cannot rename : %@", set);
+               _SC_crash("The \"Automatic\" network set cannot be renamed", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               goto done;
        }
+#endif // TARGET_OS_IPHONE
+
+#define        PREVENT_DUPLICATE_SET_NAMES
+#ifdef PREVENT_DUPLICATE_SET_NAMES
+
+#if    TARGET_OS_IPHONE
+       if (!isDefaultName) {
+               // On iOS, only block naming multiple sets with the name
+               // "Automatic".  Others names are OK.
+       } else
+#endif // TARGET_OS_IPHONE
 
-#define PREVENT_DUPLICATE_SET_NAMES
-#ifdef  PREVENT_DUPLICATE_SET_NAMES
        if (name != NULL) {
                CFArrayRef      sets;
 
@@ -1039,18 +1402,12 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
 
        // if known, store non-localized name
 
-       if ((name != NULL) && (bundle != NULL) && (non_localized != NULL)) {
+       if ((name != NULL) && (non_localized != NULL)) {
                if (localized == NULL) {
-                       localized = CFBundleCopyLocalizedString(bundle,
-                                                               CFSTR("DEFAULT_SET_NAME"),
-                                                               CFSTR("Automatic"),
-                                                               NULL);
+                       localized = copy_default_set_name(TRUE);
                }
-
-               if (localized != NULL) {
-                       if (CFEqual(name, localized)) {
-                               name = non_localized;
-                       }
+               if (CFEqual(name, localized)) {
+                       name = non_localized;
                }
        }
 
@@ -1058,18 +1415,18 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
 
        path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
        entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
-       if ((entity == NULL) && (name != NULL)) {
-               entity = CFDictionaryCreate(NULL,
-                                           NULL,
-                                           NULL,
-                                           0,
-                                           &kCFTypeDictionaryKeyCallBacks,
-                                           &kCFTypeDictionaryValueCallBacks);
-       }
-       if (isA_CFDictionary(entity)) {
+       if (isA_CFDictionary(entity) ||
+           ((entity == NULL) && (name != NULL))) {
                CFMutableDictionaryRef  newEntity;
 
-               newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+               if (entity != NULL) {
+                       newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+               } else {
+                       newEntity = CFDictionaryCreateMutable(NULL,
+                                                             0,
+                                                             &kCFTypeDictionaryKeyCallBacks,
+                                                             &kCFTypeDictionaryValueCallBacks);
+               }
                if (name != NULL) {
                        CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name);
                } else {
@@ -1082,6 +1439,10 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
 
     done :
 
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkSetSetName(): %@", set);
+       }
+
        if (localized != NULL)          CFRelease(localized);
        if (non_localized != NULL)      CFRelease(non_localized);
        return ok;
@@ -1091,7 +1452,10 @@ SCNetworkSetSetName(SCNetworkSetRef set, CFStringRef name)
 Boolean
 SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
 {
+       CFMutableArrayRef       cleanOrder;
        CFDictionaryRef         dict;
+       CFIndex                 i;
+       CFIndex                 n;
        CFMutableDictionaryRef  newDict;
        Boolean                 ok;
        CFStringRef             path;
@@ -1102,10 +1466,15 @@ SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
                return FALSE;
        }
 
-       if (isA_CFArray(newOrder)) {
-               CFIndex i;
-               CFIndex n       = CFArrayGetCount(newOrder);
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "SCNetworkSetSetServiceOrder() w/removed set\n  set = %@", set);
+               _SC_crash_once("SCNetworkSetSetServiceOrder() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
 
+       if (isA_CFArray(newOrder)) {
+               n = CFArrayGetCount(newOrder);
                for (i = 0; i < n; i++) {
                        CFStringRef     serviceID;
 
@@ -1135,7 +1504,24 @@ SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
                                                    &kCFTypeDictionaryValueCallBacks);
        }
 
-       CFDictionarySetValue(newDict, kSCPropNetServiceOrder, newOrder);
+       cleanOrder = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       n = CFArrayGetCount(newOrder);
+       for (i = 0; i < n; i++) {
+               CFIndex         nClean          = CFArrayGetCount(cleanOrder);
+               CFStringRef     serviceID       = CFArrayGetValueAtIndex(newOrder, i);
+
+               if ((nClean == 0) ||
+                   !CFArrayContainsValue(cleanOrder, CFRangeMake(0, nClean), serviceID)) {
+                       // if first reference to this serviceID
+                       CFArrayAppendValue(cleanOrder, serviceID);
+               } else {
+                       // skip duplicate serviceID
+                       SC_log(LOG_ERR, "SCNetworkSetSetServiceOrder() found duplicate serviceID: removed %@\n", serviceID);
+               }
+       }
+       CFDictionarySetValue(newDict, kSCPropNetServiceOrder, cleanOrder);
+       CFRelease(cleanOrder);
+
        ok = SCPreferencesPathSetValue(setPrivate->prefs, path, newDict);
        CFRelease(newDict);
        CFRelease(path);
@@ -1148,6 +1534,31 @@ SCNetworkSetSetServiceOrder(SCNetworkSetRef set, CFArrayRef newOrder)
 #pragma mark SCNetworkSet SPIs
 
 
+__private_extern__
+Boolean
+__SCNetworkSetExists(SCNetworkSetRef set)
+{
+       CFDictionaryRef                 entity;
+       CFStringRef                     path;
+       SCNetworkSetPrivateRef          setPrivate      = (SCNetworkSetPrivateRef)set;
+
+       if (setPrivate->prefs == NULL) {
+               return FALSE;
+       }
+
+       path = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
+       entity = SCPreferencesPathGetValue(setPrivate->prefs, path);
+       CFRelease(path);
+
+       if (!isA_CFDictionary(entity)) {
+               // if no "set"
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
 static void
 add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef interface)
 {
@@ -1173,83 +1584,369 @@ add_supported_interfaces(CFMutableArrayRef interface_list, SCNetworkInterfaceRef
 }
 
 
-static CFStringRef
-next_service_name(SCNetworkServiceRef service)
+static CFSetRef        /* of SCNetworkInterfaceRef's */
+copyExcludedInterfaces(SCPreferencesRef prefs)
 {
-       CFArrayRef              components;
-       CFIndex                 n;
-       CFStringRef             name;
-       CFMutableArrayRef       newComponents;
-       SInt32                  suffix  = 2;
+       CFMutableSetRef excluded;
+       CFArrayRef      interfaces;
 
-       name = SCNetworkServiceGetName(service);
-       if (name == NULL) {
-               return NULL;
+       excluded = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+
+#if    !TARGET_OS_IPHONE
+       // exclude Bond [member] interfaces
+       interfaces = SCBondInterfaceCopyAll(prefs);
+       if (interfaces != NULL) {
+               __SCBondInterfaceListCollectMembers(interfaces, excluded);
+               CFRelease(interfaces);
        }
+#endif // !TARGET_OS_IPHONE
 
-       components = CFStringCreateArrayBySeparatingStrings(NULL, name, CFSTR(" "));
-       if (components != NULL) {
-               newComponents = CFArrayCreateMutableCopy(NULL, 0, components);
-               CFRelease(components);
-       } else {
-               newComponents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-               CFArrayAppendValue(newComponents, name);
+       // exclude Bridge [member] interfaces
+       interfaces = SCBridgeInterfaceCopyAll(prefs);
+       if (interfaces != NULL) {
+               __SCBridgeInterfaceListCollectMembers(interfaces, excluded);
+               CFRelease(interfaces);
        }
 
-       n = CFArrayGetCount(newComponents);
-       if (n > 1) {
-               CFStringRef     str;
+       return excluded;
+}
 
-               str = CFArrayGetValueAtIndex(newComponents, n - 1);
-               suffix = CFStringGetIntValue(str);
-               if (suffix++ > 0) {
-                       CFArrayRemoveValueAtIndex(newComponents, n - 1);
-               } else {
-                       suffix = 2;
+
+#if    !TARGET_OS_IPHONE
+static SCBridgeInterfaceRef
+copyAutoBridgeInterface(SCPreferencesRef prefs, CFStringRef bridgeName)
+{
+       SCBridgeInterfaceRef    bridge          = NULL;
+       CFArrayRef              interfaces;
+
+       // exclude Bridge [member] interfaces
+       interfaces = SCBridgeInterfaceCopyAll(prefs);
+       if (interfaces != NULL) {
+               CFIndex         i;
+               CFIndex         n;
+
+               n = CFArrayGetCount(interfaces);
+               for (i = 0; i < n; i++) {
+                       SCBridgeInterfaceRef    interface;
+                       CFStringRef             name    = NULL;
+                       CFDictionaryRef         options;
+
+                       interface = CFArrayGetValueAtIndex(interfaces, i);
+                       options = SCBridgeInterfaceGetOptions(interface);
+                       if ((options != NULL) &&
+                           CFDictionaryGetValueIfPresent(options,
+                                                         CFSTR("__AUTO__"),
+                                                         (const void **)&name) &&
+                           _SC_CFEqual(name, bridgeName)) {
+                               bridge = interface;
+                               CFRetain(bridge);
+                               break;
+                       }
                }
+
+               CFRelease(interfaces);
        }
 
-       name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), suffix);
-       CFArrayAppendValue(newComponents, name);
-       CFRelease(name);
+       if (bridge == NULL) {
+               bridge = SCBridgeInterfaceCreate(prefs);
+               if (bridge != NULL) {
+                       CFMutableDictionaryRef  newOptions;
+                       Boolean                 ok;
 
-       name = CFStringCreateByCombiningStrings(NULL, newComponents, CFSTR(" "));
-       CFRelease(newComponents);
+                       newOptions = CFDictionaryCreateMutable(NULL, 0,
+                                                              &kCFTypeDictionaryKeyCallBacks,
+                                                              &kCFTypeDictionaryValueCallBacks);
+                       CFDictionarySetValue(newOptions, CFSTR("__AUTO__"), bridgeName);
+                       ok = SCBridgeInterfaceSetOptions(bridge, newOptions);
+                       CFRelease(newOptions);
+                       if (!ok) {
+                               CFRelease(bridge);
+                               bridge = NULL;
+                       }
+               }
+       }
 
-       return name;
+       return bridge;
 }
+#endif // !TARGET_OS_IPHONE
 
 
-static Boolean
-__SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces)
+static CFArrayRef
+copyServices(SCNetworkSetRef set)
 {
-       CFIndex                 i;
-       CFIndex                 n;
-       Boolean                 ok              = TRUE;
        CFArrayRef              services;
        SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
-       Boolean                 updated         = FALSE;
 
        // first, assume that we only want to add new services
        // for those interfaces that are not represented in the
        // current set.
        services = SCNetworkSetCopyServices(set);
-       if ((services != NULL) && (CFArrayGetCount(services) > 0)) {
-               // but, if we are starting off with a non-empty
-               // set than we only want to add new services for
-               // those interfaces that are not represented in
-               // *any* set.
+       if ((services != NULL) && setPrivate->established) {
+               // but, if we are given an existing (or "established") set
+               // than we only want to add new services for those interfaces
+               // that are not represented in *any* set.
                CFRelease(services);
                services = SCNetworkServiceCopyAll(setPrivate->prefs);
        }
 
-       n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
+       return services;
+}
+
+
+#if    !TARGET_OS_IPHONE
+static CF_RETURNS_RETAINED CFArrayRef
+updateServices(CFArrayRef services, SCNetworkInterfaceRef interface)
+{
+       CFStringRef             bsdName;
+       CFIndex                 i;
+       CFIndex                 n;
+       CFMutableArrayRef       newServices;
+
+       if (services == NULL) {
+               return NULL;
+       }
+
+       bsdName = SCNetworkInterfaceGetBSDName(interface);
+
+       newServices = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+       n = CFArrayGetCount(services);
+       for (i = 0; i < n; i++) {
+               SCNetworkInterfaceRef           interface;
+               CFStringRef                     interfaceName;
+               SCNetworkServiceRef             newService;
+               SCNetworkServiceRef             service;
+               CFStringRef                     serviceID;
+               SCNetworkServicePrivateRef      servicePrivate;
+
+               service = CFArrayGetValueAtIndex(services, i);
+               interface = SCNetworkServiceGetInterface(service);
+               interfaceName = SCNetworkInterfaceGetBSDName(interface);
+               if (!_SC_CFEqual(interfaceName, bsdName)) {
+                       // if not a match, retain
+                       CFArrayAppendValue(newServices, service);
+                       continue;
+               }
+
+               // if a match, update
+               serviceID = SCNetworkServiceGetServiceID(service);
+               servicePrivate = (SCNetworkServicePrivateRef)service;
+               newService = SCNetworkServiceCopy(servicePrivate->prefs, serviceID);
+               if (newService != NULL) {
+                       CFArrayAppendValue(newServices, newService);
+                       CFRelease(newService);
+               }
+       }
+
+       return newServices;
+}
+#endif // !TARGET_OS_IPHONE
+
+
+static __inline__ Boolean
+skipInterface(SCNetworkInterfaceRef interface)
+{
+       CFStringRef     action;
+
+       action = _SCNetworkInterfaceGetConfigurationAction(interface);
+       if (isA_CFString(action) &&
+           CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+CFComparisonResult
+_SCNetworkSetCompare(const void *val1, const void *val2, void *context)
+{
+#pragma unused(context)
+       CFStringRef     id1;
+       CFStringRef     id2;
+       CFStringRef     name1;
+       CFStringRef     name2;
+       SCNetworkSetRef s1      = (SCNetworkSetRef)val1;
+       SCNetworkSetRef s2      = (SCNetworkSetRef)val2;
+
+       name1 = SCNetworkSetGetName(s1);
+       name2 = SCNetworkSetGetName(s2);
+
+       if (name1 != NULL) {
+               if (name2 != NULL) {
+                       return CFStringCompare(name1, name2, 0);
+               } else {
+                       return kCFCompareLessThan;
+               }
+       }
+
+       if (name2 != NULL) {
+               return kCFCompareGreaterThan;
+       }
+
+       id1 = SCNetworkSetGetSetID(s1);
+       id2 = SCNetworkSetGetSetID(s2);
+       return CFStringCompare(id1, id2, 0);
+}
+
+
+static Boolean
+__SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CFArrayRef interfaces, Boolean excludeHidden)
+{
+       CFSetRef                excluded;
+       CFIndex                 i;
+       CFIndex                 n               = 0;
+       Boolean                 ok              = TRUE;
+       CFArrayRef              services;
+       SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
+       Boolean                 updated         = FALSE;
+#if    !TARGET_OS_IPHONE
+       Boolean                 updatedIFs      = FALSE;
+#endif // !TARGET_OS_IPHONE
+
+#if    TARGET_OS_IPHONE
+       CFArrayRef              orphans         = NULL;
+       CFArrayRef              sets;
+
+       sets = SCNetworkSetCopyAll(setPrivate->prefs);
+       if (sets != NULL) {
+               if (CFArrayGetCount(sets) == 1) {
+                       services = SCNetworkSetCopyServices(set);
+                       if (services != NULL) {
+                               n = CFArrayGetCount(services);
+                               CFRelease(services);
+                       }
+
+                       if ((n == 0) && CFEqual(set, CFArrayGetValueAtIndex(sets, 0))) {
+                               // after a "Reset Network Settings" we need to find (and
+                               // add back) any VPN services that were orphaned.
+                               orphans = SCNetworkServiceCopyAll(setPrivate->prefs);
+                       }
+               }
+
+               CFRelease(sets);
+       }
+#endif // TARGET_OS_IPHONE
+
+       // copy network services
+       services = copyServices(set);
+
+       // copy network interfaces to be excluded
+       excluded = copyExcludedInterfaces(setPrivate->prefs);
+
+#if    !TARGET_OS_IPHONE
+       // look for interfaces that should auto-magically be added
+       // to an Ethernet bridge
+       n = ((services != NULL) && (interfaces != NULL)) ? CFArrayGetCount(interfaces) : 0;
+       for (i = 0; i < n; i++) {
+               SCBridgeInterfaceRef    bridge          = NULL;
+               SCNetworkInterfaceRef   interface;
+
+               interface = CFArrayGetValueAtIndex(interfaces, i);
+
+               if (excludeHidden && skipInterface(interface)) {
+                       // if not auto-configure
+                       continue;
+               }
+
+               if (CFSetContainsValue(excluded, interface)) {
+                       // if this interface is a member of a Bond or Bridge
+                       continue;
+               }
+
+               if (__SCNetworkServiceExistsForInterface(services, interface)) {
+                       // if this is not a new interface
+                       continue;
+               }
+
+               if (_SCNetworkInterfaceIsBuiltin(interface) &&
+                   _SCNetworkInterfaceIsThunderbolt(interface) &&
+                   !isA_SCBridgeInterface(interface)) {
+                       // add built-in Thunderbolt interfaces to bridge
+                       bridge = copyAutoBridgeInterface(setPrivate->prefs, CFSTR("thunderbolt-bridge"));
+               }
+
+               if (bridge != NULL) {
+                       CFIndex                 bridgeIndex;
+                       CFArrayRef              members;
+                       CFMutableArrayRef       newMembers;
+                       CFMutableSetRef         newExcluded;
+                       CFMutableArrayRef       newInterfaces;
+                       CFArrayRef              newServices;
+
+                       // track the bridge interface (if it's in our list)
+                       bridgeIndex = CFArrayGetFirstIndexOfValue(interfaces,
+                                                                 CFRangeMake(0, CFArrayGetCount(interfaces)),
+                                                                 bridge);
+
+                       // add new member interface
+                       members = SCBridgeInterfaceGetMemberInterfaces(bridge);
+                       if ((members != NULL) && (CFArrayGetCount(members) > 0)) {
+                               newMembers = CFArrayCreateMutableCopy(NULL, 0, members);
+                               updated = TRUE;         // if we're updating an existing bridge
+                       } else {
+                               newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                       }
+                       CFArrayAppendValue(newMembers, interface);
+                       ok = SCBridgeInterfaceSetMemberInterfaces(bridge, newMembers);
+                       CFRelease(newMembers);
+                       if (!ok) {
+                               SC_log(LOG_INFO, "could not update bridge with \"%@\": %s",
+                                      SCNetworkInterfaceGetLocalizedDisplayName(interface),
+                                      SCErrorString(SCError()));
+                               CFRelease(bridge);
+                               continue;
+                       }
+
+                       // exclude the new member interface
+                       newExcluded = CFSetCreateMutableCopy(NULL, 0, excluded);
+                       CFRelease(excluded);
+                       CFSetAddValue(newExcluded, interface);
+                       excluded = newExcluded;
+
+                       // update the list of interfaces to include the [new or updated] bridge
+                       newInterfaces = CFArrayCreateMutableCopy(NULL, 0, interfaces);
+                       if (bridgeIndex != kCFNotFound) {
+                               CFArraySetValueAtIndex(newInterfaces, bridgeIndex, bridge);
+                       } else {
+                               CFArrayAppendValue(newInterfaces, bridge);
+                       }
+                       if (updatedIFs) {
+                               CFRelease(interfaces);
+                       }
+                       interfaces = newInterfaces;
+                       updatedIFs = TRUE;
+
+                       // refresh [existing] services
+                       newServices = updateServices(services, bridge);
+                       if (newServices != NULL) {
+                               CFRelease(services);
+                               services = newServices;
+                       }
+
+                       CFRelease(bridge);
+               }
+       }
+#endif // !TARGET_OS_IPHONE
+
+       n = ((services != NULL) && (interfaces != NULL)) ? CFArrayGetCount(interfaces) : 0;
        for (i = 0; i < n; i++) {
                SCNetworkInterfaceRef   interface;
                CFMutableArrayRef       interface_list;
 
                interface = CFArrayGetValueAtIndex(interfaces, i);
-               if (_SCNetworkServiceExistsForInterface(services, interface)) {
+
+               if (excludeHidden && skipInterface(interface)) {
+                       // if not auto-configure
+                       continue;
+               }
+
+               if (CFSetContainsValue(excluded, interface)) {
+                       // if this interface is a member of a Bond or Bridge
+                       continue;
+               }
+
+               if (__SCNetworkServiceExistsForInterface(services, interface)) {
                        // if this is not a new interface
                        continue;
                }
@@ -1268,72 +1965,31 @@ __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CF
 
                                service = SCNetworkServiceCreate(setPrivate->prefs, interface);
                                if (service == NULL) {
-                                       SCLog(TRUE, LOG_DEBUG,
-                                             CFSTR("could not create service for \"%@\": %s\n"),
-                                             SCNetworkInterfaceGetLocalizedDisplayName(interface),
-                                             SCErrorString(SCError()));
+                                       SC_log(LOG_ERR, "could not create service for \"%@\": %s",
+                                              SCNetworkInterfaceGetLocalizedDisplayName(interface),
+                                              SCErrorString(SCError()));
                                        ok = FALSE;
                                        goto nextInterface;
                                }
 
                                ok = SCNetworkServiceEstablishDefaultConfiguration(service);
                                if (!ok) {
-                                       SCLog(TRUE, LOG_DEBUG,
-                                             CFSTR("could not estabish default configuration for \"%@\": %s\n"),
-                                             SCNetworkInterfaceGetLocalizedDisplayName(interface),
-                                             SCErrorString(SCError()));
+                                       SC_log(LOG_ERR, "could not estabish default configuration for \"%@\": %s",
+                                              SCNetworkInterfaceGetLocalizedDisplayName(interface),
+                                              SCErrorString(SCError()));
                                        SCNetworkServiceRemove(service);
                                        CFRelease(service);
                                        goto nextInterface;
                                }
 
-                               while (TRUE) {
-                                       CFStringRef     newName;
-
-                                       ok = SCNetworkSetAddService(set, service);
-                                       if (ok) {
-                                               break;
-                                       }
-
-                                       if (SCError() != kSCStatusKeyExists) {
-                                               SCLog(TRUE, LOG_DEBUG,
-                                                     CFSTR("could not add service for \"%@\": %s\n"),
-                                                     SCNetworkInterfaceGetLocalizedDisplayName(interface),
-                                                     SCErrorString(SCError()));
-                                               SCNetworkServiceRemove(service);
-                                               CFRelease(service);
-                                               goto nextInterface;
-                                       }
-
-                                       // we have two interfaces with the same service
-                                       // name, acquire a new, hopefully unique, name
-
-                                       newName = next_service_name(service);
-                                       if (newName == NULL) {
-                                               SCLog(TRUE, LOG_DEBUG,
-                                                     CFSTR("could not set unique name for \"%@\": %s\n"),
-                                                     SCNetworkInterfaceGetLocalizedDisplayName(interface),
-                                                     SCErrorString(SCError()));
-                                               SCNetworkServiceRemove(service);
-                                               CFRelease(service);
-                                               goto nextInterface;
-                                       }
-
-                                       ok = SCNetworkServiceSetName(service, newName);
-                                       CFRelease(newName);
-                                       if (ok) {
-                                               continue;
-                                       }
-
-                                       if (SCError() != kSCStatusKeyExists) {
-                                               SCLog(TRUE, LOG_DEBUG,
-                                                     CFSTR("could not set unique name for \"%@\": %s\n"),
-                                                     SCNetworkInterfaceGetLocalizedDisplayName(interface),
-                                                     SCErrorString(SCError()));
-                                               SCNetworkServiceRemove(service);
-                                               CFRelease(service);
-                                               goto nextInterface;
-                                       }
+                               ok = SCNetworkSetAddService(set, service);
+                               if (!ok) {
+                                       SC_log(LOG_ERR, "could not add service for \"%@\": %s",
+                                              SCNetworkInterfaceGetLocalizedDisplayName(interface),
+                                              SCErrorString(SCError()));
+                                       SCNetworkServiceRemove(service);
+                                       CFRelease(service);
+                                       goto nextInterface;
                                }
 
                                CFRelease(service);
@@ -1348,11 +2004,50 @@ __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CF
                }
                CFRelease(interface_list);
        }
+#if    !TARGET_OS_IPHONE
+       if (updatedIFs && (interfaces != NULL)) {
+               CFRelease(interfaces);
+       }
+#endif // !TARGET_OS_IPHONE
        if (services != NULL)   CFRelease(services);
+       CFRelease(excluded);
 
-       if (ok && !updated) {
-               // if no changes were made
-               _SCErrorSet(kSCStatusOK);
+#if    TARGET_OS_IPHONE
+       if (orphans != NULL) {
+               if (ok && updated) {
+                       CFIndex i;
+                       CFIndex n       = CFArrayGetCount(orphans);
+
+                       for (i = 0; i < n; i++) {
+                               SCNetworkServiceRef     service;
+
+                               service = CFArrayGetValueAtIndex(orphans, i);
+                               if (_SCNetworkServiceIsVPN(service)) {
+                                       ok = SCNetworkSetAddService(set, service);
+                                       if (!ok) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               CFRelease(orphans);
+       }
+#endif // TARGET_OS_IPHONE
+
+       if (ok) {
+               if (updated) {
+                       CFStringRef     model;
+
+                       model = SCPreferencesGetValue(setPrivate->prefs, MODEL);
+                       if (model == NULL) {
+                               model = _SC_hw_model(FALSE);
+                               SCPreferencesSetValue(setPrivate->prefs, MODEL, model);
+                       }
+               } else {
+                       // if no changes were made
+                       _SCErrorSet(kSCStatusOK);
+               }
        }
 
        return updated;
@@ -1362,17 +2057,20 @@ __SCNetworkSetEstablishDefaultConfigurationForInterfaces(SCNetworkSetRef set, CF
 Boolean
 SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set)
 {
-       CFArrayRef      interfaces;
-       Boolean         updated;
+       CFArrayRef              interfaces;
+       SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
+       Boolean                 updated         = FALSE;
 
        if (!isA_SCNetworkSet(set)) {
                _SCErrorSet(kSCStatusInvalidArgument);
                return FALSE;
        }
 
-       interfaces = SCNetworkInterfaceCopyAll();
-       updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
-       if (interfaces != NULL) CFRelease(interfaces);
+       interfaces = _SCNetworkInterfaceCopyAllWithPreferences(setPrivate->prefs);
+       if (interfaces != NULL) {
+               updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces, TRUE);
+               CFRelease(interfaces);
+       }
 
        return updated;
 }
@@ -1381,8 +2079,8 @@ SCNetworkSetEstablishDefaultConfiguration(SCNetworkSetRef set)
 Boolean
 SCNetworkSetEstablishDefaultInterfaceConfiguration(SCNetworkSetRef set, SCNetworkInterfaceRef interface)
 {
-       CFMutableArrayRef       interfaces;
-       Boolean                 updated;
+       CFArrayRef      interfaces;
+       Boolean         updated;
 
        if (!isA_SCNetworkSet(set)) {
                _SCErrorSet(kSCStatusInvalidArgument);
@@ -1394,10 +2092,253 @@ SCNetworkSetEstablishDefaultInterfaceConfiguration(SCNetworkSetRef set, SCNetwor
                return FALSE;
        }
 
-       interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-       CFArrayAppendValue(interfaces, interface);
-       updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces);
+       interfaces = CFArrayCreate(NULL, (const void **)&interface, 1, &kCFTypeArrayCallBacks);
+       assert(interfaces != NULL);
+       updated = __SCNetworkSetEstablishDefaultConfigurationForInterfaces(set, interfaces, FALSE);
        CFRelease(interfaces);
 
        return updated;
 }
+
+
+SCNetworkServiceRef
+SCNetworkSetCopySelectedVPNService(SCNetworkSetRef set)
+{
+       CFIndex                 i;
+       CFIndex                 n;
+       SCNetworkServiceRef     selected        = NULL;
+       CFArrayRef              services;
+       CFMutableArrayRef       services_vpn    = NULL;
+
+       if (!isA_SCNetworkSet(set)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       services = SCNetworkSetCopyServices(set);
+       if (services != NULL) {
+               n = CFArrayGetCount(services);
+               for (i = 0; i < n; i++) {
+                       SCNetworkServiceRef     service;
+
+                       service = CFArrayGetValueAtIndex(services, i);
+                       if (!SCNetworkServiceGetEnabled(service)) {
+                               // if not enabled
+                               continue;
+                       }
+
+                       if (!_SCNetworkServiceIsVPN(service)) {
+                               // if not VPN service
+                               continue;
+                       }
+
+                       if (services_vpn == NULL) {
+                               services_vpn = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                       }
+                       CFArrayAppendValue(services_vpn, service);
+               }
+
+               CFRelease(services);
+       }
+
+       if (services_vpn == NULL) {
+               // if no VPN services
+               return NULL;
+       }
+
+       n = CFArrayGetCount(services_vpn);
+       if (n > 1) {
+               CFArrayRef              order;
+               CFMutableArrayRef       sorted;
+
+               order = SCNetworkSetGetServiceOrder(set);
+               sorted = CFArrayCreateMutableCopy(NULL, 0, services_vpn);
+               CFArraySortValues(sorted,
+                                 CFRangeMake(0, CFArrayGetCount(sorted)),
+                                 _SCNetworkServiceCompare,
+                                 (void *)order);
+               CFRelease(services_vpn);
+               services_vpn = sorted;
+       }
+
+#if    TARGET_OS_IPHONE
+       if (n > 1) {
+               CFStringRef     serviceID_prefs;
+
+#define VPN_PREFERENCES        CFSTR("com.apple.mobilevpn")
+#define VPN_SERVICE_ID CFSTR("activeVPNID")
+
+               CFPreferencesAppSynchronize(VPN_PREFERENCES);
+               serviceID_prefs = CFPreferencesCopyAppValue(VPN_SERVICE_ID, VPN_PREFERENCES);
+               if (serviceID_prefs != NULL) {
+                       for (i = 0; i < n; i++) {
+                               SCNetworkServiceRef     service;
+                               CFStringRef             serviceID;
+
+                               service = CFArrayGetValueAtIndex(services_vpn, i);
+                               serviceID = SCNetworkServiceGetServiceID(service);
+                               if (CFEqual(serviceID, serviceID_prefs)) {
+                                       selected = service;
+                                       CFRetain(selected);
+                                       break;
+                               }
+
+                       }
+
+                       CFRelease(serviceID_prefs);
+               }
+       }
+#endif // TARGET_OS_IPHONE
+
+       if (selected == NULL) {
+               selected = CFArrayGetValueAtIndex(services_vpn, 0);
+               CFRetain(selected);
+       }
+
+       CFRelease(services_vpn);
+       return selected;
+}
+
+
+Boolean
+SCNetworkSetSetSelectedVPNService(SCNetworkSetRef set, SCNetworkServiceRef service)
+{
+       Boolean         ok      = TRUE;
+       CFArrayRef      services;
+
+       if (!isA_SCNetworkSet(set)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!isA_SCNetworkService(service) || !_SCNetworkServiceIsVPN(service)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       services = SCNetworkSetCopyServices(set);
+       if (services != NULL) {
+               CFIndex i;
+               CFIndex n       = CFArrayGetCount(services);
+
+               if (!CFArrayContainsValue(services, CFRangeMake(0, n), service)) {
+                       // if selected service not a member of the current set
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       ok = FALSE;
+                       goto done;
+               }
+
+               for (i = 0; ok && (i < n); i++) {
+                       SCNetworkServiceRef     vpn;
+
+                       vpn = CFArrayGetValueAtIndex(services, i);
+                       if (!_SCNetworkServiceIsVPN(vpn)) {
+                               // if not VPN service
+                               continue;
+                       }
+
+                       ok = SCNetworkServiceSetEnabled(vpn, CFEqual(service, vpn));
+               }
+       }
+
+    done :
+
+       if (services != NULL) CFRelease(services);
+       return ok;
+}
+
+
+Boolean
+_SCNetworkSetSetSetID(SCNetworkSetRef set, CFStringRef newSetID)
+{
+       SCNetworkSetRef         currentSet              = NULL;
+       SCNetworkSetPrivateRef  currentSetPrivate       = NULL;
+       CFDictionaryRef         entity;
+       CFStringRef             newPath;
+       Boolean                 ok                      = FALSE;
+       CFStringRef             oldPath                 = NULL;
+       SCNetworkSetPrivateRef  setPrivate              = (SCNetworkSetPrivateRef)set;
+       Boolean                 updateCurrentSet        = FALSE;
+
+       if (!isA_SCNetworkSet(set)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!isA_CFString(newSetID)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkSetExists(set)) {
+               SC_log(LOG_ERR, "_SCNetworkSetSetSetID() w/removed set\n  set = %@\n  setID = %@",
+                      set,
+                      newSetID);
+               _SC_crash_once("_SCNetworkSetSetSetID() w/removed set", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       // If newSetID is equal to current setID, our work is done
+       if (CFEqual(newSetID, setPrivate->setID)) {
+               return TRUE;
+       }
+
+       newPath = SCPreferencesPathKeyCreateSet(NULL, newSetID);
+       entity = SCPreferencesPathGetValue(setPrivate->prefs, newPath);
+       if (isA_CFDictionary(entity)) {
+               // if the new set already exists
+               _SCErrorSet(kSCStatusKeyExists);
+               goto done;
+       }
+
+       oldPath = SCPreferencesPathKeyCreateSet(NULL, setPrivate->setID);
+       entity = SCPreferencesPathGetValue(setPrivate->prefs, oldPath);
+       if (!isA_CFDictionary(entity)) {
+               // if the set has already been removed
+               _SCErrorSet(kSCStatusNoKey);
+               goto done;
+       }
+
+       ok = SCPreferencesPathSetValue(setPrivate->prefs, newPath, entity);
+       if (!ok) {
+               goto done;
+       }
+
+       ok = SCPreferencesPathRemoveValue(setPrivate->prefs, oldPath);
+       if (!ok) {
+               goto done;
+       }
+
+       // update current set (if needed)
+       currentSet = SCNetworkSetCopyCurrent(setPrivate->prefs);
+       if (currentSet != NULL) {
+               currentSetPrivate = (SCNetworkSetPrivateRef)currentSet;
+               if (CFEqual(currentSetPrivate->setID, setPrivate->setID)) {
+                       updateCurrentSet = TRUE;
+               }
+               CFRelease(currentSet);
+       }
+
+       SC_log(LOG_DEBUG, "_SCNetworkSetSetID(): %@ --> %@", set, newSetID);
+
+       // replace setID with new one
+       CFRetain(newSetID);
+       CFRelease(setPrivate->setID);
+       setPrivate->setID = newSetID;
+
+       if (updateCurrentSet) {
+               SCNetworkSetSetCurrent(set);
+       }
+
+    done:
+
+       if (oldPath != NULL) {
+               CFRelease(oldPath);
+       }
+       if (newPath != NULL) {
+               CFRelease(newPath);
+       }
+
+       return ok;
+}