]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/SCNetworkService.c
configd-1061.40.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkService.c
index db30ae7724e434a435e3eaedbce4325c176a6fdf..d9c8d4bed15e63048bfd5db122fb49c29fd26b34 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2018 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 <SystemConfiguration/SCNetworkConfigurationInternal.h>
-#include <SystemConfiguration/SCValidation.h>
-#include <SystemConfiguration/SCPrivate.h>
+#include "SCNetworkConfigurationInternal.h"
+#include "SCPreferencesInternal.h"
 
 #include <pthread.h>
 
+#define EXTERNAL_ID_DOMAIN_PREFIX      "_"
 
 static CFStringRef     __SCNetworkServiceCopyDescription       (CFTypeRef cf);
 static void            __SCNetworkServiceDeallocate            (CFTypeRef cf);
 static Boolean         __SCNetworkServiceEqual                 (CFTypeRef cf1, CFTypeRef cf2);
+static CFHashCode      __SCNetworkServiceHash                  (CFTypeRef cf);
 
 
-static CFTypeID __kSCNetworkServiceTypeID      = _kCFRuntimeNotATypeID;
+static CFTypeID __kSCNetworkServiceTypeID              = _kCFRuntimeNotATypeID;
 
 
 static const CFRuntimeClass __SCNetworkServiceClass = {
@@ -54,20 +54,13 @@ static const CFRuntimeClass __SCNetworkServiceClass = {
        NULL,                                   // copy
        __SCNetworkServiceDeallocate,           // dealloc
        __SCNetworkServiceEqual,                // equal
-       NULL,                                   // hash
+       __SCNetworkServiceHash,                 // hash
        NULL,                                   // copyFormattingDesc
        __SCNetworkServiceCopyDescription       // copyDebugDesc
 };
 
 
-static pthread_once_t          initialized     = PTHREAD_ONCE_INIT;
-
-
-static __inline__ CFTypeRef
-isA_SCNetworkService(CFTypeRef obj)
-{
-       return (isA_CFType(obj, SCNetworkServiceGetTypeID()));
-}
+static pthread_once_t          initialized             = PTHREAD_ONCE_INIT;
 
 
 static CFStringRef
@@ -75,13 +68,24 @@ __SCNetworkServiceCopyDescription(CFTypeRef cf)
 {
        CFAllocatorRef                  allocator       = CFGetAllocator(cf);
        CFMutableStringRef              result;
-       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)cf;
+       SCNetworkServiceRef             service         = (SCNetworkServiceRef)cf;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
        result = CFStringCreateMutable(allocator, 0);
-       CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkService %p [%p]> { "), cf, allocator);
-       CFStringAppendFormat(result, NULL, CFSTR("id=%@"), servicePrivate->serviceID);
-//     CFStringAppendFormat(result, NULL, CFSTR(", prefs=%@"), servicePrivate->prefs);
-       CFStringAppendFormat(result, NULL, CFSTR(" }"));
+       CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkService %p [%p]> {"), service, allocator);
+       CFStringAppendFormat(result, NULL, CFSTR("id = %@"), servicePrivate->serviceID);
+       if (servicePrivate->prefs != NULL) {
+               CFStringAppendFormat(result, NULL, CFSTR(", prefs = %p"), servicePrivate->prefs);
+       } else if (servicePrivate->store != NULL) {
+               CFStringAppendFormat(result, NULL, CFSTR(", store = %p"), servicePrivate->store);
+       }
+       if (servicePrivate->name != NULL) {
+               CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), servicePrivate->name);
+       }
+       if (!__SCNetworkServiceExists(service)) {
+               CFStringAppendFormat(result, NULL, CFSTR(", REMOVED"));
+       }
+       CFStringAppendFormat(result, NULL, CFSTR("}"));
 
        return result;
 }
@@ -95,8 +99,11 @@ __SCNetworkServiceDeallocate(CFTypeRef cf)
        /* release resources */
 
        CFRelease(servicePrivate->serviceID);
-       if (servicePrivate->interface != NULL)  CFRelease(servicePrivate->interface);
-       CFRelease(servicePrivate->prefs);
+       if (servicePrivate->interface != NULL) CFRelease(servicePrivate->interface);
+       if (servicePrivate->prefs != NULL) CFRelease(servicePrivate->prefs);
+       if (servicePrivate->store != NULL) CFRelease(servicePrivate->store);
+       if (servicePrivate->name != NULL) CFRelease(servicePrivate->name);
+       if (servicePrivate->externalIDs != NULL) CFRelease(servicePrivate->externalIDs);
 
        return;
 }
@@ -121,6 +128,15 @@ __SCNetworkServiceEqual(CFTypeRef cf1, CFTypeRef cf2)
 }
 
 
+static CFHashCode
+__SCNetworkServiceHash(CFTypeRef cf)
+{
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)cf;
+
+       return CFHash(servicePrivate->serviceID);
+}
+
+
 static void
 __SCNetworkServiceInitialize(void)
 {
@@ -131,9 +147,9 @@ __SCNetworkServiceInitialize(void)
 
 __private_extern__ SCNetworkServicePrivateRef
 __SCNetworkServiceCreatePrivate(CFAllocatorRef         allocator,
+                               SCPreferencesRef        prefs,
                                CFStringRef             serviceID,
-                               SCNetworkInterfaceRef   interface,
-                               SCPreferencesRef        prefs)
+                               SCNetworkInterfaceRef   interface)
 {
        SCNetworkServicePrivateRef              servicePrivate;
        uint32_t                                size;
@@ -151,7 +167,8 @@ __SCNetworkServiceCreatePrivate(CFAllocatorRef              allocator,
                return NULL;
        }
 
-       servicePrivate->prefs           = CFRetain(prefs);
+       /* initialize non-zero/NULL members */
+       servicePrivate->prefs           = (prefs != NULL) ? CFRetain(prefs): NULL;
        servicePrivate->serviceID       = CFStringCreateCopy(NULL, serviceID);
        servicePrivate->interface       = (interface != NULL) ? CFRetain(interface) : NULL;
 
@@ -159,26 +176,225 @@ __SCNetworkServiceCreatePrivate(CFAllocatorRef           allocator,
 }
 
 
-/* ---------- SCNetworkService APIs ---------- */
+#pragma mark -
+#pragma mark Service ordering
+
+
+CFComparisonResult
+_SCNetworkServiceCompare(const void *val1, const void *val2, void *context)
+{
+       CFStringRef             id1;
+       CFStringRef             id2;
+       CFArrayRef              order   = (CFArrayRef)context;
+       SCNetworkServiceRef     s1      = (SCNetworkServiceRef)val1;
+       SCNetworkServiceRef     s2      = (SCNetworkServiceRef)val2;
+
+       id1 = SCNetworkServiceGetServiceID(s1);
+       id2 = SCNetworkServiceGetServiceID(s2);
+
+       if (order != NULL) {
+               CFIndex o1;
+               CFIndex o2;
+               CFRange range;
+
+               range = CFRangeMake(0, CFArrayGetCount(order));
+               o1 = CFArrayGetFirstIndexOfValue(order, range, id1);
+               o2 = CFArrayGetFirstIndexOfValue(order, range, id2);
+
+               if (o1 > o2) {
+                       return (o2 != kCFNotFound) ? kCFCompareGreaterThan : kCFCompareLessThan;
+               } else if (o1 < o2) {
+                       return (o1 != kCFNotFound) ? kCFCompareLessThan    : kCFCompareGreaterThan;
+               }
+       }
+
+       return CFStringCompare(id1, id2, 0);
+}
+
+
+#pragma mark -
+#pragma mark SCNetworkService APIs
 
 
 #define        N_QUICK 64
 
 
+__private_extern__ CFArrayRef /* of SCNetworkServiceRef's */
+__SCNetworkServiceCopyAllEnabled(SCPreferencesRef prefs)
+{
+       CFMutableArrayRef       array   = NULL;
+       CFIndex                 i_sets;
+       CFIndex                 n_sets;
+       CFArrayRef              sets;
+
+       sets = SCNetworkSetCopyAll(prefs);
+       if (sets == NULL) {
+               return NULL;
+       }
+
+       n_sets = CFArrayGetCount(sets);
+       for (i_sets = 0; i_sets < n_sets; i_sets++) {
+               CFIndex         i_services;
+               CFIndex         n_services;
+               CFArrayRef      services;
+               SCNetworkSetRef set;
+
+               set = CFArrayGetValueAtIndex(sets, i_sets);
+               services = SCNetworkSetCopyServices(set);
+               if (services == NULL) {
+                       continue;
+               }
+
+               n_services = CFArrayGetCount(services);
+               for (i_services = 0; i_services < n_services; i_services++) {
+                       SCNetworkServiceRef service;
+
+                       service = CFArrayGetValueAtIndex(services, i_services);
+                       if (!SCNetworkServiceGetEnabled(service)) {
+                               // if not enabled
+                               continue;
+                       }
+
+                       if ((array == NULL) ||
+                           !CFArrayContainsValue(array,
+                                                 CFRangeMake(0, CFArrayGetCount(array)),
+                                                 service)) {
+                               if (array == NULL) {
+                                       array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                               }
+                               CFArrayAppendValue(array, service);
+                       }
+               }
+               CFRelease(services);
+       }
+       CFRelease(sets);
+
+       return array;
+}
+
+
+__private_extern__ 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;
+                       }
+
+                       service_interface = SCNetworkInterfaceGetInterface(service_interface);
+               }
+       }
+
+       return FALSE;
+}
+
+
+static void
+mergeDict(const void *key, const void *value, void *context)
+{
+       CFMutableDictionaryRef  newDict = (CFMutableDictionaryRef)context;
+
+       CFDictionarySetValue(newDict, key, value);
+       return;
+}
+
+
+static CF_RETURNS_RETAINED CFDictionaryRef
+_protocolTemplate(SCNetworkServiceRef service, CFStringRef protocolType)
+{
+       SCNetworkInterfaceRef           interface;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+       CFDictionaryRef                 template        = NULL;
+
+       interface = servicePrivate->interface;
+       if (interface != NULL) {
+               SCNetworkInterfaceRef   childInterface;
+               CFStringRef             childInterfaceType      = NULL;
+               CFStringRef             interfaceType;
+
+               // get the template
+               interfaceType = SCNetworkInterfaceGetInterfaceType(servicePrivate->interface);
+               childInterface = SCNetworkInterfaceGetInterface(servicePrivate->interface);
+               if (childInterface != NULL) {
+                       childInterfaceType = SCNetworkInterfaceGetInterfaceType(childInterface);
+               }
+
+               template = __copyProtocolTemplate(interfaceType, childInterfaceType, protocolType);
+               if (template != NULL) {
+                       CFDictionaryRef         overrides;
+
+                       // move to the interface at the lowest layer
+                       while (childInterface != NULL) {
+                               interface = childInterface;
+                               childInterface = SCNetworkInterfaceGetInterface(interface);
+                       }
+
+                       overrides = __SCNetworkInterfaceGetTemplateOverrides(interface, protocolType);
+                       if (isA_CFDictionary(overrides)) {
+                               CFMutableDictionaryRef  newTemplate;
+
+                               newTemplate = CFDictionaryCreateMutableCopy(NULL, 0, template);
+                               CFDictionaryApplyFunction(overrides, mergeDict, newTemplate);
+                               CFRelease(template);
+                               template = newTemplate;
+                       }
+               }
+       }
+
+       if (template == NULL) {
+               template = CFDictionaryCreate(NULL,
+                                             NULL,
+                                             NULL,
+                                             0,
+                                             &kCFTypeDictionaryKeyCallBacks,
+                                             &kCFTypeDictionaryValueCallBacks);
+       }
+
+       return template;
+}
+
+
 Boolean
 SCNetworkServiceAddProtocolType(SCNetworkServiceRef service, CFStringRef protocolType)
 {
        CFDictionaryRef                 entity;
+       Boolean                         newEnabled;
        CFDictionaryRef                 newEntity       = NULL;
        Boolean                         ok              = FALSE;
        CFStringRef                     path;
+       SCNetworkProtocolRef            protocol        = NULL;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
        if (!__SCNetworkProtocolIsValidType(protocolType)) {
                _SCErrorSet(kSCStatusInvalidArgument);
                return FALSE;
        }
 
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceAddProtocolType() w/removed service\n  service = %@\n  protocol = %@",
+                      service,
+                      protocolType);
+               _SC_crash_once("SCNetworkServiceAddProtocolType() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              protocolType);                    // entity
@@ -190,34 +406,47 @@ SCNetworkServiceAddProtocolType(SCNetworkServiceRef service, CFStringRef protoco
                goto done;
        }
 
-       if (servicePrivate->interface != NULL) {
-               SCNetworkInterfaceRef   childInterface;
-               CFStringRef             childInterfaceType      = NULL;
-               CFStringRef             interfaceType;
+       newEntity = CFDictionaryCreate(NULL,
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks);
+       ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, newEntity);
+       CFRelease(newEntity);
+       newEntity = NULL;
+       if (!ok) {
+               goto done;
+       }
 
-               interfaceType = SCNetworkInterfaceGetInterfaceType(servicePrivate->interface);
-               childInterface = SCNetworkInterfaceGetInterface(servicePrivate->interface);
-               if (childInterface != NULL) {
-                       childInterfaceType = SCNetworkInterfaceGetInterfaceType(childInterface);
-               }
+       protocol  = SCNetworkServiceCopyProtocol(service, protocolType);
+       assert(protocol != NULL);
 
-               newEntity = __copyProtocolTemplate(interfaceType, childInterfaceType, protocolType);
+       newEntity = _protocolTemplate(service, protocolType);
+       assert(newEntity != NULL);
+       
+       ok = SCNetworkProtocolSetConfiguration(protocol, newEntity);
+       if (!ok) {
+               // could not set default configuration
+               goto done;
        }
 
-       if (newEntity == NULL) {
-               newEntity = CFDictionaryCreate(NULL,
-                                              NULL,
-                                              NULL,
-                                              0,
-                                              &kCFTypeDictionaryKeyCallBacks,
-                                              &kCFTypeDictionaryValueCallBacks);
+       newEnabled = !CFDictionaryContainsKey(newEntity, kSCResvInactive);
+       ok = SCNetworkProtocolSetEnabled(protocol, newEnabled);
+       if (!ok) {
+               // could not enable/disable protocol
+               goto done;
        }
 
-       ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, newEntity);
-       CFRelease(newEntity);
-
     done :
 
+       if (newEntity != NULL) CFRelease(newEntity);
+       if (protocol != NULL) CFRelease(protocol);
+
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkServiceAddProtocolType(): %@, %@", service, protocolType);
+       }
+
        CFRelease(path);
        return ok;
 }
