]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/VLANConfiguration.c
configd-84.6.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / VLANConfiguration.c
diff --git a/SystemConfiguration.fproj/VLANConfiguration.c b/SystemConfiguration.fproj/VLANConfiguration.c
new file mode 100644 (file)
index 0000000..7072892
--- /dev/null
@@ -0,0 +1,1573 @@
+/*
+ * Copyright (c) 2003 Apple Computer, 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,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * 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@
+ */
+
+/*
+ * Modification History
+ *
+ * November 14, 2003           Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFRuntime.h>
+
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCValidation.h>
+#include <SystemConfiguration/SCPrivate.h>
+
+#include <ifaddrs.h>
+#include <pthread.h>
+#include <sys/types.h>
+#define        KERNEL_PRIVATE
+#include <sys/ioctl.h>
+#undef KERNEL_PRIVATE
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <net/ethernet.h>
+#define        KERNEL_PRIVATE  1
+#include <net/if.h>
+#include <net/if_var.h>
+#undef KERNEL_PRIVATE
+#include <net/if_vlan_var.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <SystemConfiguration/VLANConfiguration.h>
+#include <SystemConfiguration/VLANConfigurationPrivate.h>
+
+/* ---------- VLAN support ---------- */
+
+static int
+inet_dgram_socket()
+{
+       int     s;
+
+       s = socket(AF_INET, SOCK_DGRAM, 0);
+       if (s == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+       }
+
+       return s;
+}
+
+
+static Boolean
+_VLAN_create(int s, CFStringRef interface)
+{
+#ifdef SIOCIFCREATE
+       struct ifreq    ifr;
+
+       bzero(&ifr, sizeof(ifr));
+       (void) _SC_cfstring_to_cstring(interface,
+                                      ifr.ifr_name,
+                                      sizeof(ifr.ifr_name),
+                                      kCFStringEncodingASCII);
+
+       if (ioctl(s, SIOCIFCREATE, &ifr) == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCIFCREATE) failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       return TRUE;
+#else  /* SIOCIFCREATE */
+       return FALSE;
+#endif /* SIOCIFCREATE */
+}
+
+
+static Boolean
+_VLAN_destroy(int s, CFStringRef interface)
+{
+#ifdef SIOCIFDESTROY
+       struct ifreq    ifr;
+
+       bzero(&ifr, sizeof(ifr));
+       (void) _SC_cfstring_to_cstring(interface,
+                                      ifr.ifr_name,
+                                      sizeof(ifr.ifr_name),
+                                      kCFStringEncodingASCII);
+
+       if (ioctl(s, SIOCIFDESTROY, &ifr) == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCIFDESTROY) failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       return TRUE;
+#else  /* SIOCIFDESTROY */
+       return FALSE;
+#endif /* SIOCIFDESTROY */
+}
+
+
+static Boolean
+_VLANDevice_set(int s, CFStringRef interface, CFStringRef device, CFNumberRef tag)
+{
+#ifdef SIOCSETVLAN
+       struct ifreq    ifr;
+       int             tag_val;
+       struct vlanreq  vreq;
+
+       bzero(&ifr, sizeof(ifr));
+       bzero(&vreq, sizeof(vreq));
+
+       // interface
+       (void) _SC_cfstring_to_cstring(interface,
+                                      ifr.ifr_name,
+                                      sizeof(ifr.ifr_name),
+                                      kCFStringEncodingASCII);
+       ifr.ifr_data = (caddr_t)&vreq;
+
+       // parent device
+       (void) _SC_cfstring_to_cstring(device,
+                                      vreq.vlr_parent,
+                                      sizeof(vreq.vlr_parent),
+                                      kCFStringEncodingASCII);
+
+       // tag
+       CFNumberGetValue(tag, kCFNumberIntType, &tag_val);
+       vreq.vlr_tag = tag_val;
+
+       // update parent device and tag
+       if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCSETVLAN) failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       return TRUE;
+#else  /* SIOCSETVLAN */
+       return FALSE;
+#endif /* SIOCSETVLAN */
+}
+
+
+static Boolean
+_VLANDevice_unset(int s, CFStringRef interface)
+{
+#ifdef SIOCSETVLAN
+       struct ifreq    ifr;
+       struct vlanreq  vreq;
+
+       bzero(&ifr, sizeof(ifr));
+       bzero(&vreq, sizeof(vreq));
+
+       // interface
+       (void) _SC_cfstring_to_cstring(interface,
+                                      ifr.ifr_name,
+                                      sizeof(ifr.ifr_name),
+                                      kCFStringEncodingASCII);
+       ifr.ifr_data = (caddr_t)&vreq;
+
+       // clear parent device
+       bzero(&vreq.vlr_parent, sizeof(vreq.vlr_parent));
+
+       // clear tag
+       vreq.vlr_tag = 0;
+
+       // update parent device and tag
+       if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCSETVLAN) failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+               return FALSE;
+       }
+
+       return TRUE;
+#else  /* SIOCSETVLAN */
+       return FALSE;
+#endif /* SIOCSETVLAN */
+}
+
+
+/* ---------- VLAN "device" ---------- */
+
+Boolean
+IsVLANSupported(CFStringRef device)
+{
+       char *                  buf     = NULL;
+       size_t                  buf_len = 0;
+       struct if_msghdr *      ifm;
+       char *                  if_name = NULL;
+       unsigned int            if_index;
+       Boolean                 isVlan  = FALSE;
+       int                     mib[6];
+
+       /* get the interface index */
+
+       if_name = _SC_cfstring_to_cstring(device, NULL, NULL, kCFStringEncodingASCII);
+       if (if_name == NULL) {
+               return FALSE;   // if conversion error
+       }
+       if_index = if_nametoindex(if_name);
+       if (if_index == 0) {
+               goto done;      // if unknown interface
+       }
+
+       /* get information for the specified device */
+
+       mib[0] = CTL_NET;
+       mib[1] = PF_ROUTE;
+       mib[2] = 0;
+       mib[3] = AF_LINK;
+       mib[4] = NET_RT_IFLIST;
+       mib[5] = if_index;      /* ask for exactly one interface */
+
+       if (sysctl(mib, 6, NULL, &buf_len, NULL, 0) < 0) {
+               SCLog(TRUE, LOG_ERR, CFSTR("sysctl() size failed: %s"), strerror(errno));
+               goto done;
+       }
+       buf = CFAllocatorAllocate(NULL, buf_len, 0);
+       if (sysctl(mib, 6, buf, &buf_len, NULL, 0) < 0) {
+               SCLog(TRUE, LOG_ERR, CFSTR("sysctl() failed: %s"), strerror(errno));
+               goto done;
+       }
+
+       /* check the link type and hwassist flags */
+
+       ifm = (struct if_msghdr *)buf;
+       switch (ifm->ifm_type) {
+               case RTM_IFINFO : {
+#if    defined(IF_HWASSIST_VLAN_TAGGING) && defined(IF_HWASSIST_VLAN_MTU)
+                       struct if_data  *if_data = &ifm->ifm_data;
+
+                       if (if_data->ifi_hwassist & (IF_HWASSIST_VLAN_TAGGING | IF_HWASSIST_VLAN_MTU)) {
+                               isVlan = TRUE;
+                       }
+#endif
+                       break;
+               }
+       }
+
+    done :
+
+       if (if_name != NULL)    CFAllocatorDeallocate(NULL, if_name);
+       if (buf != NULL)        CFAllocatorDeallocate(NULL, buf);
+
+       return isVlan;
+}
+
+/* ---------- VLANInterface ---------- */
+
+typedef struct {
+
+       /* base CFType information */
+       CFRuntimeBase                   cfBase;
+
+       /* vlan interface configuration */
+       CFStringRef                     ifname;         // e.g. vlan0, vlan1, ...
+       CFStringRef                     device;         // e.g. en0, en1, ...
+       CFNumberRef                     tag;            // e.g. 1 <= tag <= 4094
+       CFDictionaryRef                 options;        // e.g. UserDefinedName
+
+} VLANInterfacePrivate, * VLANInterfacePrivateRef;
+
+
+static CFStringRef     __VLANInterfaceCopyDescription  (CFTypeRef cf);
+static void            __VLANInterfaceDeallocate       (CFTypeRef cf);
+static Boolean         __VLANInterfaceEqual            (CFTypeRef cf1, CFTypeRef cf2);
+
+
+static const CFRuntimeClass __VLANInterfaceClass = {
+       0,                                      // version
+       "VLANInterface",                        // className
+       NULL,                                   // init
+       NULL,                                   // copy
+       __VLANInterfaceDeallocate,              // dealloc
+       __VLANInterfaceEqual,                   // equal
+       NULL,                                   // hash
+       NULL,                                   // copyFormattingDesc
+       __VLANInterfaceCopyDescription          // copyDebugDesc
+};
+
+
+static CFTypeID                __kVLANInterfaceTypeID  = _kCFRuntimeNotATypeID;
+
+
+static pthread_once_t  vlanInterface_init      = PTHREAD_ONCE_INIT;
+
+
+static CFStringRef
+__VLANInterfaceCopyDescription(CFTypeRef cf)
+{
+       CFAllocatorRef          allocator       = CFGetAllocator(cf);
+       CFMutableStringRef      result;
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)cf;
+
+       result = CFStringCreateMutable(allocator, 0);
+       CFStringAppendFormat(result, NULL, CFSTR("<VLANInterface %p [%p]> {"), cf, allocator);
+       CFStringAppendFormat(result, NULL, CFSTR(" if = %@"), vlanPrivate->ifname);
+       CFStringAppendFormat(result, NULL, CFSTR(", device = %@"), vlanPrivate->device);
+       CFStringAppendFormat(result, NULL, CFSTR(", tag = %@"), vlanPrivate->tag);
+       if (vlanPrivate->options != NULL) {
+               CFStringAppendFormat(result, NULL, CFSTR(", options = %@"), vlanPrivate->options);
+       }
+       CFStringAppendFormat(result, NULL, CFSTR(" }"));
+
+       return result;
+}
+
+
+static void
+__VLANInterfaceDeallocate(CFTypeRef cf)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)cf;
+
+       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__VLANInterfaceDeallocate:"));
+
+       /* release resources */
+
+       CFRelease(vlanPrivate->ifname);
+       CFRelease(vlanPrivate->device);
+       CFRelease(vlanPrivate->tag);
+       if (vlanPrivate->options)       CFRelease(vlanPrivate->options);
+
+       return;
+}
+
+
+static Boolean
+__VLANInterfaceEquiv(CFTypeRef cf1, CFTypeRef cf2)
+{
+       VLANInterfacePrivateRef vlan1   = (VLANInterfacePrivateRef)cf1;
+       VLANInterfacePrivateRef vlan2   = (VLANInterfacePrivateRef)cf2;
+
+       if (vlan1 == vlan2)
+               return TRUE;
+
+       if (!CFEqual(vlan1->ifname, vlan2->ifname))
+               return FALSE;   // if not the same interface
+
+       if (!CFEqual(vlan1->device, vlan2->device))
+               return FALSE;   // if not the same device
+
+       if (!CFEqual(vlan1->tag, vlan2->tag))
+               return FALSE;   // if not the same tag
+
+       return TRUE;
+}
+
+
+static Boolean
+__VLANInterfaceEqual(CFTypeRef cf1, CFTypeRef cf2)
+{
+       VLANInterfacePrivateRef vlan1   = (VLANInterfacePrivateRef)cf1;
+       VLANInterfacePrivateRef vlan2   = (VLANInterfacePrivateRef)cf2;
+
+       if (!__VLANInterfaceEquiv(vlan1, vlan2))
+               return FALSE;   // if not the same VLAN interface/device/tag
+
+       if (vlan1->options != vlan2->options) {
+               // if the options may differ
+               if ((vlan1->options != NULL) && (vlan2->options != NULL)) {
+                       // if both VLANs have options
+                       if (!CFEqual(vlan1->options, vlan2->options)) {
+                               // if the options are not equal
+                               return FALSE;
+                       }
+               } else {
+                       // if only one VLAN has options
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+
+static void
+__VLANInterfaceInitialize(void)
+{
+       __kVLANInterfaceTypeID = _CFRuntimeRegisterClass(&__VLANInterfaceClass);
+       return;
+}
+
+
+static __inline__ CFTypeRef
+isA_VLANInterface(CFTypeRef obj)
+{
+       return (isA_CFType(obj, VLANInterfaceGetTypeID()));
+}
+
+
+CFTypeID
+VLANInterfaceGetTypeID(void)
+{
+       pthread_once(&vlanInterface_init, __VLANInterfaceInitialize);   /* initialize runtime */
+       return __kVLANInterfaceTypeID;
+}
+
+
+static VLANInterfaceRef
+__VLANInterfaceCreatePrivate(CFAllocatorRef    allocator,
+                            CFStringRef        ifname,
+                            CFStringRef        device,
+                            CFNumberRef        tag,
+                            CFDictionaryRef    options)
+{
+       VLANInterfacePrivateRef         vlanPrivate;
+       uint32_t                        size;
+
+       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__VLANInterfaceCreatePrivate:"));
+
+       /* initialize runtime */
+       pthread_once(&vlanInterface_init, __VLANInterfaceInitialize);
+
+       /* allocate vlan */
+       size        = sizeof(VLANInterfacePrivate) - sizeof(CFRuntimeBase);
+       vlanPrivate = (VLANInterfacePrivateRef)_CFRuntimeCreateInstance(allocator,
+                                                                       __kVLANInterfaceTypeID,
+                                                                       size,
+                                                                       NULL);
+       if (!vlanPrivate) {
+               return NULL;
+       }
+
+       /* establish the vlan */
+
+       vlanPrivate->ifname  = CFStringCreateCopy(allocator, ifname);
+       vlanPrivate->device  = CFStringCreateCopy(allocator, device);
+       vlanPrivate->tag     = CFRetain(tag);
+       if (options != NULL) {
+               vlanPrivate->options = CFDictionaryCreateCopy(allocator, options);
+       } else {
+               vlanPrivate->options = NULL;
+       }
+
+       return (VLANInterfaceRef)vlanPrivate;
+}
+
+
+CFStringRef
+VLANInterfaceGetInterface(VLANInterfaceRef vlan)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+       CFStringRef             vlan_if         = NULL;
+
+       if (isA_VLANInterface(vlan)) {
+               vlan_if = vlanPrivate->ifname;
+       }
+
+       return vlan_if;
+}
+
+
+CFStringRef
+VLANInterfaceGetDevice(VLANInterfaceRef        vlan)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+       CFStringRef             vlan_device     = NULL;
+
+       if (isA_VLANInterface(vlan)) {
+               vlan_device = vlanPrivate->device;
+       }
+
+       return vlan_device;
+}
+
+
+static void
+VLANInterfaceSetDevice(VLANInterfaceRef        vlan, CFStringRef newDevice)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+
+       if (isA_VLANInterface(vlan)) {
+               CFAllocatorRef  allocator       = CFGetAllocator(vlan);
+
+               CFRelease(vlanPrivate->device);
+               vlanPrivate->device = CFStringCreateCopy(allocator, newDevice);
+       }
+
+       return;
+}
+
+
+CFNumberRef
+VLANInterfaceGetTag(VLANInterfaceRef vlan)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+       CFNumberRef             vlan_tag        = NULL;
+
+       if (isA_VLANInterface(vlan)) {
+               vlan_tag = vlanPrivate->tag;
+       }
+
+       return vlan_tag;
+}
+
+
+static void
+VLANInterfaceSetTag(VLANInterfaceRef vlan, CFNumberRef newTag)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+
+       if (isA_VLANInterface(vlan)) {
+               CFRelease(vlanPrivate->tag);
+               vlanPrivate->tag = CFRetain(newTag);
+       }
+
+       return;
+}
+
+
+CFDictionaryRef
+VLANInterfaceGetOptions(VLANInterfaceRef vlan)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+       CFDictionaryRef         vlan_options    = NULL;
+
+       if (isA_VLANInterface(vlan)) {
+               vlan_options = vlanPrivate->options;
+       }
+
+       return vlan_options;
+}
+
+
+static void
+VLANInterfaceSetOptions(VLANInterfaceRef vlan, CFDictionaryRef newOptions)
+{
+       VLANInterfacePrivateRef vlanPrivate     = (VLANInterfacePrivateRef)vlan;
+
+       if (isA_VLANInterface(vlan)) {
+               CFAllocatorRef  allocator       = CFGetAllocator(vlan);
+
+               if (vlanPrivate->options)       CFRelease(vlanPrivate->options);
+               if (newOptions != NULL) {
+                       vlanPrivate->options = CFDictionaryCreateCopy(allocator, newOptions);
+               } else {
+                       vlanPrivate->options = NULL;
+               }
+       }
+
+       return;
+}
+
+
+/* ---------- VLANPreferences ---------- */
+
+#define        VLAN_PREFERENCES_VLANS          CFSTR("VLANs")
+
+#define        __kVLANInterface_interface      CFSTR("interface")      // e.g. vlan0, vlan1, ...
+#define        __kVLANInterface_device         CFSTR("device")         // e.g. en0, en1, ...
+#define __kVLANInterface_tag           CFSTR("tag")            // e.g. 1 <= tag <= 4094
+#define __kVLANInterface_options       CFSTR("options")        // e.g. UserDefinedName
+
+typedef struct {
+
+       /* base CFType information */
+       CFRuntimeBase                   cfBase;
+
+       /* lock */
+       pthread_mutex_t                 lock;
+
+       /* underlying preferences */
+       SCPreferencesRef                prefs;
+
+       /* base VLANs (before any commits) */
+       CFArrayRef                      vlBase;
+
+} VLANPreferencesPrivate, * VLANPreferencesPrivateRef;
+
+
+static CFStringRef     __VLANPreferencesCopyDescription        (CFTypeRef cf);
+static void            __VLANPreferencesDeallocate             (CFTypeRef cf);
+
+
+static const CFRuntimeClass __VLANPreferencesClass = {
+       0,                                      // version
+       "VLANPreferences",                      // className
+       NULL,                                   // init
+       NULL,                                   // copy
+       __VLANPreferencesDeallocate,            // dealloc
+       NULL,                                   // equal
+       NULL,                                   // hash
+       NULL,                                   // copyFormattingDesc
+       __VLANPreferencesCopyDescription        // copyDebugDesc
+};
+
+
+static CFTypeID                __kVLANPreferencesTypeID        = _kCFRuntimeNotATypeID;
+
+
+static pthread_once_t  vlanPreferences_init            = PTHREAD_ONCE_INIT;
+
+
+static CFStringRef
+__VLANPreferencesCopyDescription(CFTypeRef cf)
+{
+       CFAllocatorRef                  allocator       = CFGetAllocator(cf);
+       CFIndex                         i;
+       CFArrayRef                      keys;
+       CFIndex                         n;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)cf;
+       CFMutableStringRef              result;
+
+       result = CFStringCreateMutable(allocator, 0);
+       CFStringAppendFormat(result, NULL, CFSTR("<VLANPreferences %p [%p]> {"), cf, allocator);
+
+       keys = SCPreferencesCopyKeyList(prefsPrivate->prefs);
+       n = CFArrayGetCount(keys);
+       for (i = 0; i < n; i++) {
+               CFStringRef             key;
+               CFPropertyListRef       val;
+
+               key = CFArrayGetValueAtIndex(keys, i);
+               val = SCPreferencesGetValue(prefsPrivate->prefs, key);
+
+               CFStringAppendFormat(result, NULL, CFSTR("%@ : %@"), key, val);
+       }
+       CFRelease(keys);
+
+       CFStringAppendFormat(result, NULL, CFSTR(" }"));
+
+       return result;
+}
+
+
+#define N_QUICK        8
+
+
+static void
+__VLANPreferencesDeallocate(CFTypeRef cf)
+{
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)cf;
+
+       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__VLANPreferencesDeallocate:"));
+
+       /* release resources */
+
+       pthread_mutex_destroy(&prefsPrivate->lock);
+
+       if (prefsPrivate->prefs)        CFRelease(prefsPrivate->prefs);
+       if (prefsPrivate->vlBase)       CFRelease(prefsPrivate->vlBase);
+
+       return;
+}
+
+
+static void
+__VLANPreferencesInitialize(void)
+{
+       __kVLANPreferencesTypeID = _CFRuntimeRegisterClass(&__VLANPreferencesClass);
+       return;
+}
+
+
+static __inline__ CFTypeRef
+isA_VLANPreferences(CFTypeRef obj)
+{
+       return (isA_CFType(obj, VLANPreferencesGetTypeID()));
+}
+
+
+CFArrayRef
+_VLANPreferencesCopyActiveInterfaces()
+{
+       CFArrayCallBacks        callbacks;
+       struct ifaddrs          *ifap;
+       struct ifaddrs          *ifp;
+       int                     s;
+       CFMutableArrayRef       vlans   = NULL;
+
+       if (getifaddrs(&ifap) == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("getifaddrs() failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+               return NULL;
+       }
+
+       s = inet_dgram_socket();
+       if (s == -1) {
+               SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
+               _SCErrorSet(kSCStatusFailed);
+               goto done;
+       }
+
+       callbacks = kCFTypeArrayCallBacks;
+       callbacks.equal = __VLANInterfaceEquiv;
+       vlans = CFArrayCreateMutable(NULL, 0, &callbacks);
+
+       for (ifp = ifap; ifp != NULL; ifp = ifp->ifa_next) {
+               switch (ifp->ifa_addr->sa_family) {
+                       case AF_LINK : {
+                               CFStringRef             device;
+                               struct if_data          *if_data;
+                               struct ifreq            ifr;
+                               CFNumberRef             tag;
+                               VLANInterfaceRef        vlan;
+                               CFStringRef             vlan_if;
+                               char                    vlr_parent[IFNAMSIZ+1];
+                               int                     vlr_tag;
+                               struct vlanreq          vreq;
+
+                               if_data = (struct if_data *)ifp->ifa_data;
+                               if (if_data == NULL) {
+                                       break;  // if no interface data
+                               }
+
+                               if (if_data->ifi_type != IFT_L2VLAN) {
+                                       break;  // if not VLAN
+                               }
+
+                               bzero(&ifr, sizeof(ifr));
+                               bzero(&vreq, sizeof(vreq));
+                               strncpy(ifr.ifr_name, ifp->ifa_name, sizeof(ifr.ifr_name));
+                               ifr.ifr_data = (caddr_t)&vreq;
+
+                               if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1) {
+                                       SCLog(TRUE, LOG_ERR, CFSTR("ioctl() failed: %s"), strerror(errno));
+                                       _SCErrorSet(kSCStatusFailed);
+                                       CFRelease(vlans);
+                                       goto done;
+                               }
+                               vlr_tag = vreq.vlr_tag;
+                               strlcpy(vlr_parent, vreq.vlr_parent, sizeof(vlr_parent));
+
+                               vlan_if = CFStringCreateWithCString(NULL, ifp->ifa_name, kCFStringEncodingASCII);
+                               device  = CFStringCreateWithCString(NULL, vlr_parent, kCFStringEncodingASCII);
+                               tag     = CFNumberCreate(NULL, kCFNumberIntType, &vlr_tag);
+                               vlan    = __VLANInterfaceCreatePrivate(NULL, vlan_if, device, tag, NULL);
+                               CFArrayAppendValue(vlans, vlan);
+                               CFRelease(vlan_if);
+                               CFRelease(device);
+                               CFRelease(tag);
+                               CFRelease(vlan);
+                               break;
+                       }
+
+                       default :
+                               break;
+               }
+       }
+
+    done :
+
+       (void) close(s);
+       freeifaddrs(ifap);
+       return vlans;
+}
+
+
+static CFIndex
+findVLAN(CFArrayRef vlans, CFStringRef device, CFNumberRef tag)
+{
+       CFIndex found   = kCFNotFound;
+       CFIndex i;
+       CFIndex n;
+
+       n = isA_CFArray(vlans) ? CFArrayGetCount(vlans) : 0;
+       for (i = 0; i < n; i++) {
+               CFDictionaryRef vlan_dict;
+               CFStringRef     vlan_device;
+               CFStringRef     vlan_if;
+               CFNumberRef     vlan_tag;
+
+               vlan_dict = CFArrayGetValueAtIndex(vlans, i);
+               if (!isA_CFDictionary(vlan_dict)) {
+                       continue;       // if the prefs are confused
+               }
+
+               vlan_if = CFDictionaryGetValue(vlan_dict, __kVLANInterface_interface);
+               if (!isA_CFString(vlan_if)) {
+                       continue;       // if the prefs are confused
+               }
+
+               vlan_device = CFDictionaryGetValue(vlan_dict, __kVLANInterface_device);
+               if (isA_CFString(vlan_device)) {
+                       if (!CFEqual(device, vlan_device)) {
+                               continue;       // if not a match
+                       }
+               }
+
+               vlan_tag = CFDictionaryGetValue(vlan_dict, __kVLANInterface_tag);
+               if (isA_CFNumber(vlan_tag)) {
+                       if (!CFEqual(tag, vlan_tag)) {
+                               continue;       // if not a match
+                       }
+               }
+
+               // if we have found a match
+               found = i;
+               break;
+       }
+
+       return found;
+}
+
+
+static void
+setConfigurationChanged(VLANPreferencesRef prefs)
+{
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+
+       /*
+        * to facilitate device configuration we will take
+        * a snapshot of the VLAN preferences before any
+        * changes are made.  Then, when the changes are
+        * applied we can compare what we had to what we
+        * want and configured the system accordingly.
+        */
+       if (prefsPrivate->vlBase == NULL) {
+               prefsPrivate->vlBase = VLANPreferencesCopyInterfaces(prefs);
+       }
+
+       return;
+}
+
+
+CFTypeID
+VLANPreferencesGetTypeID(void)
+{
+       pthread_once(&vlanPreferences_init, __VLANPreferencesInitialize);       /* initialize runtime */
+       return __kVLANPreferencesTypeID;
+}
+
+
+VLANPreferencesRef
+VLANPreferencesCreate(CFAllocatorRef allocator)
+{
+       CFBundleRef                     bundle;
+       CFStringRef                     bundleID        = NULL;
+       CFStringRef                     name            = CFSTR("VLANConfiguration");
+       VLANPreferencesPrivateRef       prefsPrivate;
+       uint32_t                        size;
+
+       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__VLANPreferencesCreate:"));
+
+       /* initialize runtime */
+       pthread_once(&vlanPreferences_init, __VLANPreferencesInitialize);
+
+       /* allocate preferences */
+       size         = sizeof(VLANPreferencesPrivate) - sizeof(CFRuntimeBase);
+       prefsPrivate = (VLANPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
+                                                                          __kVLANPreferencesTypeID,
+                                                                          size,
+                                                                          NULL);
+       if (!prefsPrivate) {
+               return NULL;
+       }
+
+       /* establish the prefs */
+
+       pthread_mutex_init(&prefsPrivate->lock, NULL);
+
+       bundle = CFBundleGetMainBundle();
+       if (bundle) {
+               bundleID = CFBundleGetIdentifier(bundle);
+               if (bundleID) {
+                       CFRetain(bundleID);
+               } else {
+                       CFURLRef        url;
+
+                       url = CFBundleCopyExecutableURL(bundle);
+                       if (url) {
+                               bundleID = CFURLCopyPath(url);
+                               CFRelease(url);
+                       }
+               }
+       }
+
+       if (bundleID) {
+               CFStringRef     fullName;
+
+               if (CFEqual(bundleID, CFSTR("/"))) {
+                       CFRelease(bundleID);
+                       bundleID = CFStringCreateWithFormat(allocator, NULL, CFSTR("(%d)"), getpid());
+               }
+
+               fullName = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@:%@"), bundleID, name);
+               name = fullName;
+               CFRelease(bundleID);
+       } else {
+               CFRetain(name);
+       }
+
+       prefsPrivate->prefs = SCPreferencesCreate(allocator, name, VLAN_PREFERENCES_ID);
+       CFRelease(name);
+
+       prefsPrivate->vlBase = NULL;
+
+       return (VLANPreferencesRef)prefsPrivate;
+}
+
+
+CFArrayRef
+VLANPreferencesCopyInterfaces(VLANPreferencesRef prefs)
+{
+       CFAllocatorRef                  allocator;
+       CFArrayCallBacks                callbacks;
+       CFIndex                         i;
+       CFIndex                         n;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+       CFMutableArrayRef               result;
+       CFArrayRef                      vlans;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       allocator = CFGetAllocator(prefs);
+       callbacks = kCFTypeArrayCallBacks;
+       callbacks.equal = __VLANInterfaceEquiv;
+       result = CFArrayCreateMutable(allocator, 0, &callbacks);
+
+       vlans = SCPreferencesGetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS);
+       n = isA_CFArray(vlans) ? CFArrayGetCount(vlans) : 0;
+       for (i = 0; i < n; i++) {
+               CFDictionaryRef         vlan_dict;
+               CFStringRef             device;
+               CFDictionaryRef         options;
+               CFNumberRef             tag;
+               VLANInterfaceRef        vlan;
+               CFStringRef             vlan_if;
+
+               vlan_dict = CFArrayGetValueAtIndex(vlans, i);
+               if (!isA_CFDictionary(vlan_dict)) {
+                       continue;       // if the prefs are confused
+               }
+
+               vlan_if = CFDictionaryGetValue(vlan_dict, __kVLANInterface_interface);
+               if (!isA_CFString(vlan_if)) {
+                       continue;       // if the prefs are confused
+               }
+
+
+               device = CFDictionaryGetValue(vlan_dict, __kVLANInterface_device);
+               if (!isA_CFString(device)) {
+                       continue;       // if the prefs are confused
+               }
+
+               tag = CFDictionaryGetValue(vlan_dict, __kVLANInterface_tag);
+               if (!isA_CFNumber(tag)) {
+                       continue;       // if the prefs are confused
+               }
+
+               options = CFDictionaryGetValue(vlan_dict, __kVLANInterface_options);
+               if ((options != NULL) && !isA_CFDictionary(options)) {
+                       continue;       // if the prefs are confused
+               }
+
+               vlan = __VLANInterfaceCreatePrivate(allocator, vlan_if, device, tag, options);
+               CFArrayAppendValue(result, vlan);
+               CFRelease(vlan);
+       }
+
+       return result;
+}
+
+
+VLANInterfaceRef
+VLANPreferencesAddInterface(VLANPreferencesRef prefs,
+                           CFStringRef         device,
+                           CFNumberRef         tag,
+                           CFDictionaryRef     options)
+{
+       CFArrayRef                      active_vlans;
+       CFAllocatorRef                  allocator;
+       CFArrayRef                      config_vlans;
+       CFIndex                         dup_if;
+       CFIndex                         i;
+       CFIndex                         nActive;
+       CFIndex                         nConfig;
+       VLANInterfaceRef                newVlan         = NULL;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (!isA_CFString(device)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if (isA_CFNumber(tag)) {
+               int     tag_val;
+
+               CFNumberGetValue(tag, kCFNumberIntType, &tag_val);
+               if ((tag_val < 1) || (tag_val > 4094)) {
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       return NULL;
+               }
+       } else {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       if ((options != NULL) && !isA_CFDictionary(options)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return NULL;
+       }
+
+       pthread_mutex_lock(&prefsPrivate->lock);
+
+       /* get "configured" VLANs (and check to ensure we are not creating a duplicate) */
+       config_vlans = SCPreferencesGetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS);
+       nConfig      = isA_CFArray(config_vlans) ? CFArrayGetCount(config_vlans) : 0;
+
+       dup_if = findVLAN(config_vlans, device, tag);
+       if (dup_if != kCFNotFound) {
+               // sorry, you can't add a vlan using the same device/tag */
+               _SCErrorSet(kSCStatusKeyExists);
+               goto done;
+       }
+
+       /* get "active" VLANs */
+       active_vlans = _VLANPreferencesCopyActiveInterfaces();
+       nActive      = isA_CFArray(active_vlans) ? CFArrayGetCount(active_vlans) : 0;
+
+       /* create a new vlan using an unused interface name */
+       allocator = CFGetAllocator(prefs);
+
+       for (i = 0; newVlan == NULL; i++) {
+               CFIndex                 j;
+               CFMutableDictionaryRef  newDict;
+               CFMutableArrayRef       newVlans;
+               CFStringRef             vlan_if;
+
+               vlan_if = CFStringCreateWithFormat(allocator, NULL, CFSTR("vlan%d"), i);
+
+               for (j = 0; j < nActive; j++) {
+                       CFStringRef             active_if;
+                       VLANInterfaceRef        active_vlan;
+
+                       active_vlan = CFArrayGetValueAtIndex(active_vlans, j);
+                       active_if   = VLANInterfaceGetInterface(active_vlan);
+
+                       if (CFEqual(vlan_if, active_if)) {
+                               goto next_if;   // if VLAN interface name not available
+                       }
+               }
+
+               for (j = 0; j < nConfig; j++) {
+                       CFDictionaryRef config;
+                       CFStringRef     config_if;
+
+                       config = CFArrayGetValueAtIndex(config_vlans, j);
+                       if (!isA_CFDictionary(config)) {
+                               continue;       // if the prefs are confused
+                       }
+
+                       config_if = CFDictionaryGetValue(config, __kVLANInterface_interface);
+                       if (!isA_CFString(config_if)) {
+                               continue;       // if the prefs are confused
+                       }
+
+                       if (CFEqual(vlan_if, config_if)) {
+                               goto next_if;   // if VLAN interface name not available
+                       }
+               }
+
+               /* create the vlan */
+
+               newDict = CFDictionaryCreateMutable(allocator,
+                                                   0,
+                                                   &kCFTypeDictionaryKeyCallBacks,
+                                                   &kCFTypeDictionaryValueCallBacks);
+               CFDictionaryAddValue(newDict, __kVLANInterface_interface, vlan_if);
+               CFDictionaryAddValue(newDict, __kVLANInterface_device,    device);
+               CFDictionaryAddValue(newDict, __kVLANInterface_tag,       tag);
+               if (options != NULL) {
+                       CFDictionaryAddValue(newDict, __kVLANInterface_options, options);
+               }
+
+               /* create the accessor handle to be returned */
+
+               newVlan = __VLANInterfaceCreatePrivate(allocator, vlan_if, device, tag, options);
+
+               /* save in the prefs */
+
+               if (nConfig == 0) {
+                       newVlans = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
+               } else {
+                       newVlans = CFArrayCreateMutableCopy(allocator, 0, config_vlans);
+               }
+               CFArrayAppendValue(newVlans, newDict);
+               CFRelease(newDict);
+
+               (void) SCPreferencesSetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS, newVlans);
+               CFRelease(newVlans);
+
+               /* yes, we've change the configuration */
+               setConfigurationChanged(prefs);
+
+           next_if :
+               CFRelease(vlan_if);
+       }
+
+       CFRelease(active_vlans);
+
+    done :
+
+       pthread_mutex_unlock(&prefsPrivate->lock);
+
+       return (VLANInterfaceRef) newVlan;
+}
+
+
+Boolean
+VLANPreferencesUpdateInterface(VLANPreferencesRef      prefs,
+                              VLANInterfaceRef         vlan,
+                              CFStringRef              newDevice,
+                              CFNumberRef              newTag,
+                              CFDictionaryRef          newOptions)
+{
+       CFAllocatorRef                  allocator;
+       CFIndex                         cur_if;
+       CFIndex                         dup_if;
+       CFMutableDictionaryRef          newDict;
+       CFMutableArrayRef               newVlans;
+       Boolean                         ok              = FALSE;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+       CFArrayRef                      vlans;
+       CFStringRef                     vlan_if;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!isA_VLANInterface(vlan)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if ((newDevice != NULL) && !isA_CFString(newDevice)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (newTag != NULL) {
+               if (isA_CFNumber(newTag)) {
+                       int     tag_val;
+
+                       CFNumberGetValue(newTag, kCFNumberIntType, &tag_val);
+                       if ((tag_val < 1) || (tag_val > 4094)) {
+                               _SCErrorSet(kSCStatusInvalidArgument);
+                               return FALSE;
+                       }
+               } else {
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       return FALSE;
+               }
+       }
+
+       if ((newOptions != NULL)
+           && !isA_CFDictionary(newOptions) && (newOptions != (CFDictionaryRef)kCFNull)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       pthread_mutex_lock(&prefsPrivate->lock);
+
+       vlan_if = VLANInterfaceGetInterface(vlan);
+
+       vlans = SCPreferencesGetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS);
+       if (!isA_CFArray(vlans)) {
+               goto done;      // if the prefs are confused
+       }
+
+       cur_if = findVLAN(vlans,
+                         VLANInterfaceGetDevice(vlan),
+                         VLANInterfaceGetTag   (vlan));
+       if (cur_if == kCFNotFound) {
+               _SCErrorSet(kSCStatusNoKey);
+               goto done;
+       }
+
+       dup_if = findVLAN(vlans,
+                         newDevice != NULL ? newDevice : VLANInterfaceGetDevice(vlan),
+                         newTag    != NULL ? newTag    : VLANInterfaceGetTag   (vlan));
+       if (dup_if != kCFNotFound) {
+               // if the same device/tag has already been defined
+               if (cur_if != dup_if) {
+                       /*
+                        * sorry, you can't update another vlan that is using
+                        * the same device/tag
+                        */
+                       _SCErrorSet(kSCStatusKeyExists);
+                       goto done;
+               }
+       }
+
+       /* update the vlan */
+
+       if (newDevice != NULL) {
+               VLANInterfaceSetDevice(vlan, newDevice);
+       } else {
+               newDevice = VLANInterfaceGetDevice(vlan);
+       }
+
+       if (newTag != NULL) {
+               VLANInterfaceSetTag(vlan, newTag);
+       } else {
+               newTag = VLANInterfaceGetTag(vlan);
+       }
+
+       if (newOptions != NULL) {
+               if (newOptions != (CFDictionaryRef)kCFNull) {
+                       VLANInterfaceSetOptions(vlan, newOptions);
+               } else {
+                       VLANInterfaceSetOptions(vlan, NULL);
+                       newOptions = NULL;
+               }
+       } else {
+               newOptions = VLANInterfaceGetOptions(vlan);
+       }
+
+       /* update the prefs */
+
+       allocator = CFGetAllocator(prefs);
+       newDict = CFDictionaryCreateMutable(allocator,
+                                           0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks);
+       CFDictionaryAddValue(newDict, __kVLANInterface_interface, vlan_if);
+       CFDictionaryAddValue(newDict, __kVLANInterface_device,    newDevice);
+       CFDictionaryAddValue(newDict, __kVLANInterface_tag,       newTag);
+       if (newOptions != NULL) {
+               CFDictionaryAddValue(newDict, __kVLANInterface_options, newOptions);
+       }
+
+       /* update the prefs */
+
+       newVlans = CFArrayCreateMutableCopy(allocator, 0, vlans);
+       CFArrayRemoveValueAtIndex(newVlans, cur_if);
+       CFArrayAppendValue(newVlans, newDict);
+       CFRelease(newDict);
+
+       (void) SCPreferencesSetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS, newVlans);
+       CFRelease(newVlans);
+
+       /* yes, we've change the configuration */
+       setConfigurationChanged(prefs);
+
+       ok = TRUE;
+
+    done :
+
+       pthread_mutex_unlock(&prefsPrivate->lock);
+
+       return ok;
+}
+
+
+Boolean
+VLANPreferencesRemoveInterface(VLANPreferencesRef      prefs,
+                              VLANInterfaceRef         vlan)
+{
+       CFAllocatorRef                  allocator;
+       CFIndex                         cur_if;
+       CFMutableArrayRef               newVlans;
+       Boolean                         ok              = FALSE;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+       CFArrayRef                      vlans;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       if (!isA_VLANInterface(vlan)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       pthread_mutex_lock(&prefsPrivate->lock);
+
+       vlans = SCPreferencesGetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS);
+       if (!isA_CFArray(vlans)) {
+               _SCErrorSet(kSCStatusNoKey);
+               goto done;      // if the prefs are confused
+       }
+
+       cur_if = findVLAN(vlans,
+                         VLANInterfaceGetDevice(vlan),
+                         VLANInterfaceGetTag   (vlan));
+       if (cur_if == kCFNotFound) {
+               _SCErrorSet(kSCStatusNoKey);
+               goto done;
+       }
+
+       /* remove the vlan */
+
+       allocator = CFGetAllocator(prefs);
+       newVlans = CFArrayCreateMutableCopy(allocator, 0, vlans);
+       CFArrayRemoveValueAtIndex(newVlans, cur_if);
+
+       (void) SCPreferencesSetValue(prefsPrivate->prefs, VLAN_PREFERENCES_VLANS, newVlans);
+       CFRelease(newVlans);
+
+       /* yes, we've change the configuration */
+       setConfigurationChanged(prefs);
+
+       ok = TRUE;
+
+    done :
+
+       pthread_mutex_unlock(&prefsPrivate->lock);
+
+       return ok;
+}
+
+
+Boolean
+VLANPreferencesCommitChanges(VLANPreferencesRef        prefs)
+{
+       Boolean                         ok              = FALSE;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       ok = SCPreferencesCommitChanges(prefsPrivate->prefs);
+       if (!ok) {
+               return ok;
+       }
+
+       if (prefsPrivate->vlBase != NULL)  {
+               CFRelease(prefsPrivate->vlBase);
+               prefsPrivate->vlBase = NULL;
+       }
+
+       return TRUE;
+}
+
+
+Boolean
+_VLANPreferencesUpdateConfiguration(VLANPreferencesRef prefs)
+{
+       CFArrayRef                      active          = NULL;
+       CFArrayRef                      config          = NULL;
+       CFMutableDictionaryRef          devices         = NULL;
+       CFIndex                         i;
+       CFIndex                         nActive;
+       CFIndex                         nConfig;
+       Boolean                         ok              = FALSE;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+       int                             s               = -1;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       /* configured VLANs */
+       if (prefsPrivate->vlBase != NULL) {
+               /*
+                * updated VLAN preferences have not been committed
+                * so we ignore any in-progress changes and apply the
+                * saved preferences.
+                */
+               config = CFRetain(prefsPrivate->vlBase);
+       } else {
+               /*
+                * apply the saved preferences
+                */
+               config = VLANPreferencesCopyInterfaces(prefs);
+       }
+       nConfig = CFArrayGetCount(config);
+
+       /* [parent] devices */
+       devices = CFDictionaryCreateMutable(NULL,
+                                           0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks);
+
+       /* active VLANs */
+       active  = _VLANPreferencesCopyActiveInterfaces();
+       nActive = CFArrayGetCount(active);
+
+       /* remove any no-longer-configured VLAN interfaces */
+       for (i = 0; i < nActive; i++) {
+               VLANInterfaceRef        a_vlan;
+               CFStringRef             a_vlan_if;
+               CFIndex                 j;
+               Boolean                 found   = FALSE;
+
+               a_vlan    = CFArrayGetValueAtIndex(active, i);
+               a_vlan_if = VLANInterfaceGetInterface(a_vlan);
+
+               for (j = 0; j < nConfig; j++) {
+                       VLANInterfaceRef        c_vlan;
+                       CFStringRef             c_vlan_if;
+
+                       c_vlan    = CFArrayGetValueAtIndex(config, j);
+                       c_vlan_if = VLANInterfaceGetInterface(c_vlan);
+
+                       if (CFEqual(a_vlan_if, c_vlan_if)) {
+                               found = TRUE;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       // remove VLAN interface
+                       if (s == -1) {
+                               s = inet_dgram_socket();
+                       }
+
+                       ok = _VLAN_destroy(s, a_vlan_if);
+                       if (!ok) {
+                               goto done;
+                       }
+               }
+       }
+
+       /* create (and update) configured VLAN interfaces */
+       for (i = 0; i < nConfig; i++) {
+               VLANInterfaceRef        c_vlan;
+               CFStringRef             c_vlan_device;
+               CFStringRef             c_vlan_if;
+               Boolean                 found           = FALSE;
+               CFIndex                 j;
+               CFBooleanRef            supported;
+
+               c_vlan        = CFArrayGetValueAtIndex(config, i);
+               c_vlan_device = VLANInterfaceGetDevice(c_vlan);
+               c_vlan_if     = VLANInterfaceGetInterface(c_vlan);
+
+               // determine if the [parent] device supports VLANs
+               supported = CFDictionaryGetValue(devices, c_vlan_device);
+               if (supported == NULL) {
+                       supported = IsVLANSupported(c_vlan_device) ? kCFBooleanTrue
+                                                                  : kCFBooleanFalse;
+                       CFDictionaryAddValue(devices, c_vlan_device, supported);
+               }
+
+               for (j = 0; j < nActive; j++) {
+                       VLANInterfaceRef        a_vlan;
+                       CFStringRef             a_vlan_if;
+
+                       a_vlan    = CFArrayGetValueAtIndex(active, j);
+                       a_vlan_if = VLANInterfaceGetInterface(a_vlan);
+
+                       if (CFEqual(c_vlan_if, a_vlan_if)) {
+                               if (!__VLANInterfaceEquiv(c_vlan, a_vlan)) {
+                                       // update VLAN interface;
+                                       if (s == -1) {
+                                               s = inet_dgram_socket();
+                                       }
+
+                                       if (CFBooleanGetValue(supported)) {
+                                               // if the new [parent] device supports VLANs
+                                               ok = _VLANDevice_unset(s, c_vlan_if);
+                                               if (!ok) {
+                                                       goto done;
+                                               }
+
+                                               ok = _VLANDevice_set(s,
+                                                                    c_vlan_if,
+                                                                    c_vlan_device,
+                                                                    VLANInterfaceGetTag(c_vlan));
+                                               if (!ok) {
+                                                       goto done;
+                                               }
+                                       } else {
+                                               // if the new [parent] device does not support VLANs
+                                               ok = _VLAN_destroy(s, c_vlan_if);
+                                               if (!ok) {
+                                                       goto done;
+                                               }
+                                       }
+                               }
+
+                               found = TRUE;
+                               break;
+                       }
+               }
+
+               if (!found && CFBooleanGetValue(supported)) {
+                       // if the [parent] device supports VLANs, add new interface
+                       if (s == -1) {
+                               s = inet_dgram_socket();
+                       }
+
+                       ok = _VLAN_create(s, c_vlan_if);
+                       if (!ok) {
+                               goto done;
+                       }
+
+                       ok = _VLANDevice_set(s,
+                                            c_vlan_if,
+                                            c_vlan_device,
+                                            VLANInterfaceGetTag(c_vlan));
+                       if (!ok) {
+                               goto done;
+                       }
+               }
+
+       }
+
+       ok = TRUE;
+
+    done :
+
+       if (active)     CFRelease(active);
+       if (config)     CFRelease(config);
+       if (devices)    CFRelease(devices);
+       if (s != -1)    (void) close(s);
+
+       return ok;
+}
+
+
+Boolean
+VLANPreferencesApplyChanges(VLANPreferencesRef prefs)
+{
+       Boolean                         ok              = FALSE;
+       VLANPreferencesPrivateRef       prefsPrivate    = (VLANPreferencesPrivateRef)prefs;
+
+       if (!isA_VLANPreferences(prefs)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
+       }
+
+       pthread_mutex_lock(&prefsPrivate->lock);
+
+       /* apply the preferences */
+       ok = SCPreferencesApplyChanges(prefsPrivate->prefs);
+       if (!ok) {
+               goto done;
+       }
+
+       /* apply the VLAN configuration */
+       ok = _VLANPreferencesUpdateConfiguration(prefs);
+       if (!ok) {
+               goto done;
+       }
+
+       ok = TRUE;
+
+    done :
+
+       pthread_mutex_unlock(&prefsPrivate->lock);
+
+       return ok;
+}