@@ -259,25 +488,26 @@ SCNetworkServiceCopyAll(SCPreferencesRef prefs)
                        SCNetworkServicePrivateRef      servicePrivate;
 
                        if (!isA_CFDictionary(vals[i])) {
-                               SCLog(TRUE,
-                                     LOG_INFO,
-                                     CFSTR("SCNetworkServiceCopyAll(): error w/service \"%@\"\n"),
-                                     keys[i]);
+                               SC_log(LOG_INFO, "error w/service \"%@\"", keys[i]);
                                continue;
                        }
 
                        entity = CFDictionaryGetValue(vals[i], kSCEntNetInterface);
                        if (!isA_CFDictionary(entity)) {
                                // if no "interface"
-                               SCLog(TRUE,
-                                     LOG_INFO,
-                                     CFSTR("SCNetworkServiceCopyAll(): no \"%@\" entity for service \"%@\"\n"),
-                                     kSCEntNetInterface,
-                                     keys[i]);
+                               SC_log(LOG_INFO, "no \"%@\" entity for service \"%@\"",
+                                      kSCEntNetInterface,
+                                      keys[i]);
+                               continue;
+                       }
+
+                       if (__SCNetworkInterfaceEntityIsPPTP(entity)) {
+                               SC_log(LOG_INFO, "PPTP services are no longer supported");
                                continue;
                        }
 
-                       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, keys[i], NULL, prefs);
+                       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, prefs, keys[i], NULL);
+                       assert(servicePrivate != NULL);
                        CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate);
                        CFRelease(servicePrivate);
                }
@@ -291,8 +521,55 @@ SCNetworkServiceCopyAll(SCPreferencesRef prefs)
 }
 
 
+__private_extern__
+CFArrayRef /* of SCNetworkInterfaceRef's */
+__SCNetworkServiceCopyAllInterfaces(SCPreferencesRef prefs)
+{
+       CFMutableArrayRef interfaces = NULL;
+       CFArrayRef services = NULL;
+       CFIndex servicesCount = 0;
+       SCNetworkServiceRef service = NULL;
+       SCNetworkInterfaceRef interface = NULL;
+
+       services = SCNetworkServiceCopyAll(prefs);
+       if (services == NULL) {
+               goto done;
+       }
+
+       servicesCount = CFArrayGetCount(services);
+       if (servicesCount == 0) {
+               goto done;
+       }
+
+       interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+       for (CFIndex idx = 0; idx < servicesCount; idx++) {
+               service = CFArrayGetValueAtIndex(services, idx);
+               interface = SCNetworkServiceGetInterface(service);
+
+               if (isA_SCNetworkInterface(interface) == NULL) {
+                       continue;
+               }
+               CFArrayAppendValue(interfaces, interface);
+       }
+
+       if (CFArrayGetCount(interfaces) == 0) {
+               // Do not return an empty array
+               CFRelease(interfaces);
+               interfaces = NULL;
+       }
+
+    done:
+
+       if (services != NULL) {
+               CFRelease(services);
+       }
+       return  interfaces;
+}
+
+
 /*
- * build a list of all of a servives entity types that are associated
+ * build a list of all of a services entity types that are associated
  * with the services interface.  The list will include :
  *
  * - entity types associated with the interface type (Ethernet, FireWire, PPP, ...)
@@ -312,13 +589,12 @@ _copyInterfaceEntityTypes(CFDictionaryRef protocols)
                CFStringRef     entities[]      = { kSCPropNetInterfaceType,
                                                    kSCPropNetInterfaceSubType,
                                                    kSCPropNetInterfaceHardware };
-               int             i;
 
                // include the "Interface" entity itself
                CFSetAddValue(interface_entity_types, kSCEntNetInterface);
 
                // include the entities associated with the interface
-               for (i = 0; i < sizeof(entities)/sizeof(entities[0]); i++) {
+               for (size_t i = 0; i < sizeof(entities)/sizeof(entities[0]); i++) {
                        CFStringRef     entity;
 
                        entity = CFDictionaryGetValue(interface, entities[i]);
@@ -346,6 +622,11 @@ SCNetworkServiceCopy(SCPreferencesRef prefs, CFStringRef serviceID)
        CFStringRef                     path;
        SCNetworkServicePrivateRef      servicePrivate;
 
+       if (!isA_CFString(serviceID)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                     // allocator
                                                              serviceID,                // service
                                                              kSCEntNetInterface);      // entity
@@ -358,7 +639,32 @@ SCNetworkServiceCopy(SCPreferencesRef prefs, CFStringRef serviceID)
                return NULL;
        }
 
-       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, serviceID, NULL, prefs);
+       if (__SCNetworkInterfaceEntityIsPPTP(entity)) {
+               SC_log(LOG_INFO, "PPTP services are no longer supported");
+               _SCErrorSet(kSCStatusNoKey);
+               return NULL;
+       }
+
+       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, prefs, serviceID, NULL);
+       return (SCNetworkServiceRef)servicePrivate;
+}
+
+
+SCNetworkServiceRef
+_SCNetworkServiceCopyActive(SCDynamicStoreRef store, CFStringRef serviceID)
+{
+       SCNetworkServicePrivateRef      servicePrivate;
+
+       if (!isA_CFString(serviceID)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, NULL, serviceID, NULL);
+       assert(servicePrivate != NULL);
+       if (store != NULL) {
+               servicePrivate->store = CFRetain(store);
+       }
        return (SCNetworkServiceRef)servicePrivate;
 }
 
@@ -372,13 +678,23 @@ SCNetworkServiceCopyProtocol(SCNetworkServiceRef service, CFStringRef protocolTy
        SCNetworkProtocolPrivateRef     protocolPrivate = NULL;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (!isA_CFString(protocolType)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              NULL);                            // entity
        protocols = SCPreferencesPathGetValue(servicePrivate->prefs, path);
        CFRelease(path);
 
-       if ((protocols != NULL) && !isA_CFDictionary(protocols)) {
+       if (!isA_CFDictionary(protocols)) {
                // if corrupt prefs
                _SCErrorSet(kSCStatusFailed);
                return NULL;
@@ -417,6 +733,11 @@ SCNetworkServiceCopyProtocols(SCNetworkServiceRef service)
        CFDictionaryRef                 protocols;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              NULL);                            // entity
@@ -424,6 +745,8 @@ SCNetworkServiceCopyProtocols(SCNetworkServiceRef service)
        CFRelease(path);
 
        if (!isA_CFDictionary(protocols)) {
+               // if corrupt prefs
+               _SCErrorSet(kSCStatusFailed);
                return NULL;
        }
 
@@ -478,8 +801,7 @@ static Boolean
 __SCNetworkServiceSetInterfaceEntity(SCNetworkServiceRef     service,
                                     SCNetworkInterfaceRef   interface)
 {
-       CFMutableDictionaryRef          entity;
-       SCNetworkInterfacePrivateRef    interfacePrivate        = (SCNetworkInterfacePrivateRef)interface;
+       CFDictionaryRef                 entity;
        Boolean                         ok;
        CFStringRef                     path;
        SCNetworkServicePrivateRef      servicePrivate          = (SCNetworkServicePrivateRef)service;
@@ -487,41 +809,7 @@ __SCNetworkServiceSetInterfaceEntity(SCNetworkServiceRef     service,
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              kSCEntNetInterface);              // entity
-       entity = CFDictionaryCreateMutable(NULL,
-                                          0,
-                                          &kCFTypeDictionaryKeyCallBacks,
-                                          &kCFTypeDictionaryValueCallBacks);
-       if (interfacePrivate->entity_type != NULL) {
-               CFDictionarySetValue(entity,
-                                    kSCPropNetInterfaceType,
-                                    interfacePrivate->entity_type);
-       }
-       if (interfacePrivate->entity_subtype != NULL) {
-               CFDictionarySetValue(entity,
-                                    kSCPropNetInterfaceSubType,
-                                    interfacePrivate->entity_subtype);
-       }
-       if (interfacePrivate->entity_device != NULL) {
-               CFDictionarySetValue(entity,
-                                    kSCPropNetInterfaceDeviceName,
-                                    interfacePrivate->entity_device);
-       }
-       if (interfacePrivate->entity_hardware != NULL) {
-               CFDictionarySetValue(entity,
-                                    kSCPropNetInterfaceHardware,
-                                    interfacePrivate->entity_hardware);
-       }
-       if (CFEqual(interfacePrivate->interface_type, kSCNetworkInterfaceTypeModem) &&
-           interfacePrivate->supportsDeviceOnHold) {
-               int             one     = 1;
-               CFNumberRef     num;
-
-               num = CFNumberCreate(NULL, kCFNumberIntType, &one);
-               CFDictionarySetValue(entity,
-                                    kSCPropNetInterfaceSupportsModemOnHold,
-                                    num);
-               CFRelease(num);
-       }
+       entity = __SCNetworkInterfaceCopyInterfaceEntity(interface);
        ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, entity);
        CFRelease(entity);
        CFRelease(path);
@@ -535,15 +823,44 @@ SCNetworkServiceCreate(SCPreferencesRef prefs, SCNetworkInterfaceRef interface)
 {
        CFArrayRef                      components;
        CFArrayRef                      interface_config;
+       CFStringRef                     interface_name;
        SCNetworkInterfaceRef           newInterface;
        CFStringRef                     path;
        CFStringRef                     prefix;
        CFStringRef                     serviceID;
        SCNetworkServicePrivateRef      servicePrivate;
+       CFArrayRef                      supported_protocols;
+
+       if (!isA_SCNetworkInterface(interface)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       // only allow network interfaces which support one or more protocols
+       // to be added to a service.  The one exception is that we allow
+       // third-party interface types to be configured.
+       supported_protocols = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
+       if (supported_protocols == NULL) {
+               CFStringRef     interface_type;
+
+               interface_type = SCNetworkInterfaceGetInterfaceType(interface);
+               if (CFStringFind(interface_type, CFSTR("."), 0).location == kCFNotFound) {
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       return NULL;
+               }
+       }
+
+       // do not allow creation of a network service if the interface is a
+       // member of a bond or bridge
+       if (__SCNetworkInterfaceIsMember(prefs, interface)) {
+               _SCErrorSet(kSCStatusKeyExists);
+               return NULL;
+       }
 
        // establish the service
        prefix = SCPreferencesPathKeyCreateNetworkServices(NULL);
-       path = SCPreferencesPathCreateUniqueChild(prefs, prefix);
+       path = __SCPreferencesPathCreateUniqueChild_WithMoreSCFCompatibility(prefs, prefix);
+       if (path == NULL) path = SCPreferencesPathCreateUniqueChild(prefs, prefix);
        CFRelease(prefix);
        if (path == NULL) {
                return NULL;
@@ -553,13 +870,14 @@ SCNetworkServiceCreate(SCPreferencesRef prefs, SCNetworkInterfaceRef interface)
        CFRelease(path);
 
        serviceID = CFArrayGetValueAtIndex(components, 2);
-       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, serviceID, NULL, prefs);
+       servicePrivate = __SCNetworkServiceCreatePrivate(NULL, prefs, serviceID, NULL);
        CFRelease(components);
 
        // duplicate the interface and associate the copy with the new service
        newInterface = (SCNetworkInterfaceRef)__SCNetworkInterfaceCreateCopy(NULL,
                                                                             interface,
-                                                                            (SCNetworkServiceRef)servicePrivate);
+                                                                            prefs,
+                                                                            serviceID);
        servicePrivate->interface = newInterface;
 
        // establish "default" configuration(s) for the interface
@@ -572,14 +890,57 @@ SCNetworkServiceCreate(SCPreferencesRef prefs, SCNetworkInterfaceRef interface)
                CFStringRef             interfaceType;
 
                interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
-               childInterface = SCNetworkInterfaceGetInterface(servicePrivate->interface);
+               childInterface = SCNetworkInterfaceGetInterface(interface);
                if (childInterface != NULL) {
                        childInterfaceType = SCNetworkInterfaceGetInterfaceType(childInterface);
                }
 
                config = __copyInterfaceTemplate(interfaceType, childInterfaceType);
                if (config != NULL) {
-                       (void) __SCNetworkInterfaceSetConfiguration(interface, config, TRUE);
+                       if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBluetooth) ||
+                           CFEqual(interfaceType, kSCNetworkInterfaceTypeIrDA     ) ||
+                           CFEqual(interfaceType, kSCNetworkInterfaceTypeModem    ) ||
+                           CFEqual(interfaceType, kSCNetworkInterfaceTypeSerial   ) ||
+                           CFEqual(interfaceType, kSCNetworkInterfaceTypeWWAN     )) {
+                               CFDictionaryRef         overrides;
+
+                               overrides = __SCNetworkInterfaceGetTemplateOverrides(interface, kSCNetworkInterfaceTypeModem);
+
+                               // a ConnectionScript (and related keys) from the interface
+                               // should trump the settings from the configuration template.
+                               if (isA_CFDictionary(overrides)) {
+                                       CFMutableDictionaryRef  newConfig;
+
+                                       newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config);
+                                       if (CFDictionaryContainsKey(overrides, kSCPropNetModemConnectionScript)) {
+                                               CFDictionaryRemoveValue(newConfig, kSCPropNetModemConnectionPersonality);
+                                               CFDictionaryRemoveValue(newConfig, kSCPropNetModemConnectionScript);
+                                               CFDictionaryRemoveValue(newConfig, kSCPropNetModemDeviceVendor);
+                                               CFDictionaryRemoveValue(newConfig, kSCPropNetModemDeviceModel);
+                                       }
+                                       CFDictionaryApplyFunction(overrides, mergeDict, newConfig);
+                                       CFRelease(config);
+                                       config = newConfig;
+                               }
+                       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) ||
+                                  CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN)) {
+                               CFDictionaryRef         overrides;
+
+                               overrides = __SCNetworkInterfaceGetTemplateOverrides(interface, kSCNetworkInterfaceTypePPP);
+                               if (isA_CFDictionary(overrides)) {
+                                       CFMutableDictionaryRef  newConfig;
+
+                                       newConfig = CFDictionaryCreateMutableCopy(NULL, 0, config);
+                                       CFDictionaryApplyFunction(overrides, mergeDict, newConfig);
+                                       CFRelease(config);
+                                       config = newConfig;
+                               }
+                       }
+
+                       if (!__SCNetworkInterfaceSetConfiguration(interface, NULL, config, TRUE)) {
+                               SC_log(LOG_INFO, "__SCNetworkInterfaceSetConfiguration failed(), interface=%@, type=NULL",
+                                      interface);
+                       }
                        CFRelease(config);
                }
        }
@@ -588,14 +949,96 @@ SCNetworkServiceCreate(SCPreferencesRef prefs, SCNetworkInterfaceRef interface)
        (void) __SCNetworkServiceSetInterfaceEntity((SCNetworkServiceRef)servicePrivate,
                                                    servicePrivate->interface);
 
-       // push the [deep] interface configuration into into the service.
-       interface_config = __SCNetworkInterfaceCopyDeepConfiguration(servicePrivate->interface);
-       __SCNetworkInterfaceSetDeepConfiguration(servicePrivate->interface, interface_config);
+       // push the [deep] interface configuration into the service.
+       interface_config = __SCNetworkInterfaceCopyDeepConfiguration(NULL, servicePrivate->interface);
+       __SCNetworkInterfaceSetDeepConfiguration(NULL, servicePrivate->interface, interface_config);
+       if (interface_config != NULL) CFRelease(interface_config);
+
+       // set the service name to match that of the associated interface
+       //
+       // Note: It might seem a bit odd to call SCNetworkServiceGetName
+       // followed by an immediate call to SCNetworkServiceSetName.  The
+       // trick here is that if no name has previously been set, the
+       // "get" function will return the name of the associated interface.
+       //
+       // ... and we "set" a name to ensure that applications that do
+       // not use the APIs will still find a UserDefinedName property
+       // in the SCDynamicStore.
+       //
+       interface_name = SCNetworkServiceGetName((SCNetworkServiceRef)servicePrivate);
+       if (interface_name != NULL) {
+               (void) SCNetworkServiceSetName((SCNetworkServiceRef)servicePrivate,
+                                              interface_name);
+       }
+
+       SC_log(LOG_DEBUG, "SCNetworkServiceCreate(): %@", servicePrivate);
 
        return (SCNetworkServiceRef)servicePrivate;
 }
 
 
+Boolean
+SCNetworkServiceEstablishDefaultConfiguration(SCNetworkServiceRef service)
+{
+       CFIndex                         i;
+       SCNetworkInterfaceRef           interface;
+       CFIndex                         n;
+       Boolean                         ok;
+       CFArrayRef                      protocolTypes;
+       CFStringRef                     rankStr;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       interface = SCNetworkServiceGetInterface(service);
+       if (interface == NULL) {
+               return FALSE;
+       }
+
+       protocolTypes = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
+       n = (protocolTypes != NULL) ? CFArrayGetCount(protocolTypes) : 0;
+       for (i = 0; i < n; i++) {
+               CFStringRef     protocolType;
+
+               protocolType = CFArrayGetValueAtIndex(protocolTypes, i);
+               ok = SCNetworkServiceAddProtocolType(service, protocolType);
+               if (!ok) {
+                       SC_log(LOG_INFO,
+                              "SCNetworkServiceEstablishDefaultConfiguration(): could not add protocol \"%@\"",
+                              protocolType);
+               }
+       }
+
+       rankStr = __SCNetworkInterfaceGetTemplateOverrides(interface, kSCPropNetServicePrimaryRank);
+       if (isA_CFString(rankStr)) {
+               SCNetworkServicePrimaryRank     rank;
+
+               ok = __str_to_rank(rankStr, &rank);
+               if (!ok) {
+                       SC_log(LOG_INFO,
+                              "SCNetworkServiceEstablishDefaultConfiguration(): unknown rank \"%@\"",
+                              rankStr);
+                       goto done;
+               }
+
+               ok = SCNetworkServiceSetPrimaryRank(service, rank);
+               if (!ok) {
+                       SC_log(LOG_INFO,
+                              "SCNetworkServiceEstablishDefaultConfiguration(): could not set rank \"%@\"",
+                              rankStr);
+                       goto done;
+               }
+       }
+
+    done :
+
+       return TRUE;
+}
+
+
 Boolean
 SCNetworkServiceGetEnabled(SCNetworkServiceRef service)
 {
@@ -603,6 +1046,11 @@ SCNetworkServiceGetEnabled(SCNetworkServiceRef service)
        CFStringRef                     path;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              NULL);                            // entity
@@ -618,6 +1066,11 @@ SCNetworkServiceGetInterface(SCNetworkServiceRef service)
 {
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
        if (servicePrivate->interface == NULL) {
                CFDictionaryRef entity;
                CFStringRef     path;
@@ -629,7 +1082,7 @@ SCNetworkServiceGetInterface(SCNetworkServiceRef service)
                CFRelease(path);
 
                if (isA_CFDictionary(entity)) {
-                       servicePrivate->interface = __SCNetworkInterfaceCreateWithEntity(NULL, entity, service);
+                       servicePrivate->interface = _SCNetworkInterfaceCreateWithEntity(NULL, entity, service);
                }
        }
 
@@ -641,9 +1094,20 @@ CFStringRef
 SCNetworkServiceGetName(SCNetworkServiceRef service)
 {
        CFDictionaryRef                 entity;
-       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+       SCNetworkInterfaceRef           interface;
        CFStringRef                     name            = NULL;
        CFStringRef                     path;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+       Boolean                         useSystemInterfaces = TRUE;
+
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (servicePrivate->name != NULL) {
+               return servicePrivate->name;
+       }
 
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
@@ -651,19 +1115,143 @@ SCNetworkServiceGetName(SCNetworkServiceRef service)
        entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
        CFRelease(path);
 
+       useSystemInterfaces = ((__SCPreferencesUsingDefaultPrefs(servicePrivate->prefs)) &&
+                              !__SCPreferencesGetLimitSCNetworkConfiguration(servicePrivate->prefs));
+
        if (isA_CFDictionary(entity)) {
                name = CFDictionaryGetValue(entity, kSCPropUserDefinedName);
+               if (isA_CFString(name)) {
+                       servicePrivate->name = CFRetain(name);
+                       if (!useSystemInterfaces) {
+                               return servicePrivate->name;
+                       }
+               }
        }
 
-       return isA_CFString(name) ? name : NULL;
-}
+       interface = SCNetworkServiceGetInterface(service);
+       while (interface != NULL) {
+               SCNetworkInterfaceRef   childInterface;
+               CFStringRef             interfaceType;
 
+               interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
+               if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN)) {
+                       break;
+               }
 
-CFStringRef
+               childInterface = SCNetworkInterfaceGetInterface(interface);
+               if ((childInterface == NULL) ||
+                   CFEqual(childInterface, kSCNetworkInterfaceIPv4)) {
+                       break;
+               }
+
+               interface = childInterface;
+       }
+
+       if (interface != NULL) {
+               int             i;
+               CFStringRef     interface_name  = NULL;
+               CFStringRef     suffix          = NULL;
+
+               //
+               // check if the [stored] service name matches the non-localized interface
+               // name.  If so, return the localized name.
+               //
+               // Also, the older "Built-in XXX" interface names are too long for the
+               // current UI. If we find that the [stored] service name matches the older
+               // name, return the newer (and shorter) localized name.
+               //
+               // Note: the user/admin will no longer be able to set the service name
+               //       to "Built-in Ethernet".
+               //
+               for (i = 0; i < 3; i++) {
+                       if (servicePrivate->name == NULL) {
+                               // if no [stored] service name to compare
+                               break;
+                       }
+
+                       switch (i) {
+                               case 0 :
+                                       // compare the non-localized interface name
+                                       interface_name = __SCNetworkInterfaceGetNonLocalizedDisplayName(interface);
+                                       if (interface_name != NULL) {
+                                               CFRetain(interface_name);
+                                       }
+                                       break;
+#if    !TARGET_OS_IPHONE
+                               case 1 :
+                                       // compare the older "Built-in XXX" localized name
+                                       interface_name = __SCNetworkInterfaceCopyXLocalizedDisplayName(interface);
+                                       break;
+                               case 2 :
+                                       // compare the older "Built-in XXX" non-localized name
+                                       interface_name = __SCNetworkInterfaceCopyXNonLocalizedDisplayName(interface);
+                                       break;
+#else  // !TARGET_OS_IPHONE
+                               default :
+                                       continue;
+#endif // !TARGET_OS_IPHONE
+                       }
+
+                       if (interface_name != NULL) {
+                               Boolean match   = FALSE;
+
+                               if (CFEqual(name, interface_name)) {
+                                       // if service name matches the OLD localized
+                                       // interface name
+                                       match = TRUE;
+                               } else if (CFStringHasPrefix(name, interface_name)) {
+                                       CFIndex prefixLen       = CFStringGetLength(interface_name);
+                                       CFIndex suffixLen       = CFStringGetLength(name);
+
+                                       suffix = CFStringCreateWithSubstring(NULL,
+                                                                            name,
+                                                                            CFRangeMake(prefixLen, suffixLen - prefixLen));
+                                       match = TRUE;
+                               }
+                               CFRelease(interface_name);
+
+                               if (match) {
+                                       CFRelease(servicePrivate->name);
+                                       servicePrivate->name = NULL;
+                                       break;
+                               }
+                       }
+               }
+
+               //
+               // if the service name has not been set, use the localized interface name
+               //
+               if (servicePrivate->name == NULL) {
+                       interface_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+                       if (interface_name != NULL) {
+                               if (suffix != NULL) {
+                                       servicePrivate->name = CFStringCreateWithFormat(NULL,
+                                                                                       NULL,
+                                                                                       CFSTR("%@%@"),
+                                                                                       interface_name,
+                                                                                       suffix);
+                               } else {
+                                       servicePrivate->name = CFRetain(interface_name);
+                               }
+                       }
+               }
+               if (suffix != NULL) CFRelease(suffix);
+       }
+
+       return servicePrivate->name;
+}
+
+
+CFStringRef
 SCNetworkServiceGetServiceID(SCNetworkServiceRef service)
 {
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
        return servicePrivate->serviceID;
 }
 
@@ -680,25 +1268,37 @@ Boolean
 SCNetworkServiceRemove(SCNetworkServiceRef service)
 {
        Boolean                         ok              = FALSE;
+       CFStringRef                     path;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
        CFArrayRef                      sets;
-       CFStringRef                     path;
+
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceRemove() w/removed service\n  service = %@", service);
+               _SC_crash_once("SCNetworkServiceRemove() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
 
        // remove service from all sets
 
        sets = SCNetworkSetCopyAll(servicePrivate->prefs);
        if (sets != NULL) {
-               CFIndex i;
                CFIndex n;
 
                n = CFArrayGetCount(sets);
-               for (i = 0; i < n; i++) {
+               for (CFIndex i = 0; i < n; i++) {
                        SCNetworkSetRef set;
 
                        set = CFArrayGetValueAtIndex(sets, i);
                        ok = SCNetworkSetRemoveService(set, service);
                        if (!ok && (SCError() != kSCStatusNoKey)) {
-                               break;
+                               CFRelease(sets);
+                               return ok;
                        }
                }
                CFRelease(sets);
@@ -712,6 +1312,10 @@ SCNetworkServiceRemove(SCNetworkServiceRef service)
        ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, path);
        CFRelease(path);
 
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkServiceRemove(): %@", service);
+       }
+
        return ok;
 }
 
@@ -724,6 +1328,25 @@ SCNetworkServiceRemoveProtocolType(SCNetworkServiceRef service, CFStringRef prot
        CFStringRef                     path;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceRemoveProtocolType() w/removed service\n  service = %@\n  protocol = %@",
+                      service,
+                      protocolType);
+               _SC_crash_once("SCNetworkServiceRemoveProtocolType() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkProtocolIsValidType(protocolType)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              protocolType);                    // entity
@@ -739,6 +1362,10 @@ SCNetworkServiceRemoveProtocolType(SCNetworkServiceRef service, CFStringRef prot
 
     done :
 
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkServiceRemoveProtocolType(): %@, %@", service, protocolType);
+       }
+
        CFRelease(path);
        return ok;
 }
@@ -751,12 +1378,43 @@ SCNetworkServiceSetEnabled(SCNetworkServiceRef service, Boolean enabled)
        CFStringRef                     path;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceSetEnabled() w/removed service\n  service = %@", service);
+               _SC_crash_once("SCNetworkProtocolSetEnabled() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       // make sure that we do not enable a network service if the
+       // associated interface is a member of a bond or bridge.
+       if (enabled) {
+               SCNetworkInterfaceRef   interface;
+
+               interface = SCNetworkServiceGetInterface(service);
+               if ((interface != NULL) &&
+                   __SCNetworkInterfaceIsMember(servicePrivate->prefs, interface)) {
+                       _SCErrorSet(kSCStatusKeyExists);
+                       return FALSE;
+               }
+       }
+
        path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
                                                              servicePrivate->serviceID,        // service
                                                              NULL);                            // entity
        ok = __setPrefsEnabled(servicePrivate->prefs, path, enabled);
        CFRelease(path);
 
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkServiceSetEnabled(): %@ -> %s",
+                      service,
+                      enabled ? "Enabled" : "Disabled");
+       }
+
        return ok;
 }
 
@@ -767,11 +1425,88 @@ SCNetworkServiceSetName(SCNetworkServiceRef service, CFStringRef name)
        CFDictionaryRef                 entity;
        Boolean                         ok              = FALSE;
        CFStringRef                     path;
+       CFStringRef                     saveName        = NULL;
        SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
 
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceSetName() w/removed service\n  service = %@\n  name = %@",
+                      service,
+                      name != NULL ? name : CFSTR("<NULL>"));
+               _SC_crash_once("SCNetworkServiceSetName() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (name != NULL) {
+               if (!isA_CFString(name)) {
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       return FALSE;
+               }
+               saveName = CFRetain(name);
+       }
+
+       if (name != NULL) {
+               SCNetworkInterfaceRef   interface;
+
+               interface = SCNetworkServiceGetInterface(service);
+               while (interface != NULL) {
+                       SCNetworkInterfaceRef   childInterface;
+
+                       childInterface = SCNetworkInterfaceGetInterface(interface);
+                       if (childInterface == NULL) {
+                               break;
+                       }
+
+                       interface = childInterface;
+               }
+
+               if (interface != NULL) {
+                       CFStringRef     interface_name;
+
+                       interface_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+                       if (interface_name != NULL) {
+                               if (CFEqual(name, interface_name)) {
+                                       // if service name matches the localized interface name
+                                       // then store the non-localized name.
+                                       interface_name = __SCNetworkInterfaceGetNonLocalizedDisplayName(interface);
+                                       if (interface_name != NULL) {
+                                               CFRelease(saveName);
+                                               saveName = CFRetain(interface_name);
+                                       }
+                               } else if (CFStringHasPrefix(name, interface_name)) {
+                                       CFIndex         prefixLen       = CFStringGetLength(interface_name);
+                                       CFStringRef     suffix;
+                                       CFIndex         suffixLen       = CFStringGetLength(name);
+
+                                       // if service name matches the localized interface name plus
+                                       // a few extra characters) then store the non-localized name with
+                                       // the same suffix.
+                                       suffix = CFStringCreateWithSubstring(NULL,
+                                                                            name,
+                                                                            CFRangeMake(prefixLen, suffixLen - prefixLen));
+                                       interface_name = __SCNetworkInterfaceGetNonLocalizedDisplayName(interface);
+                                       if (interface_name != NULL) {
+                                               CFRelease(saveName);
+                                               saveName = CFStringCreateWithFormat(NULL,
+                                                                                   NULL,
+                                                                                   CFSTR("%@%@"),
+                                                                                   interface_name,
+                                                                                   suffix);
+                                       }
+                                       CFRelease(suffix);
+                               }
+                       }
+               }
+       }
+
 #define PREVENT_DUPLICATE_SERVICE_NAMES
 #ifdef  PREVENT_DUPLICATE_SERVICE_NAMES
-       if (isA_CFString(name)) {
+       if (name != NULL) {
                CFArrayRef      sets;
 
                // ensure that each service is uniquely named within its sets
@@ -822,6 +1557,7 @@ SCNetworkServiceSetName(SCNetworkServiceRef service, CFStringRef name)
                                         * the "name" is not unique.
                                         */
                                        CFRelease(sets);
+                                       if (saveName != NULL) CFRelease(saveName);
                                        _SCErrorSet(kSCStatusKeyExists);
                                        return FALSE;
                                }
@@ -836,20 +1572,20 @@ SCNetworkServiceSetName(SCNetworkServiceRef service, CFStringRef name)
                                                              servicePrivate->serviceID,        // service
                                                              NULL);                            // entity
        entity = SCPreferencesPathGetValue(servicePrivate->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 (isA_CFString(name)) {
-                       CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name);
+               if (entity != NULL) {
+                       newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+               } else {
+                       newEntity = CFDictionaryCreateMutable(NULL,
+                                                             0,
+                                                             &kCFTypeDictionaryKeyCallBacks,
+                                                             &kCFTypeDictionaryValueCallBacks);
+               }
+               if (saveName != NULL) {
+                       CFDictionarySetValue(newEntity, kSCPropUserDefinedName, saveName);
                } else {
                        CFDictionaryRemoveValue(newEntity, kSCPropUserDefinedName);
                }
@@ -857,6 +1593,964 @@ SCNetworkServiceSetName(SCNetworkServiceRef service, CFStringRef name)
                CFRelease(newEntity);
        }
        CFRelease(path);
+       if (saveName != NULL) CFRelease(saveName);
+
+       if (servicePrivate->name != NULL) CFRelease(servicePrivate->name);
+       if (name != NULL) CFRetain(name);
+       servicePrivate->name = name;
+
+       if (ok) {
+               SC_log(LOG_DEBUG, "SCNetworkServiceSetName(): %@", service);
+       }
+
+       return ok;
+}
+
+
+#pragma mark -
+#pragma mark SCNetworkService SPIs
+
+
+__private_extern__
+Boolean
+__SCNetworkServiceExists(SCNetworkServiceRef service)
+{
+       CFDictionaryRef                 entity;
+       CFStringRef                     path;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+
+       if (servicePrivate->prefs == NULL) {
+               // if no prefs
+               return FALSE;
+       }
+
+       path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
+                                                             servicePrivate->serviceID,        // service
+                                                             kSCEntNetInterface);               // entity
+       entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
+       CFRelease(path);
+
+       if (!isA_CFDictionary(entity)) {
+               // a "service" must have an "interface"
+               return FALSE;
+       }
+
+       return TRUE;
+}
 
+
+SCNetworkServicePrimaryRank
+SCNetworkServiceGetPrimaryRank(SCNetworkServiceRef service)
+{
+       CFDictionaryRef                 entity;
+       Boolean                         ok              = TRUE;
+       CFStringRef                     path;
+       SCNetworkServicePrimaryRank     rank            = kSCNetworkServicePrimaryRankDefault;
+       CFStringRef                     rankStr         = NULL;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+
+       if (!isA_SCNetworkService(service)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return rank;
+       }
+
+       if (servicePrivate->prefs != NULL) {
+               path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+                                                                     servicePrivate->serviceID,
+                                                                     NULL);
+               entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
+               CFRelease(path);
+               if (isA_CFDictionary(entity)) {
+                       rankStr = CFDictionaryGetValue(entity, kSCPropNetServicePrimaryRank);
+                       ok = __str_to_rank(rankStr, &rank);
+               }
+       } else if (servicePrivate->store != NULL) {
+               path = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+                                                                  kSCDynamicStoreDomainState,
+                                                                  servicePrivate->serviceID,
+                                                                  NULL);
+               entity = SCDynamicStoreCopyValue(servicePrivate->store, path);
+               CFRelease(path);
+               if (entity != NULL) {
+                       if (isA_CFDictionary(entity)) {
+                               rankStr = CFDictionaryGetValue(entity, kSCPropNetServicePrimaryRank);
+                               ok = __str_to_rank(rankStr, &rank);
+                       }
+                       CFRelease(entity);
+               }
+       } else {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return rank;
+       }
+
+       if (!ok) {
+               rank = kSCNetworkServicePrimaryRankDefault;
+               _SCErrorSet(kSCStatusInvalidArgument);
+       } else if (rank == kSCNetworkServicePrimaryRankDefault) {
+               _SCErrorSet(kSCStatusOK);
+       }
+
+       return rank;
+}
+
+
+Boolean
+SCNetworkServiceSetPrimaryRank(SCNetworkServiceRef             service,
+                              SCNetworkServicePrimaryRank      newRank)
+{
+       Boolean                         ok;
+       CFDictionaryRef                 entity;
+       CFMutableDictionaryRef          newEntity;
+       CFStringRef                     path            = NULL;
+       CFStringRef                     rankStr         = NULL;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+
+       if (!isA_SCNetworkService(service)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if ((servicePrivate->prefs != NULL) && !__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceSetPrimaryRank() w/removed\n  service = %@", service);
+               _SC_crash_once("SCNetworkServiceSetPrimaryRank() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       ok = __rank_to_str(newRank, &rankStr);
+       if (!ok) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (servicePrivate->prefs != NULL) {
+               switch (newRank) {
+               case kSCNetworkServicePrimaryRankDefault:
+               case kSCNetworkServicePrimaryRankNever:
+               case kSCNetworkServicePrimaryRankScoped:
+                       path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+                                                                             servicePrivate->serviceID,
+                                                                             NULL);
+                       entity = SCPreferencesPathGetValue(servicePrivate->prefs, path);
+                       if (entity != NULL) {
+                               if (!isA_CFDictionary(entity)) {
+                                       // if corrupt prefs
+                                       _SCErrorSet(kSCStatusFailed);
+                                       goto done;
+                               }
+                               newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+                       } else {
+                               newEntity = CFDictionaryCreateMutable(NULL,
+                                                                     0,
+                                                                     &kCFTypeDictionaryKeyCallBacks,
+                                                                     &kCFTypeDictionaryValueCallBacks);
+                       }
+                       if (rankStr != NULL) {
+                               CFDictionarySetValue(newEntity, kSCPropNetServicePrimaryRank, rankStr);
+                       } else {
+                               CFDictionaryRemoveValue(newEntity, kSCPropNetServicePrimaryRank);
+                       }
+                       if (CFDictionaryGetCount(newEntity) > 0) {
+                               ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, newEntity);
+                       } else {
+                               ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, path);
+                       }
+                       CFRelease(newEntity);
+                       if (!ok) {
+                               goto done;
+                       }
+                       break;
+               default:
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       return FALSE;
+               }
+       } else if (servicePrivate->store != NULL) {
+               path = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+                                                                  kSCDynamicStoreDomainState,
+                                                                  servicePrivate->serviceID,
+                                                                  NULL);
+               entity = SCDynamicStoreCopyValue(servicePrivate->store, path);
+               if (entity != NULL) {
+                       if (!isA_CFDictionary(entity)) {
+                               // if corrupt prefs
+                               CFRelease(entity);
+                               _SCErrorSet(kSCStatusFailed);
+                               goto done;
+                       }
+                       newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
+                       CFRelease(entity);
+               } else {
+                       newEntity = CFDictionaryCreateMutable(NULL,
+                                                             0,
+                                                             &kCFTypeDictionaryKeyCallBacks,
+                                                             &kCFTypeDictionaryValueCallBacks);
+               }
+               if (rankStr != NULL) {
+                       CFDictionarySetValue(newEntity, kSCPropNetServicePrimaryRank, rankStr);
+               } else {
+                       CFDictionaryRemoveValue(newEntity, kSCPropNetServicePrimaryRank);
+               }
+               if (CFDictionaryGetCount(newEntity) > 0) {
+                       ok = SCDynamicStoreSetValue(servicePrivate->store, path, newEntity);
+               } else {
+                       ok = SCDynamicStoreRemoveValue(servicePrivate->store, path);
+               }
+               CFRelease(newEntity);
+               if (!ok) {
+                       goto done;
+               }
+       } else {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+    done :
+
+       if (path != NULL)       CFRelease(path);
        return ok;
 }
+
+
+Boolean
+_SCNetworkServiceIsVPN(SCNetworkServiceRef service)
+{
+       SCNetworkInterfaceRef   interface;
+       CFStringRef             interfaceType;
+
+       interface = SCNetworkServiceGetInterface(service);
+       if (interface == NULL) {
+               return FALSE;
+       }
+
+       interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
+       if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
+               interface = SCNetworkInterfaceGetInterface(interface);
+               if (interface == NULL) {
+                       return FALSE;
+               }
+
+               interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
+               if (CFEqual(interfaceType, kSCNetworkInterfaceTypeL2TP)) {
+                       return TRUE;
+               }
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+               if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPTP)) {
+                       return TRUE;
+               }
+#pragma GCC diagnostic pop
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN)) {
+               return TRUE;
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+Boolean
+SCNetworkServiceSetExternalID(SCNetworkServiceRef service, CFStringRef identifierDomain, CFStringRef identifier)
+{
+       CFStringRef                     prefs_path;
+       CFDictionaryRef                 service_dictionary;
+       SCNetworkServicePrivateRef      servicePrivate          = (SCNetworkServicePrivateRef)service;
+       Boolean                         success                 = FALSE;
+       CFStringRef                     prefixed_domain;
+
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL) || !isA_CFString(identifierDomain)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "SCNetworkServiceSetExternalID() w/removed\n  service = %@\n  id = %@",
+                      service,
+                      identifier);
+               _SC_crash_once("SCNetworkServiceSetExternalID() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (identifier != NULL && !isA_CFString(identifier)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       prefixed_domain = CFStringCreateWithFormat(NULL, 0, CFSTR("%s%@"), EXTERNAL_ID_DOMAIN_PREFIX, identifierDomain);
+
+       prefs_path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+                                                                   servicePrivate->serviceID,
+                                                                   NULL);
+
+       service_dictionary = SCPreferencesPathGetValue(servicePrivate->prefs, prefs_path);
+       if (isA_CFDictionary(service_dictionary) || ((service_dictionary == NULL) && (identifier != NULL))) {
+               CFMutableDictionaryRef  new_service_dictionary;
+
+               if (service_dictionary != NULL) {
+                       new_service_dictionary = CFDictionaryCreateMutableCopy(NULL, 0, service_dictionary);
+               } else {
+                       new_service_dictionary = CFDictionaryCreateMutable(NULL,
+                                                                          0,
+                                                                          &kCFTypeDictionaryKeyCallBacks,
+                                                                          &kCFTypeDictionaryValueCallBacks);
+               }
+
+               if (identifier != NULL) {
+                       CFDictionarySetValue(new_service_dictionary, prefixed_domain, identifier);
+               } else {
+                       CFDictionaryRemoveValue(new_service_dictionary, prefixed_domain);
+               }
+               success = SCPreferencesPathSetValue(servicePrivate->prefs, prefs_path, new_service_dictionary);
+               CFRelease(new_service_dictionary);
+       }
+       CFRelease(prefs_path);
+
+       if (identifier != NULL) {
+           if (servicePrivate->externalIDs == NULL) {
+                       servicePrivate->externalIDs = CFDictionaryCreateMutable(NULL,
+                                                                                0,
+                                                                                &kCFTypeDictionaryKeyCallBacks,
+                                                                                &kCFTypeDictionaryValueCallBacks);
+           }
+           CFDictionarySetValue(servicePrivate->externalIDs, prefixed_domain, identifier);
+       } else {
+           if (servicePrivate->externalIDs != NULL) {
+                       CFDictionaryRemoveValue(servicePrivate->externalIDs, prefixed_domain);
+           }
+       }
+
+       CFRelease(prefixed_domain);
+
+       if (!success) {
+               _SCErrorSet(kSCStatusFailed);
+       }
+
+       return success;
+}
+
+
+CFStringRef
+SCNetworkServiceCopyExternalID(SCNetworkServiceRef service, CFStringRef identifierDomain)
+{
+       CFStringRef                     identifier              = NULL;
+       CFStringRef                     prefixed_domain;
+       SCNetworkServicePrivateRef      service_private         = (SCNetworkServicePrivateRef)service;
+
+       if (!isA_SCNetworkService(service) || (service_private->prefs == NULL) || !isA_CFString(identifierDomain)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       prefixed_domain = CFStringCreateWithFormat(NULL, 0, CFSTR("%s%@"), EXTERNAL_ID_DOMAIN_PREFIX, identifierDomain);
+
+       if (service_private->externalIDs != NULL) {
+               identifier = CFDictionaryGetValue(service_private->externalIDs, prefixed_domain);
+               if (identifier != NULL) {
+                       CFRetain(identifier);
+               }
+       }
+
+       if (identifier == NULL) {
+               CFStringRef             prefs_path;
+               CFDictionaryRef         service_dictionary;
+
+               prefs_path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,
+                                                                           service_private->serviceID,
+                                                                           NULL);
+
+               service_dictionary = SCPreferencesPathGetValue(service_private->prefs, prefs_path);
+               if (isA_CFDictionary(service_dictionary)) {
+                       identifier = CFDictionaryGetValue(service_dictionary, prefixed_domain);
+                       if (identifier != NULL) {
+                               CFRetain(identifier);
+                               if (service_private->externalIDs == NULL) {
+                                       service_private->externalIDs = CFDictionaryCreateMutable(NULL,
+                                                                                                0,
+                                                                                                &kCFTypeDictionaryKeyCallBacks,
+                                                                                                &kCFTypeDictionaryValueCallBacks);
+                               }
+                               CFDictionarySetValue(service_private->externalIDs, prefixed_domain, identifier);
+                       }
+               }
+               CFRelease(prefs_path);
+       }
+
+       CFRelease(prefixed_domain);
+
+       if (identifier == NULL) {
+               _SCErrorSet(kSCStatusNoKey);
+       }
+
+       return identifier;
+}
+
+
+typedef struct {
+       CFStringRef     oldServiceID;
+       CFStringRef     newServiceID;
+} serviceContext, *serviceContextRef;
+
+
+static void
+replaceServiceID(const void *value, void *context)
+{
+       CFStringRef             link            = NULL;
+       CFStringRef             oldLink;
+       CFMutableArrayRef       newServiceOrder;
+       CFStringRef             path;
+       serviceContextRef       service_context = (serviceContextRef)context;
+       CFArrayRef              serviceOrder    = NULL;
+       SCNetworkSetRef         set             = (SCNetworkSetRef)value;
+       SCNetworkSetPrivateRef  setPrivate      = (SCNetworkSetPrivateRef)set;
+
+       // update service order
+       serviceOrder = SCNetworkSetGetServiceOrder(set);
+       if ((isA_CFArray(serviceOrder) != NULL) &&
+           CFArrayContainsValue(serviceOrder,
+                                 CFRangeMake(0, CFArrayGetCount(serviceOrder)),
+                                 service_context->oldServiceID)) {
+               CFIndex count;
+               CFIndex serviceOrderIndex;
+
+               // replacing all instances of old service ID with new one
+               newServiceOrder = CFArrayCreateMutableCopy(NULL, 0, serviceOrder);
+               count = CFArrayGetCount(newServiceOrder);
+               for (serviceOrderIndex = 0; serviceOrderIndex < count; serviceOrderIndex++) {
+                       CFStringRef     serviceID;
+
+                       serviceID = CFArrayGetValueAtIndex(newServiceOrder, serviceOrderIndex);
+                       if (CFEqual(serviceID, service_context->oldServiceID)) {
+                               CFArraySetValueAtIndex(newServiceOrder, serviceOrderIndex, service_context->newServiceID);
+                       }
+               }
+               SCNetworkSetSetServiceOrder(set, newServiceOrder);
+               CFRelease(newServiceOrder);
+       }
+
+       // check if service with old serviceID is part of the set
+       path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,                          // allocator
+                                                                setPrivate->setID,             // set
+                                                                service_context->oldServiceID, // service
+                                                                NULL);                         // entity
+       oldLink = SCPreferencesPathGetLink(setPrivate->prefs, path);
+       if (oldLink == NULL) {
+               // don't make any changes if service with old serviceID is not found
+               goto done;
+       }
+
+       // remove link between "set" and old "service"
+       (void) SCPreferencesPathRemoveValue(setPrivate->prefs, path);
+       CFRelease(path);
+
+       // create the link between "set" and the "service"
+       path = SCPreferencesPathKeyCreateSetNetworkServiceEntity(NULL,                          // allocator
+                                                                setPrivate->setID,             // set
+                                                                service_context->newServiceID, // service
+                                                                NULL);                         // entity
+       link = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                             // allocator
+                                                             service_context->newServiceID,    // service
+                                                             NULL);                            // entity
+       (void) SCPreferencesPathSetLink(setPrivate->prefs, path, link);
+
+    done:
+
+       if (path != NULL) {
+               CFRelease(path);
+       }
+       if (link != NULL) {
+               CFRelease(link);
+       }
+
+       return;
+}
+
+
+Boolean
+_SCNetworkServiceSetServiceID(SCNetworkServiceRef service, CFStringRef newServiceID)
+{
+       CFArrayRef                      allSets         = NULL;
+       CFDictionaryRef                 entity;
+       CFStringRef                     newPath;
+       Boolean                         ok              = FALSE;
+       CFStringRef                     oldPath         = NULL;
+       serviceContext                  service_context;
+       SCNetworkServicePrivateRef      servicePrivate  = (SCNetworkServicePrivateRef)service;
+
+       if (!isA_SCNetworkService(service) || (servicePrivate->prefs == NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!isA_CFString(newServiceID)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (CFEqual(newServiceID, servicePrivate->serviceID)) {
+               // no work needs to be done if new service ID is equal to current service ID
+               return TRUE;
+       }
+
+       if (!__SCNetworkServiceExists(service)) {
+               SC_log(LOG_ERR, "_SCNetworkServiceSetServiceID() w/removed service\n  service = %@\n  serviceID = %@",
+                      service,
+                      newServiceID);
+               _SC_crash_once("_SCNetworkServiceSetServiceID() w/removed service", NULL, NULL);
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       newPath = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                          // allocator
+                                                                newServiceID,                  // service
+                                                                NULL);                         // entity
+       entity = SCPreferencesPathGetValue(servicePrivate->prefs, newPath);
+       if (isA_CFDictionary(entity)) {
+               // if the new service already exists
+               _SCErrorSet(kSCStatusKeyExists);
+               goto done;
+       }
+
+       oldPath = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL,                          // allocator
+                                                                servicePrivate->serviceID,     // service
+                                                                NULL);                         // entity
+       entity = SCPreferencesPathGetValue(servicePrivate->prefs, oldPath);
+       if (!isA_CFDictionary(entity)) {
+               // if the service has already been removed
+               _SCErrorSet(kSCStatusNoKey);
+               goto done;
+       }
+
+       ok = SCPreferencesPathSetValue(servicePrivate->prefs, newPath, entity);
+       if (!ok) goto done;
+
+       ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, oldPath);
+       if (!ok) goto done;
+
+       allSets = SCNetworkSetCopyAll(servicePrivate->prefs);
+
+       service_context.newServiceID = newServiceID;
+       service_context.oldServiceID = servicePrivate->serviceID;
+
+       // find all sets w/oldServiceID and update
+       // ... and update the serviceOrder
+       CFArrayApplyFunction(allSets,
+                            CFRangeMake(0, CFArrayGetCount(allSets)),
+                            replaceServiceID,
+                            &service_context);
+
+       if (servicePrivate->interface != NULL) {
+               SCNetworkInterfaceRef           newInterface;
+
+               // duplicate the interface and associate the copy with the new service ID
+               newInterface = (SCNetworkInterfaceRef)__SCNetworkInterfaceCreateCopy(NULL,
+                                                                                    servicePrivate->interface,
+                                                                                    servicePrivate->prefs,
+                                                                                    newServiceID);
+               CFRelease(servicePrivate->interface);
+               servicePrivate->interface = newInterface;
+       }
+
+       SC_log(LOG_DEBUG, "_SCNetworkServiceSetServiceID(): %@ --> %@", service, newServiceID);
+
+       // replace serviceID with new one
+       CFRetain(newServiceID);
+       CFRelease(servicePrivate->serviceID);
+       servicePrivate->serviceID = newServiceID;
+
+    done:
+
+       if (oldPath != NULL) {
+               CFRelease(oldPath);
+       }
+       if (newPath != NULL) {
+               CFRelease(newPath);
+       }
+       if (allSets != NULL) {
+               CFRelease(allSets);
+       }
+
+       return ok;
+}
+
+#define kVPNProtocolPayloadInfo                        CFSTR("com.apple.payload")
+#define kSCEntNetLoginWindowEAPOL              CFSTR("EAPOL.LoginWindow")
+
+static void
+copyInterfaceConfiguration(SCNetworkServiceRef oldService, SCNetworkServiceRef newService)
+{
+       SCNetworkInterfaceRef   oldInterface;
+       SCNetworkInterfaceRef   newInterface;
+
+       oldInterface = SCNetworkServiceGetInterface(oldService);
+       newInterface = SCNetworkServiceGetInterface(newService);
+
+       while (oldInterface != NULL) {
+               CFDictionaryRef configuration;
+               CFStringRef             interfaceType;
+
+               if (newInterface == NULL) {
+                       // oops ... interface layering does not match
+                       return;
+               }
+
+               // copy interface configuration
+               configuration = SCNetworkInterfaceGetConfiguration(oldInterface);
+
+               if ((configuration != NULL) ||
+                   (SCError() == kSCStatusOK)) {
+                       if (!SCNetworkInterfaceSetConfiguration(newInterface, configuration)) {
+                               SC_log(LOG_INFO, "problem setting interface configuration");
+                       }
+
+               }
+
+               // special case: PPP/L2TP + IPSec
+               interfaceType = SCNetworkInterfaceGetInterfaceType(oldInterface);
+               if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
+                       SCNetworkInterfaceRef   childInterface;
+
+                       childInterface = SCNetworkInterfaceGetInterface(oldInterface);
+                       if (childInterface != NULL) {
+                               CFStringRef             childInterfaceType;
+
+                               childInterfaceType = SCNetworkInterfaceGetInterfaceType(childInterface);
+
+                               if (CFEqual(childInterfaceType, kSCNetworkInterfaceTypeL2TP)) {
+                                       configuration = SCNetworkInterfaceGetExtendedConfiguration(oldInterface, kSCEntNetIPSec);
+                                       if ((configuration != NULL) ||
+                                           (SCError() == kSCStatusOK)) {
+                                               if (!SCNetworkInterfaceSetExtendedConfiguration(newInterface, kSCEntNetIPSec, configuration)) {
+                                                       SC_log(LOG_INFO, "problem setting child interface configuration");
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // special case: 802.1x
+               configuration = SCNetworkInterfaceGetExtendedConfiguration(oldInterface, kSCEntNetEAPOL);
+               if ((configuration != NULL) ||
+                   (SCError() == kSCStatusOK)) {
+                       (void) SCNetworkInterfaceSetExtendedConfiguration(newInterface, kSCEntNetEAPOL, configuration);
+               }
+
+               // special case: Managed Client
+               configuration = SCNetworkInterfaceGetExtendedConfiguration(oldInterface, kVPNProtocolPayloadInfo);
+               if ((configuration != NULL) ||
+                   (SCError() == kSCStatusOK)) {
+                       (void) SCNetworkInterfaceSetExtendedConfiguration(newInterface, kVPNProtocolPayloadInfo, configuration);
+               }
+
+               // special case: Network Pref
+               configuration = SCNetworkInterfaceGetExtendedConfiguration(oldInterface, kSCValNetPPPAuthProtocolEAP);
+               if ((configuration != NULL) ||
+                   (SCError() == kSCStatusOK)) {
+                       (void) SCNetworkInterfaceSetExtendedConfiguration(newInterface, kSCValNetPPPAuthProtocolEAP, configuration);
+               }
+
+               // special case: Remote Pref
+               configuration = SCNetworkInterfaceGetExtendedConfiguration(oldInterface, kSCEntNetLoginWindowEAPOL);
+               if ((configuration != NULL) ||
+                   (SCError() == kSCStatusOK)) {
+                       (void) SCNetworkInterfaceSetExtendedConfiguration(newInterface, kSCEntNetLoginWindowEAPOL, configuration);
+               }
+
+               // special case: Network Extension
+               configuration = SCNetworkInterfaceGetExtendedConfiguration(oldInterface, kSCNetworkInterfaceTypeIPSec);
+               if ((configuration != NULL) ||
+                   (SCError() == kSCStatusOK)) {
+                       (void) SCNetworkInterfaceSetExtendedConfiguration(newInterface, kSCNetworkInterfaceTypeIPSec, configuration);
+               }
+
+               oldInterface = SCNetworkInterfaceGetInterface(oldInterface);
+               newInterface = SCNetworkInterfaceGetInterface(newInterface);
+       }
+
+       return;
+}
+
+__private_extern__
+void
+__SCNetworkServiceAddProtocolToService(SCNetworkServiceRef service, CFStringRef protocolType, CFDictionaryRef configuration, Boolean enabled)
+{
+       Boolean ok;
+       SCNetworkProtocolRef protocol;
+
+       protocol = SCNetworkServiceCopyProtocol(service, protocolType);
+
+       if ((protocol == NULL) &&
+           (SCError() == kSCStatusNoKey)) {
+               ok = SCNetworkServiceAddProtocolType(service, protocolType);
+               if (ok) {
+                       protocol = SCNetworkServiceCopyProtocol(service, protocolType);
+               }
+       }
+       if (protocol != NULL) {
+               SCNetworkProtocolSetConfiguration(protocol, configuration);
+               SCNetworkProtocolSetEnabled(protocol, enabled);
+               CFRelease(protocol);
+       }
+       return;
+}
+
+
+
+__private_extern__
+Boolean
+__SCNetworkServiceMigrateNew(SCPreferencesRef          prefs,
+                            SCNetworkServiceRef        service,
+                            CFDictionaryRef            bsdMapping,
+                            CFDictionaryRef            setMapping,
+                            CFDictionaryRef            serviceSetMapping)
+{
+       CFStringRef deviceName = NULL;
+       Boolean enabled;
+       SCNetworkInterfaceRef interface = NULL;
+       CFDictionaryRef interfaceEntity = NULL;
+       CFMutableDictionaryRef interfaceEntityMutable = NULL;
+       SCNetworkSetRef newSet = NULL;
+       SCPreferencesRef ni_prefs = NULL;
+       SCNetworkInterfaceRef ni_interface = NULL;
+       SCNetworkInterfaceRef oldInterface = NULL;
+       SCNetworkSetRef oldSet = NULL;
+       SCNetworkServiceRef newService = NULL;
+       CFStringRef serviceID = NULL;
+       SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef) service;
+       CFArrayRef setList = NULL;
+       Boolean success = FALSE;
+       CFStringRef targetDeviceName = NULL;
+       CFStringRef userDefinedName = NULL;
+       CFStringRef userDefinedNameInterface = NULL;
+       CFArrayRef protocols = NULL;
+       CFStringRef subType;
+
+       if ((isA_SCNetworkService(service) == NULL) ||
+       (isA_SCNetworkInterface(servicePrivate->interface) == NULL) ||
+       (servicePrivate->prefs == NULL)) {
+               goto done;
+       }
+       serviceID = servicePrivate->serviceID;
+
+       newService = SCNetworkServiceCopy(prefs, serviceID);
+       if (newService != NULL) {
+               // Cannot add service if it already exists
+               SC_log(LOG_INFO, "Service already exists");
+               goto done;
+       }
+
+       oldInterface = SCNetworkServiceGetInterface(service);
+       interfaceEntity = __SCNetworkInterfaceCopyInterfaceEntity(oldInterface);
+       if (interfaceEntity == NULL) {
+               SC_log(LOG_INFO, "No interface entity");
+               goto done;
+       }
+       interfaceEntityMutable = CFDictionaryCreateMutableCopy(NULL, 0, interfaceEntity);
+
+       if (isA_CFDictionary(bsdMapping) != NULL) {
+               deviceName = CFDictionaryGetValue(interfaceEntityMutable, kSCPropNetInterfaceDeviceName);
+               if (isA_CFString(deviceName) != NULL) {
+                       targetDeviceName = CFDictionaryGetValue(bsdMapping, deviceName);
+                       if (targetDeviceName != NULL) {
+                               // update mapping
+                               CFDictionarySetValue(interfaceEntityMutable, kSCPropNetInterfaceDeviceName, targetDeviceName);
+                               ni_prefs = __SCPreferencesCreateNIPrefsFromPrefs(prefs);
+                               ni_interface = __SCNetworkInterfaceCreateWithNIPreferencesUsingBSDName(NULL, ni_prefs, targetDeviceName);
+                               if (ni_interface != NULL) {
+                                       userDefinedNameInterface = __SCNetworkInterfaceGetUserDefinedName(ni_interface);
+                               }
+                       }
+               }
+               if (userDefinedNameInterface == NULL) {
+                       userDefinedNameInterface = CFDictionaryGetValue(interfaceEntityMutable, kSCPropUserDefinedName);
+               }
+       }
+       subType = CFDictionaryGetValue(interfaceEntityMutable, kSCPropNetInterfaceSubType);
+       interface = _SCNetworkInterfaceCreateWithEntity(NULL, interfaceEntityMutable, NULL);
+       if (userDefinedNameInterface != NULL) {
+               __SCNetworkInterfaceSetUserDefinedName(interface, userDefinedNameInterface);
+       }
+       // Supporting PPPoE subtype
+       if (subType != NULL &&
+           CFEqual(subType, kSCValNetInterfaceSubTypePPPoE)) {
+               SCNetworkInterfaceRef childInterface = SCNetworkInterfaceGetInterface(interface);
+               if (childInterface != NULL) {
+                       __SCNetworkInterfaceSetUserDefinedName(childInterface, userDefinedNameInterface);
+               }
+       }
+       newService = SCNetworkServiceCreate(prefs, interface);
+       if (newService == NULL) {
+               SC_log(LOG_INFO, "SCNetworkServiceCreate() failed");
+               goto done;
+       }
+
+       enabled = SCNetworkServiceGetEnabled(service);
+       if (!SCNetworkServiceSetEnabled(newService, enabled)) {
+               SCNetworkServiceRemove(newService);
+               SC_log(LOG_INFO, "SCNetworkServiceSetEnabled() failed");
+               goto done;
+       }
+
+       if (!SCNetworkServiceEstablishDefaultConfiguration(newService)) {
+               SCNetworkServiceRemove(newService);
+               SC_log(LOG_INFO, "SCNetworkServiceEstablishDefaultConfiguration() failed");
+               goto done;
+       }
+
+       // Set service ID
+       _SCNetworkServiceSetServiceID(newService, serviceID);
+
+       userDefinedName = SCNetworkServiceGetName(service);
+       if ((userDefinedName != NULL) &&
+           !SCNetworkServiceSetName(newService, userDefinedName)) {
+               SC_log(LOG_INFO, "SCNetworkServiceSetName(, %@) failed", userDefinedName);
+       }
+
+       // Determine which sets to add service
+       if (setMapping != NULL &&
+           serviceSetMapping != NULL) {
+               setList = CFDictionaryGetValue(serviceSetMapping, service);
+               if (setList != NULL) {
+                       for (CFIndex idx = 0; idx < CFArrayGetCount(setList); idx++) {
+                               oldSet = CFArrayGetValueAtIndex(setList, idx);
+                               newSet = CFDictionaryGetValue(setMapping, oldSet);
+                               if (newSet == NULL) {
+                                       continue;
+                               }
+
+                               if (!SCNetworkSetAddService(newSet, newService)) {
+                                       SC_log(LOG_INFO, "SCNetworkSetAddService() failed");
+                               }
+                       }
+               }
+       }
+
+       protocols = SCNetworkServiceCopyProtocols(service);
+       if (protocols != NULL) {
+
+               for (CFIndex idx = 0; idx < CFArrayGetCount(protocols); idx++) {
+                       SCNetworkProtocolRef protocol = CFArrayGetValueAtIndex(protocols, idx);
+                       CFDictionaryRef configuration = SCNetworkProtocolGetConfiguration(protocol);
+                       CFStringRef protocolType = SCNetworkProtocolGetProtocolType(protocol);
+                       enabled = SCNetworkProtocolGetEnabled(protocol);
+                       __SCNetworkServiceAddProtocolToService(newService, protocolType, configuration, enabled);
+               }
+               CFRelease(protocols);
+       }
+
+       copyInterfaceConfiguration(service, newService);
+
+       success = TRUE;
+done:
+       if (interface != NULL) {
+               CFRelease(interface);
+       }
+       if (interfaceEntity != NULL) {
+               CFRelease(interfaceEntity);
+       }
+       if (interfaceEntityMutable != NULL) {
+               CFRelease(interfaceEntityMutable);
+       }
+       if (newService != NULL) {
+               CFRelease(newService);
+       }
+       if (ni_prefs != NULL) {
+               CFRelease(ni_prefs);
+       }
+       if (ni_interface != NULL) {
+               CFRelease(ni_interface);
+       }
+       return success;
+}
+
+
+__private_extern__
+Boolean
+__SCNetworkServiceCreate(SCPreferencesRef      prefs,
+                        SCNetworkInterfaceRef  interface,
+                        CFStringRef            userDefinedName)
+{
+       SCNetworkSetRef currentSet = NULL;
+       Boolean ok = FALSE;
+       SCNetworkServiceRef service = NULL;
+
+       if (interface == NULL) {
+               goto done;
+       }
+
+       if (userDefinedName == NULL) {
+               userDefinedName = __SCNetworkInterfaceGetUserDefinedName(interface);
+               if (userDefinedName == NULL) {
+                       SC_log(LOG_INFO, "No userDefinedName");
+                       goto done;
+               }
+       }
+       service = SCNetworkServiceCreate(prefs, interface);
+       if (service == NULL) {
+               SC_log(LOG_INFO, "SCNetworkServiceCreate() failed: %s", SCErrorString(SCError()));
+       } else {
+               ok = SCNetworkServiceSetName(service, userDefinedName);
+               if (!ok) {
+                       SC_log(LOG_INFO, "SCNetworkServiceSetName() failed: %s", SCErrorString(SCError()));
+                       SCNetworkServiceRemove(service);
+                       goto done;
+               }
+
+               ok = SCNetworkServiceEstablishDefaultConfiguration(service);
+               if (!ok) {
+                       SC_log(LOG_INFO, "SCNetworkServiceEstablishDefaultConfiguration() failed: %s", SCErrorString(SCError()));
+                       SCNetworkServiceRemove(service);
+                       goto done;
+               }
+       }
+       currentSet = SCNetworkSetCopyCurrent(prefs);
+       if (currentSet == NULL) {
+               SC_log(LOG_INFO, "No current set");
+               if (service != NULL) {
+                       SCNetworkServiceRemove(service);
+               }
+               goto done;
+       }
+       if (service != NULL) {
+               ok = SCNetworkSetAddService(currentSet, service);
+               if (!ok) {
+                       SC_log(LOG_INFO, "Could not add service to the current set");
+                       SCNetworkServiceRemove(service);
+                       goto done;
+               }
+       }
+
+    done:
+       if (service != NULL) {
+               CFRelease(service);
+       }
+       if (currentSet != NULL) {
+               CFRelease(currentSet);
+       }
+       return ok;
+}
+
+__private_extern__ Boolean
+__SCNetworkServiceIsPPTP(SCNetworkServiceRef   service)
+{
+       CFStringRef intfSubtype;
+       SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service;
+
+       if (servicePrivate == NULL || servicePrivate->interface == NULL) {
+               return FALSE;
+       }
+
+       intfSubtype = __SCNetworkInterfaceGetEntitySubType(servicePrivate->interface);
+       if (intfSubtype == NULL) {
+               return FALSE;
+       }
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+       if (CFEqual(intfSubtype, kSCValNetInterfaceSubTypePPTP)) {
+               return TRUE;
+       }
+#pragma GCC diagnostic pop
+
+       return FALSE;
+}