BondConfiguration.c   [plain text]


/*
 * Copyright (c) 2004-2007 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,
 * 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 28, 2005		Allan Nathanson <ajn@apple.com>
 * - public API
 *
 * July 22, 2004		Allan Nathanson <ajn@apple.com>
 * - initial revision
 */


#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRuntime.h>

#include <SystemConfiguration/SystemConfiguration.h>
#include "SCNetworkConfigurationInternal.h"
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h>

#include <SystemConfiguration/BondConfiguration.h>

#include <ifaddrs.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/ethernet.h>
#define	KERNEL_PRIVATE
#include <net/if.h>
#include <net/if_var.h>
#undef	KERNEL_PRIVATE
#include <net/if_bond_var.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <net/route.h>

/* ---------- Bond 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));
	}

	return s;
}

static int
siocgifmedia(int s, const char * ifname, int * status, int * active)
{
	struct ifmediareq	ifmr;

	*status = 0;
	*active = 0;
	bzero(&ifmr, sizeof(ifmr));
	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
	if (ioctl(s, SIOCGIFMEDIA, &ifmr) == -1) {
		return (-1);
	}
	if (ifmr.ifm_count != 0) {
		*status = ifmr.ifm_status;
		*active = ifmr.ifm_active;
	}
	return (0);
}

static struct if_bond_status_req *
if_bond_status_req_copy(int s, const char * ifname)
{
	void *				buf = NULL;
	struct if_bond_req		ibr;
	struct if_bond_status_req *	ibsr_p;
	struct ifreq			ifr;

	bzero(&ifr, sizeof(ifr));
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	bzero((char *)&ibr, sizeof(ibr));
	ibr.ibr_op = IF_BOND_OP_GET_STATUS;
	ibsr_p = &ibr.ibr_ibru.ibru_status;
	ibsr_p->ibsr_version = IF_BOND_STATUS_REQ_VERSION;
	ifr.ifr_data = (caddr_t)&ibr;

	/* how many of them are there? */
	if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) == -1) {
		goto failed;
	}
	buf = malloc(sizeof(struct if_bond_status) * ibsr_p->ibsr_total + sizeof(*ibsr_p));
	if (buf == NULL) {
		goto failed;
	}
	if (ibsr_p->ibsr_total == 0) {
		goto done;
	}
	ibsr_p->ibsr_count = ibsr_p->ibsr_total;
	ibsr_p->ibsr_buffer = buf + sizeof(*ibsr_p);

	/* get the list */
	if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) == -1) {
		goto failed;
	}
 done:
	(*(struct if_bond_status_req *)buf) = *ibsr_p;
	return ((struct if_bond_status_req *)buf);

 failed:
	if (buf != NULL) {
		free(buf);
	}
	return (NULL);
}


static void
add_interface(CFMutableArrayRef *interfaces, CFStringRef if_name)
{
	SCNetworkInterfaceRef	interface;

	if (*interfaces == NULL) {
		*interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	}

	interface = _SCNetworkInterfaceCreateWithBSDName(NULL, if_name,
							 kIncludeNoVirtualInterfaces);
	CFArrayAppendValue(*interfaces, interface);
	CFRelease(interface);
}


static Boolean
_SCBondInterfaceSetMemberInterfaces(SCBondInterfaceRef bond, CFArrayRef members);

static Boolean
_SCBondInterfaceSetMode(SCBondInterfaceRef bond, CFNumberRef mode);


typedef struct {
	CFMutableArrayRef	bonds;
	SCPreferencesRef	prefs;
} addContext, *addContextRef;


static void
add_configured_interface(const void *key, const void *value, void *context)
{
	SCBondInterfaceRef		bond;
	CFStringRef			bond_if		= (CFStringRef)key;
	CFDictionaryRef			bond_info	= (CFDictionaryRef)value;
	CFDictionaryRef			bond_options;
	CFIndex				i;
	CFArrayRef			interfaces;
	SCNetworkInterfacePrivateRef	interfacePrivate;
	CFMutableArrayRef		members		= NULL;
	CFNumberRef			mode;
	addContextRef			myContext	= (addContextRef)context;
	CFStringRef			name;
	CFIndex				n;

	// create the bond interface
	bond = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(NULL, bond_if);

	// add member interfaces
	interfaces = CFDictionaryGetValue(bond_info, kSCPropVirtualNetworkInterfacesBondInterfaces);
	n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
	for (i = 0; i < n; i++) {
		CFStringRef	member;

		member = CFArrayGetValueAtIndex(interfaces, i);
		if (isA_CFString(member)) {
			add_interface(&members, member);
		}
	}
	if (members != NULL) {
		_SCBondInterfaceSetMemberInterfaces(bond, members);
		CFRelease(members);
	}

	// set display name
	name = CFDictionaryGetValue(bond_info, kSCPropUserDefinedName);
	if (isA_CFString(name)) {
		SCBondInterfaceSetLocalizedDisplayName(bond, name);
	}

	// set options
	bond_options = CFDictionaryGetValue(bond_info, kSCPropVirtualNetworkInterfacesBondOptions);
	if (isA_CFDictionary(bond_options)) {
		SCBondInterfaceSetOptions(bond, bond_options);
	}

	// set the mode
	mode = CFDictionaryGetValue(bond_info, kSCPropVirtualNetworkInterfacesBondMode);
	_SCBondInterfaceSetMode(bond, isA_CFNumber(mode));

	// estabish link to the stored configuration
	interfacePrivate = (SCNetworkInterfacePrivateRef)bond;
	interfacePrivate->prefs = CFRetain(myContext->prefs);

	CFArrayAppendValue(myContext->bonds, bond);
	CFRelease(bond);

	return;
}


static void
add_legacy_configuration(addContextRef myContext)
{
	CFArrayRef		bonds;
	CFIndex			i;
	CFIndex			n_bonds;
	SCPreferencesRef	prefs;

#define BOND_PREFERENCES_ID		CFSTR("VirtualNetworkInterfaces.plist")
#define	BOND_PREFERENCES_BONDS		CFSTR("Bonds")
#define	__kBondInterface_interface	CFSTR("interface")	// e.g. bond0, bond1, ...
#define	__kBondInterface_devices	CFSTR("devices")	// e.g. en0, en1, ...
#define __kBondInterface_options	CFSTR("options")	// e.g. UserDefinedName

	prefs = SCPreferencesCreate(NULL, CFSTR("SCBondInterfaceCopyAll"), BOND_PREFERENCES_ID);
	if (prefs == NULL) {
		return;
	}

	bonds = SCPreferencesGetValue(prefs, BOND_PREFERENCES_BONDS);
	if ((bonds != NULL) && !isA_CFArray(bonds)) {
		CFRelease(prefs);	// if the prefs are confused
		return;
	}

	n_bonds = (bonds != NULL) ? CFArrayGetCount(bonds) : 0;
	for (i = 0; i < n_bonds; i++) {
		SCBondInterfaceRef		bond;
		CFDictionaryRef			bond_dict;
		CFStringRef			bond_if;
		CFDictionaryRef			dict;
		CFArrayRef			interfaces;
		SCNetworkInterfacePrivateRef	interfacePrivate;
		CFIndex				j;
		CFMutableArrayRef		members		= NULL;
		CFMutableDictionaryRef		newDict;
		CFArrayRef			newInterfaces;
		CFIndex				n_interfaces;
		Boolean				ok;
		CFDictionaryRef			options;
		CFStringRef			path;

		bond_dict = CFArrayGetValueAtIndex(bonds, i);
		if (!isA_CFDictionary(bond_dict)) {
			continue;	// if the prefs are confused
		}

		bond_if = CFDictionaryGetValue(bond_dict, __kBondInterface_interface);
		if (!isA_CFString(bond_if)) {
			continue;	// if the prefs are confused
		}

		// check if this bond has already been allocated
		path = CFStringCreateWithFormat(NULL,
						NULL,
						CFSTR("/%@/%@/%@"),
						kSCPrefVirtualNetworkInterfaces,
						kSCNetworkInterfaceTypeBond,
						bond_if);
		dict = SCPreferencesPathGetValue(myContext->prefs, path);
		if (dict != NULL) {
			// if bond interface name not available
			CFRelease(path);
			continue;
		}

		// add a placeholder for the bond in the stored preferences
		newDict = CFDictionaryCreateMutable(NULL,
						    0,
						    &kCFTypeDictionaryKeyCallBacks,
						    &kCFTypeDictionaryValueCallBacks);
		newInterfaces = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
		CFDictionaryAddValue(newDict, kSCPropVirtualNetworkInterfacesBondInterfaces, newInterfaces);
		CFRelease(newInterfaces);
		ok = SCPreferencesPathSetValue(myContext->prefs, path, newDict);
		CFRelease(newDict);
		CFRelease(path);
		if (!ok) {
			// if the bond could not be saved
			continue;
		}

		// create the bond interface
		bond = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(NULL, bond_if);

		// estabish link to the stored configuration
		interfacePrivate = (SCNetworkInterfacePrivateRef)bond;
		interfacePrivate->prefs = CFRetain(myContext->prefs);

		// add member interfaces
		interfaces = CFDictionaryGetValue(bond_dict, __kBondInterface_devices);
		n_interfaces = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
		for (j = 0; j < n_interfaces; j++) {
			CFStringRef	member;

			member = CFArrayGetValueAtIndex(interfaces, j);
			if (isA_CFString(member)) {
				add_interface(&members, member);
			}
		}
		if (members != NULL) {
			_SCBondInterfaceSetMemberInterfaces(bond, members);
			CFRelease(members);
		}

		// set display name
		options = CFDictionaryGetValue(bond_dict, __kBondInterface_options);
		if (isA_CFDictionary(options)) {
			CFStringRef	name;

			name = CFDictionaryGetValue(options, kSCPropUserDefinedName);
			if (isA_CFString(name)) {
				SCBondInterfaceSetLocalizedDisplayName(bond, name);
			}
		}

		CFArrayAppendValue(myContext->bonds, bond);
		CFRelease(bond);
	}

	CFRelease(prefs);
	return;
}


#pragma mark -
#pragma mark SCBondInterface APIs


CFArrayRef
SCBondInterfaceCopyAll(SCPreferencesRef prefs)
{
	addContext		context;
	CFDictionaryRef		dict;
	CFStringRef		path;

	context.bonds = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	context.prefs = prefs;

	path = CFStringCreateWithFormat(NULL,
					NULL,
					CFSTR("/%@/%@"),
					kSCPrefVirtualNetworkInterfaces,
					kSCNetworkInterfaceTypeBond);
	dict = SCPreferencesPathGetValue(prefs, path);
	if (isA_CFDictionary(dict)) {
		CFDictionaryApplyFunction(dict, add_configured_interface, &context);
	} else {
		// no bond configuration, upgrade from legacy configuration
		dict = CFDictionaryCreate(NULL,
					  NULL, NULL, 0,
					  &kCFTypeDictionaryKeyCallBacks,
					  &kCFTypeDictionaryValueCallBacks);
		(void) SCPreferencesPathSetValue(prefs, path, dict);
		CFRelease(dict);

		add_legacy_configuration(&context);
	}
	CFRelease(path);

	return context.bonds;
}


__private_extern__ void
__SCBondInterfaceListCopyMembers(CFArrayRef interfaces, CFMutableSetRef set)
{
	CFIndex	i;
	CFIndex	n;

	n = CFArrayGetCount(interfaces);
	for (i = 0; i < n; i++) {
		SCBondInterfaceRef	bondInterface;
		CFArrayRef		members;

		bondInterface = CFArrayGetValueAtIndex(interfaces, i);
		members = SCBondInterfaceGetMemberInterfaces(bondInterface);
		if (members != NULL) {
			CFIndex	j;
			CFIndex	n_members;

			// exclude the member interfaces of this bond
			n_members = CFArrayGetCount(members);
			for (j = 0; j < n_members; j++) {
				SCNetworkInterfaceRef	member;

				member = CFArrayGetValueAtIndex(members, j);
				CFSetAddValue(set, member);
			}
		}

	}
	return;
}


CFArrayRef /* of SCNetworkInterfaceRef's */
SCBondInterfaceCopyAvailableMemberInterfaces(SCPreferencesRef prefs)
{
	CFMutableArrayRef	available;
	CFMutableSetRef		exclude;
	CFArrayRef		interfaces;

	available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	exclude   = CFSetCreateMutable  (NULL, 0, &kCFTypeSetCallBacks);

	// exclude Bond [member] interfaces
	interfaces = SCBondInterfaceCopyAll(prefs);
	if (interfaces != NULL) {
		__SCBondInterfaceListCopyMembers(interfaces, exclude);
		CFRelease(interfaces);
	}

	// exclude VLAN [physical] interfaces
	interfaces = SCVLANInterfaceCopyAll(prefs);
	if (interfaces != NULL) {
		CFIndex	i;
		CFIndex	n;

		n = CFArrayGetCount(interfaces);
		for (i = 0; i < n; i++) {
			SCVLANInterfaceRef	vlanInterface;
			SCNetworkInterfaceRef	physical;

			// exclude the physical interface of this VLAN
			vlanInterface = CFArrayGetValueAtIndex(interfaces, i);
			physical = SCVLANInterfaceGetPhysicalInterface(vlanInterface);
			CFSetAddValue(exclude, physical);
		}
		CFRelease(interfaces);
	}

	// identify available interfaces
	interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
	if (interfaces != NULL) {
		CFIndex	i;
		CFIndex	n;

		n = CFArrayGetCount(interfaces);
		for (i = 0; i < n; i++) {
			SCNetworkInterfaceRef		interface;
			SCNetworkInterfacePrivateRef	interfacePrivate;

			interface = CFArrayGetValueAtIndex(interfaces, i);
			interfacePrivate = (SCNetworkInterfacePrivateRef)interface;

			if (!interfacePrivate->supportsBond) {
				// if this interface is not available
				continue;
			}

			if (CFSetContainsValue(exclude, interface)) {
				// if excluded
				continue;
			}

			CFArrayAppendValue(available, interface);
		}
		CFRelease(interfaces);
	}

	CFRelease(exclude);

	return available;
}


CFArrayRef
_SCBondInterfaceCopyActive(void)
{
	struct ifaddrs		*ifap;
	struct ifaddrs		*ifp;
	int			s;
	CFMutableArrayRef	bonds	= NULL;

	if (getifaddrs(&ifap) == -1) {
		_SCErrorSet(errno);
		SCLog(TRUE, LOG_ERR, CFSTR("getifaddrs() failed: %s"), strerror(errno));
		return NULL;
	}

	s = inet_dgram_socket();
	if (s == -1) {
		_SCErrorSet(errno);
		goto done;
	}

	bonds = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

	for (ifp = ifap; ifp != NULL; ifp = ifp->ifa_next) {
		SCBondInterfaceRef		bond;
		CFStringRef			bond_if;
		struct if_bond_status_req	*ibsr_p;
		struct if_data			*if_data;
		int				int_val;
		CFNumberRef			mode;
		CFMutableArrayRef		members		= NULL;

		if_data = (struct if_data *)ifp->ifa_data;
		if (if_data == NULL
		    || ifp->ifa_addr->sa_family != AF_LINK
		    || if_data->ifi_type != IFT_IEEE8023ADLAG) {
			continue;
		}

		ibsr_p = if_bond_status_req_copy(s, ifp->ifa_name);
		if (ibsr_p == NULL) {
			if (errno == EBUSY) {
				continue;
			}
			_SCErrorSet(errno);
			SCLog(TRUE, LOG_ERR,
			      CFSTR("if_bond_status_req_copy(%s) failed: %s"),
			      ifp->ifa_name,
			      strerror(errno));
			CFRelease(bonds);
			bonds = NULL;
			goto done;
		}

		// create the bond interface
		bond_if = CFStringCreateWithCString(NULL, ifp->ifa_name, kCFStringEncodingASCII);
		bond    = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(NULL, bond_if);
		CFRelease(bond_if);

		// set the mode
		int_val = ibsr_p->ibsr_mode;
		mode = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
		_SCBondInterfaceSetMode(bond, mode);
		CFRelease(mode);

		// add member interfaces
		if (ibsr_p->ibsr_total > 0) {
			int 			i;
			struct if_bond_status *	ibs_p;

			// iterate over each member interface
			ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer;
			for (i = 0; i < ibsr_p->ibsr_total; i++) {
				char		if_name[IFNAMSIZ + 1];
				CFStringRef	member;

				bzero(&if_name, sizeof(if_name));
				bcopy(ibs_p[i].ibs_if_name, if_name, IFNAMSIZ);
				member = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
				add_interface(&members, member);
				CFRelease(member);
			}
		}
		free(ibsr_p);

		if (members != NULL) {
			_SCBondInterfaceSetMemberInterfaces(bond, members);
			CFRelease(members);
		}

		// add bond
		CFArrayAppendValue(bonds, bond);
		CFRelease(bond);
	}

    done :

	(void) close(s);
	freeifaddrs(ifap);
	return bonds;
}


SCBondInterfaceRef
SCBondInterfaceCreate(SCPreferencesRef prefs)
{
	CFAllocatorRef		allocator;
	SCBondInterfaceRef	bond		= NULL;
	CFIndex			i;

	if (prefs == NULL) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}

	allocator = CFGetAllocator(prefs);

	// create a new bond using an unused interface name
	for (i = 0; bond == NULL; i++) {
		CFDictionaryRef			dict;
		CFStringRef			bond_if;
		SCNetworkInterfacePrivateRef	interfacePrivate;
		CFMutableDictionaryRef		newDict;
		CFArrayRef			newInterfaces;
		Boolean				ok;
		CFStringRef			path;

		bond_if = CFStringCreateWithFormat(allocator, NULL, CFSTR("bond%d"), i);
		path    = CFStringCreateWithFormat(allocator,
						   NULL,
						   CFSTR("/%@/%@/%@"),
						   kSCPrefVirtualNetworkInterfaces,
						   kSCNetworkInterfaceTypeBond,
						   bond_if);
		dict = SCPreferencesPathGetValue(prefs, path);
		if (dict != NULL) {
			// if bond interface name not available
			CFRelease(path);
			CFRelease(bond_if);
			continue;
		}

		// add the bond to the stored preferences
		newDict = CFDictionaryCreateMutable(allocator,
						    0,
						    &kCFTypeDictionaryKeyCallBacks,
						    &kCFTypeDictionaryValueCallBacks);
		newInterfaces = CFArrayCreate(allocator, NULL, 0, &kCFTypeArrayCallBacks);
		CFDictionaryAddValue(newDict, kSCPropVirtualNetworkInterfacesBondInterfaces, newInterfaces);
		CFRelease(newInterfaces);
		ok = SCPreferencesPathSetValue(prefs, path, newDict);
		CFRelease(newDict);
		CFRelease(path);
		if (!ok) {
			// if the bond could not be saved
			CFRelease(bond_if);
			break;
		}

		// create the SCBondInterfaceRef
		bond = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(allocator, bond_if);
		CFRelease(bond_if);

		// estabish link to the stored configuration
		interfacePrivate = (SCNetworkInterfacePrivateRef)bond;
		interfacePrivate->prefs = CFRetain(prefs);
	}

	return bond;
}


Boolean
SCBondInterfaceRemove(SCBondInterfaceRef bond)
{
	CFStringRef			bond_if;
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
	Boolean				ok;
	CFStringRef			path;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (interfacePrivate->prefs == NULL) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	bond_if = SCNetworkInterfaceGetBSDName(bond);
	path    = CFStringCreateWithFormat(NULL,
					   NULL,
					   CFSTR("/%@/%@/%@"),
					   kSCPrefVirtualNetworkInterfaces,
					   kSCNetworkInterfaceTypeBond,
					   bond_if);
	ok = SCPreferencesPathRemoveValue(interfacePrivate->prefs, path);
	CFRelease(path);

	return ok;
}


CFArrayRef
SCBondInterfaceGetMemberInterfaces(SCBondInterfaceRef bond)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}

	return interfacePrivate->bond.interfaces;
}


CFDictionaryRef
SCBondInterfaceGetOptions(SCBondInterfaceRef bond)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}

	return interfacePrivate->bond.options;
}


static Boolean
_SCBondInterfaceSetMemberInterfaces(SCBondInterfaceRef bond, CFArrayRef members)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
	Boolean				ok			= TRUE;

	// set member interfaces in the stored preferences
	if (interfacePrivate->prefs != NULL) {
		CFDictionaryRef		dict;
		CFIndex			i;
		CFIndex			n;
		CFMutableDictionaryRef	newDict;
		CFMutableArrayRef	newMembers;
		CFStringRef		path;

		path = CFStringCreateWithFormat(NULL,
						NULL,
						CFSTR("/%@/%@/%@"),
						kSCPrefVirtualNetworkInterfaces,
						kSCNetworkInterfaceTypeBond,
						interfacePrivate->entity_device);
		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
		if (!isA_CFDictionary(dict)) {
			// if the prefs are confused
			CFRelease(path);
			_SCErrorSet(kSCStatusFailed);
			return FALSE;
		}

		newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
		n = (members != NULL) ? CFArrayGetCount(members) : 0;
		for (i = 0; i < n; i++) {
			SCNetworkInterfaceRef	interface;
			CFStringRef		memberName;

			interface = CFArrayGetValueAtIndex(members, i);
			memberName = SCNetworkInterfaceGetBSDName(interface);
			CFArrayAppendValue(newMembers, memberName);
		}

		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
		CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesBondInterfaces, newMembers);
		CFRelease(newMembers);

		ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
		CFRelease(newDict);
		CFRelease(path);
	}

	if (ok) {
		CFRelease(interfacePrivate->bond.interfaces);
		if (members == NULL) {
			interfacePrivate->bond.interfaces = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
		} else {
			interfacePrivate->bond.interfaces = CFArrayCreateCopy(NULL, members);
		}
	}

	return ok;
}


Boolean
SCBondInterfaceSetMemberInterfaces(SCBondInterfaceRef bond, CFArrayRef members)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
	Boolean				ok			= TRUE;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if ((members != NULL) && !isA_CFArray(members)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (interfacePrivate->prefs != NULL) {
		CFArrayRef	available;
		CFArrayRef	current;
		CFIndex		i;
		CFIndex		n_available;
		CFIndex		n_current;
		CFIndex		n_members;

		current     = SCBondInterfaceGetMemberInterfaces(bond);
		n_current   = (current != NULL) ? CFArrayGetCount(current) : 0;

		available   = SCBondInterfaceCopyAvailableMemberInterfaces(interfacePrivate->prefs);
		n_available = (available != NULL) ? CFArrayGetCount(available) : 0;

		n_members = (members != NULL) ? CFArrayGetCount(members) : 0;
		for (i = 0; i < n_members; i++) {
			SCNetworkInterfaceRef	member;

			member = CFArrayGetValueAtIndex(members, i);

			if ((current != NULL) &&
			    CFArrayContainsValue(current, CFRangeMake(0, n_current), member)) {
				// current members are allowed
				continue;
			}

			if ((available != NULL) &&
			    CFArrayContainsValue(available, CFRangeMake(0, n_available), member)) {
				// available members are allowed
				continue;
			}

			// if member not allowed
			ok = FALSE;
			break;
		}

		CFRelease(available);

		if (!ok) {
			_SCErrorSet(kSCStatusInvalidArgument);
		}
	}

	if (ok) {
		ok = _SCBondInterfaceSetMemberInterfaces(bond, members);
	}

	return ok;
}


Boolean
SCBondInterfaceSetLocalizedDisplayName(SCBondInterfaceRef bond, CFStringRef newName)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
	Boolean				ok			= TRUE;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if ((newName != NULL) && !isA_CFString(newName)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	// set name in the stored preferences
	if (interfacePrivate->prefs != NULL) {
		CFDictionaryRef		dict;
		CFMutableDictionaryRef	newDict;
		CFStringRef		path;

		path = CFStringCreateWithFormat(NULL,
						NULL,
						CFSTR("/%@/%@/%@"),
						kSCPrefVirtualNetworkInterfaces,
						kSCNetworkInterfaceTypeBond,
						interfacePrivate->entity_device);
		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
		if (!isA_CFDictionary(dict)) {
			// if the prefs are confused
			CFRelease(path);
			_SCErrorSet(kSCStatusFailed);
			return FALSE;
		}

		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
		if (newName != NULL) {
			CFDictionarySetValue(newDict, kSCPropUserDefinedName, newName);
		} else {
			CFDictionaryRemoveValue(newDict, kSCPropUserDefinedName);
		}
		ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
		CFRelease(newDict);
		CFRelease(path);
	}

	// set name in the SCBondInterfaceRef
	if (ok) {
		if (interfacePrivate->localized_name != NULL) {
			CFRelease(interfacePrivate->localized_name);
			interfacePrivate->localized_name = NULL;
		}
		if (newName != NULL) {
			interfacePrivate->localized_name = CFStringCreateCopy(NULL, newName);
		}
	}

	return ok;
}


Boolean
SCBondInterfaceSetOptions(SCBondInterfaceRef bond, CFDictionaryRef newOptions)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
	Boolean				ok			= TRUE;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if ((newOptions != NULL) && !isA_CFDictionary(newOptions)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	// set options in the stored preferences
	if (interfacePrivate->prefs != NULL) {
		CFDictionaryRef		dict;
		CFMutableDictionaryRef	newDict;
		CFStringRef		path;

		path = CFStringCreateWithFormat(NULL,
						NULL,
						CFSTR("/%@/%@/%@"),
						kSCPrefVirtualNetworkInterfaces,
						kSCNetworkInterfaceTypeBond,
						interfacePrivate->entity_device);
		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
		if (!isA_CFDictionary(dict)) {
			// if the prefs are confused
			CFRelease(path);
			_SCErrorSet(kSCStatusFailed);
			return FALSE;
		}

		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
		if (newOptions != NULL) {
			CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesBondOptions, newOptions);
		} else {
			CFDictionaryRemoveValue(newDict, kSCPropVirtualNetworkInterfacesBondOptions);
		}
		ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
		CFRelease(newDict);
		CFRelease(path);
	}

	// set options in the SCBondInterfaceRef
	if (ok) {
		if (interfacePrivate->bond.options != NULL) {
			CFRelease(interfacePrivate->bond.options);
			interfacePrivate->bond.options = NULL;
		}
		if (newOptions != NULL) {
			interfacePrivate->bond.options = CFDictionaryCreateCopy(NULL, newOptions);
		}
	}

	return ok;
}


static Boolean
_SCBondInterfaceSetMode(SCBondInterfaceRef bond, CFNumberRef mode)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
	Boolean				needs_release 		= FALSE;
	Boolean				ok			= TRUE;

	if (mode == NULL) {
		int	mode_num	= IF_BOND_MODE_LACP;

		mode = CFNumberCreate(NULL, kCFNumberIntType, &mode_num);
		needs_release = TRUE;
	}

	// set mode in the stored preferences
	if (interfacePrivate->prefs != NULL) {
		CFDictionaryRef		dict;
		CFMutableDictionaryRef	newDict;
		CFStringRef		path;

		path = CFStringCreateWithFormat(NULL,
						NULL,
						CFSTR("/%@/%@/%@"),
						kSCPrefVirtualNetworkInterfaces,
						kSCNetworkInterfaceTypeBond,
						interfacePrivate->entity_device);
		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
		if (!isA_CFDictionary(dict)) {
			// if the prefs are confused
			CFRelease(path);
			_SCErrorSet(kSCStatusFailed);
			ok = FALSE;
			goto done;
		}
		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
		CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesBondMode, mode);

		ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
		CFRelease(newDict);
		CFRelease(path);
	}

	if (ok) {
		CFRetain(mode);
		if (interfacePrivate->bond.mode != NULL) {
			CFRelease(interfacePrivate->bond.mode);
		}
		interfacePrivate->bond.mode = mode;
	}

    done :

	if (needs_release) CFRelease(mode);
	return ok;
}

Boolean
SCBondInterfaceSetMode(SCBondInterfaceRef bond, CFNumberRef mode)
{
	int	mode_num;

	if (!isA_SCBondInterface(bond) || !isA_CFNumber(mode)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (CFNumberGetValue(mode, kCFNumberIntType, &mode_num) == FALSE) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	switch (mode_num) {
		case IF_BOND_MODE_LACP:
		case IF_BOND_MODE_STATIC:
			break;
		default:
			_SCErrorSet(kSCStatusInvalidArgument);
			return FALSE;
	}

	return (_SCBondInterfaceSetMode(bond, mode));
}

CFNumberRef
SCBondInterfaceGetMode(SCBondInterfaceRef bond)
{
	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}
	return (interfacePrivate->bond.mode);
}


#pragma mark -
#pragma mark SCBondStatus APIs


typedef struct {

	// base CFType information
	CFRuntimeBase			cfBase;

	// bond status
	SCBondInterfaceRef		bond;
	CFDictionaryRef			status_bond;

	// member interfaces and status
	CFArrayRef			interfaces;		// of SCNetworkInterfaceRef's
	CFDictionaryRef			status_interfaces;	// key = interface, val = interface status)

} SCBondStatusPrivate, * SCBondStatusPrivateRef;


const CFStringRef kSCBondStatusDeviceAggregationStatus	= CFSTR("AggregationStatus");
const CFStringRef kSCBondStatusDeviceCollecting		= CFSTR("Collecting");
const CFStringRef kSCBondStatusDeviceDistributing	= CFSTR("Distributing");


static CFStringRef	__SCBondStatusCopyDescription	(CFTypeRef cf);
static void		__SCBondStatusDeallocate	(CFTypeRef cf);
static Boolean		__SCBondStatusEqual		(CFTypeRef cf1, CFTypeRef cf2);


static const CFRuntimeClass __SCBondStatusClass = {
	0,				// version
	"BondStatus",			// className
	NULL,				// init
	NULL,				// copy
	__SCBondStatusDeallocate,	// dealloc
	__SCBondStatusEqual,		// equal
	NULL,				// hash
	NULL,				// copyFormattingDesc
	__SCBondStatusCopyDescription	// copyDebugDesc
};


static CFTypeID		__kSCBondStatusTypeID	= _kCFRuntimeNotATypeID;


static pthread_once_t	bondStatus_init		= PTHREAD_ONCE_INIT;


static CFStringRef
__SCBondStatusCopyDescription(CFTypeRef cf)
{
	CFAllocatorRef		allocator	= CFGetAllocator(cf);
	CFMutableStringRef	result;
	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)cf;

	result = CFStringCreateMutable(allocator, 0);
	CFStringAppendFormat(result, NULL, CFSTR("<SCBondStatus %p [%p]> {"), cf, allocator);
	CFStringAppendFormat(result, NULL, CFSTR(" bond = %@"), statusPrivate->bond);
	CFStringAppendFormat(result, NULL, CFSTR(", interface = %@"), statusPrivate->status_bond);
	CFStringAppendFormat(result, NULL, CFSTR(", members = %@"),   statusPrivate->status_interfaces);
	CFStringAppendFormat(result, NULL, CFSTR(" }"));

	return result;
}


static void
__SCBondStatusDeallocate(CFTypeRef cf)
{
	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)cf;

	/* release resources */

	CFRelease(statusPrivate->bond);
	CFRelease(statusPrivate->status_bond);
	if (statusPrivate->interfaces != NULL) CFRelease(statusPrivate->interfaces);
	CFRelease(statusPrivate->status_interfaces);
	return;
}


static Boolean
__SCBondStatusEqual(CFTypeRef cf1, CFTypeRef cf2)
{
	SCBondStatusPrivateRef	status1	= (SCBondStatusPrivateRef)cf1;
	SCBondStatusPrivateRef	status2	= (SCBondStatusPrivateRef)cf2;

	if (status1 == status2)
		return TRUE;

	if (!CFEqual(status1->bond, status2->bond))
		return FALSE;	// if not the same bond

	if (!CFEqual(status1->status_bond, status2->status_bond))
		return FALSE;	// if not the same interface status

	if (!CFEqual(status1->status_interfaces, status2->status_interfaces))
		return FALSE;	// if not the same status of the member interfaces

	return TRUE;
}


static void
__SCBondStatusInitialize(void)
{
	__kSCBondStatusTypeID = _CFRuntimeRegisterClass(&__SCBondStatusClass);
	return;
}


static SCBondStatusRef
__SCBondStatusCreatePrivate(CFAllocatorRef	allocator,
			    SCBondInterfaceRef	bond,
			    CFDictionaryRef	status_bond,
			    CFDictionaryRef	status_interfaces)
{
	SCBondStatusPrivateRef	statusPrivate;
	uint32_t		size;

	/* initialize runtime */
	pthread_once(&bondStatus_init, __SCBondStatusInitialize);

	/* allocate bond */
	size          = sizeof(SCBondStatusPrivate) - sizeof(CFRuntimeBase);
	statusPrivate = (SCBondStatusPrivateRef)_CFRuntimeCreateInstance(allocator,
									 __kSCBondStatusTypeID,
									 size,
									 NULL);
	if (statusPrivate == NULL) {
		return NULL;
	}

	/* establish the bond status */

	statusPrivate->bond		 = CFRetain(bond);
	statusPrivate->status_bond       = CFDictionaryCreateCopy(NULL, status_bond);

	statusPrivate->interfaces        = NULL;
	statusPrivate->status_interfaces = CFDictionaryCreateCopy(NULL, status_interfaces);

	return (SCBondStatusRef)statusPrivate;
}


static __inline__ CFTypeRef
isA_SCBondStatus(CFTypeRef obj)
{
	return (isA_CFType(obj, SCBondStatusGetTypeID()));
}


CFTypeID
SCBondStatusGetTypeID()
{
	pthread_once(&bondStatus_init, __SCBondStatusInitialize);	/* initialize runtime */
	return __kSCBondStatusTypeID;
}


#define	N_QUICK	16


CFArrayRef /* of SCNetworkInterfaceRef's */
SCBondStatusGetMemberInterfaces(SCBondStatusRef bondStatus)
{
	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)bondStatus;

	if (!isA_SCBondStatus(bondStatus)) {
		return NULL;
	}

	if (statusPrivate->interfaces == NULL) {
		const void *	keys_q[N_QUICK];
		const void **	keys	= keys_q;
		CFIndex		n;

		n = CFDictionaryGetCount(statusPrivate->status_interfaces);
		if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
			keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
		}
		CFDictionaryGetKeysAndValues(statusPrivate->status_interfaces, keys, NULL);
		statusPrivate->interfaces = CFArrayCreate(NULL, keys, n, &kCFTypeArrayCallBacks);
		if (keys != keys_q) {
			CFAllocatorDeallocate(NULL, keys);
		}
	}

	return statusPrivate->interfaces;
}


CFDictionaryRef
SCBondStatusGetInterfaceStatus(SCBondStatusRef bondStatus, SCNetworkInterfaceRef interface)
{
	CFDictionaryRef		status		= NULL;
	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)bondStatus;

	if (!isA_SCBondStatus(bondStatus)) {
		return NULL;
	}

	if (interface == NULL) {
		// return status of the bond
		status = statusPrivate->status_bond;
	} else {
		// return status of the member interface
		status = CFDictionaryGetValue(statusPrivate->status_interfaces, interface);
	}

	return status;
}


SCBondStatusRef
SCBondInterfaceCopyStatus(SCBondInterfaceRef bond)
{
	int				bond_if_active;
	int				bond_if_status;
	CFIndex				i;
	struct if_bond_status_req	*ibsr_p		= NULL;
	char				if_name[IFNAMSIZ + 1];
	CFIndex				n;
	CFNumberRef			num;
	int				s;
	struct if_bond_status		*scan_p;
	SCBondStatusRef			status		= NULL;
	CFMutableDictionaryRef		status_bond;
	CFMutableDictionaryRef		status_interfaces;

	if (!isA_SCBondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}

	s = inet_dgram_socket();
	if (s == -1) {
		_SCErrorSet(errno);
		goto done;
	}

	_SC_cfstring_to_cstring(SCNetworkInterfaceGetBSDName(bond),
				if_name,
				sizeof(if_name),
				kCFStringEncodingASCII);
	if (siocgifmedia(s, if_name, &bond_if_status, &bond_if_active) == -1) {
		_SCErrorSet(errno);
		switch (errno) {
			case EBUSY :
			case ENXIO :
				break;
			default :
				SCLog(TRUE, LOG_ERR,
				      CFSTR("siocgifmedia(%s) failed: %s"),
				      if_name,
				      strerror(errno));
		}
		goto done;
	}
	ibsr_p = if_bond_status_req_copy(s, if_name);
	if (ibsr_p == NULL) {
		_SCErrorSet(errno);
		goto done;
	}

	status_bond = CFDictionaryCreateMutable(NULL,
						0,
						&kCFTypeDictionaryKeyCallBacks,
						&kCFTypeDictionaryValueCallBacks);

	status_interfaces = CFDictionaryCreateMutable(NULL,
						      0,
						      &kCFTypeDictionaryKeyCallBacks,
						      &kCFTypeDictionaryValueCallBacks);
	n = ibsr_p->ibsr_total;
	for (i = 0, scan_p = (struct if_bond_status *)ibsr_p->ibsr_buffer; i < n; i++, scan_p++) {
		int				collecting	= 0;
		int				distributing	= 0;
		SCNetworkInterfaceRef		interface;
		CFStringRef			interface_name;
		struct if_bond_partner_state *	ps;
		CFMutableDictionaryRef		status_interface;
		int				status_val;

		ps = &scan_p->ibs_partner_state;

		if (lacp_actor_partner_state_in_sync(scan_p->ibs_state)) {
			/* we're in-sync */
			status_val = kSCBondStatusOK;
			if (lacp_actor_partner_state_in_sync(ps->ibps_state)) {
				/* partner is also in-sync */
				if (lacp_actor_partner_state_collecting(scan_p->ibs_state)
				    && lacp_actor_partner_state_distributing(ps->ibps_state)) {
					/* we're able to collect (receive) frames */
					collecting = 1;
				}
				if (lacp_actor_partner_state_distributing(scan_p->ibs_state)
				    && lacp_actor_partner_state_collecting(ps->ibps_state)) {
					/* we're able to distribute (transmit) frames */
					distributing = 1;
				}
			}
		} else {
			int			active = 0;
			int			status = 0;
			static lacp_system	zeroes = { {0, 0, 0, 0, 0, 0}};

			if (siocgifmedia(s, scan_p->ibs_if_name, &status, &active) == -1) {
				switch (errno) {
					case EBUSY :
					case ENXIO :
						break;
					default :
						SCLog(TRUE, LOG_ERR,
						      CFSTR("siocgifmedia(%s) failed: %s"),
						      if_name,
						      strerror(errno));
						break;
				}
			}
			if (((status & IFM_AVALID) == 0) ||
			    ((status & IFM_ACTIVE) == 0) ||
			    ((active & IFM_FDX   ) == 0)) {
				/* link down or not full-duplex */
				status_val = kSCBondStatusLinkInvalid;
			} else if ((ps->ibps_system_priority == 0) &&
				   (bcmp(&zeroes, &ps->ibps_system, sizeof(zeroes)) == 0)) {
				/* no one on the other end of the link */
				status_val = kSCBondStatusNoPartner;
			} else if (active != bond_if_active) {
				/* the link speed was different */
				status_val = kSCBondStatusLinkInvalid;
			} else {
				/* partner is not in the active group */
				status_val = kSCBondStatusNotInActiveGroup;
			}
		}

		// interface
		bzero(&if_name, sizeof(if_name));
		bcopy(scan_p->ibs_if_name, if_name, IFNAMSIZ);
		interface_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
		interface = _SCNetworkInterfaceCreateWithBSDName(NULL, interface_name,
								 kIncludeNoVirtualInterfaces);
		CFRelease(interface_name);

		// interface status
		status_interface = CFDictionaryCreateMutable(NULL,
							     0,
							     &kCFTypeDictionaryKeyCallBacks,
							     &kCFTypeDictionaryValueCallBacks);
		num = CFNumberCreate(NULL, kCFNumberIntType, &status_val);
		CFDictionarySetValue(status_interface, kSCBondStatusDeviceAggregationStatus, num);
		CFRelease(num);
		num = CFNumberCreate(NULL, kCFNumberIntType, &collecting);
		CFDictionarySetValue(status_interface, kSCBondStatusDeviceCollecting, num);
		CFRelease(num);
		num = CFNumberCreate(NULL, kCFNumberIntType, &distributing);
		CFDictionarySetValue(status_interface, kSCBondStatusDeviceDistributing, num);
		CFRelease(num);

		CFDictionarySetValue(status_interfaces, interface, status_interface);
		CFRelease(interface);
		CFRelease(status_interface);
	}

	status = __SCBondStatusCreatePrivate(NULL, bond, status_bond, status_interfaces);
	CFRelease(status_bond);
	CFRelease(status_interfaces);

    done:

	if (s != -1) {
		close(s);
	}
	if (ibsr_p != NULL) {
		free(ibsr_p);
	}
	return (SCBondStatusRef)status;
}


#pragma mark -
#pragma mark SCBondInterface management


static Boolean
__bond_set_mode(int s, CFStringRef bond_if, CFNumberRef mode)
{
	struct if_bond_req	breq;
	struct ifreq		ifr;
	int					mode_num;

	mode_num = IF_BOND_MODE_LACP;
	if (mode != NULL) {
		CFNumberGetValue(mode, kCFNumberIntType, &mode_num);
	}

	// bond interface
	bzero(&ifr, sizeof(ifr));
	(void) _SC_cfstring_to_cstring(bond_if,
				       ifr.ifr_name,
				       sizeof(ifr.ifr_name),
				       kCFStringEncodingASCII);
	ifr.ifr_data = (caddr_t)&breq;
	bzero(&breq, sizeof(breq));
	breq.ibr_op = IF_BOND_OP_SET_MODE;
	breq.ibr_ibru.ibru_int_val = mode_num;
	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) {
		_SCErrorSet(errno);
		SCLog(TRUE, LOG_ERR,
		      CFSTR("could not set mode to %d on bond \"%@\": %s"),
		      mode,
		      bond_if,
		      strerror(errno));
		return FALSE;
	}

	return TRUE;
}

static Boolean
__bond_add_interface(int s, CFStringRef bond_if, CFStringRef interface_if)
{
	struct if_bond_req	breq;
	struct ifreq		ifr;

	// bond interface
	bzero(&ifr, sizeof(ifr));
	(void) _SC_cfstring_to_cstring(bond_if,
				       ifr.ifr_name,
				       sizeof(ifr.ifr_name),
				       kCFStringEncodingASCII);
	ifr.ifr_data = (caddr_t)&breq;

	// new bond member
	bzero(&breq, sizeof(breq));
	breq.ibr_op = IF_BOND_OP_ADD_INTERFACE;
	(void) _SC_cfstring_to_cstring(interface_if,
				       breq.ibr_ibru.ibru_if_name,
				       sizeof(breq.ibr_ibru.ibru_if_name),
				       kCFStringEncodingASCII);

	// add new bond member
	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) {
		_SCErrorSet(errno);
		SCLog(TRUE, LOG_ERR,
		      CFSTR("could not add interface \"%@\" to bond \"%@\": %s"),
		      interface_if,
		      bond_if,
		      strerror(errno));
		return FALSE;
	}

	return TRUE;
}


static Boolean
__bond_remove_interface(int s, CFStringRef bond_if, CFStringRef interface_if)
{
	struct if_bond_req	breq;
	struct ifreq		ifr;

	// bond interface
	bzero(&ifr, sizeof(ifr));
	(void) _SC_cfstring_to_cstring(bond_if,
				       ifr.ifr_name,
				       sizeof(ifr.ifr_name),
				       kCFStringEncodingASCII);
	ifr.ifr_data = (caddr_t)&breq;

	// bond member to remove
	bzero(&breq, sizeof(breq));
	breq.ibr_op = IF_BOND_OP_REMOVE_INTERFACE;
	(void) _SC_cfstring_to_cstring(interface_if,
				       breq.ibr_ibru.ibru_if_name,
				       sizeof(breq.ibr_ibru.ibru_if_name),
				       kCFStringEncodingASCII);

	// remove bond member
	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) {
		_SCErrorSet(errno);
		SCLog(TRUE, LOG_ERR,
		      CFSTR("could not remove interface \"%@\" from bond \"%@\": %s"),
		      interface_if,
		      bond_if,
		      strerror(errno));
		return FALSE;
	}

	return TRUE;
}


Boolean
_SCBondInterfaceUpdateConfiguration(SCPreferencesRef prefs)
{
	CFArrayRef			active		= NULL;
	CFArrayRef			config		= NULL;
	CFIndex				i;
	CFIndex				nActive;
	CFIndex				nConfig;
	Boolean				ok		= TRUE;
	int				s		= -1;

	if (prefs == NULL) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	/* configured Bonds */
	config  = SCBondInterfaceCopyAll(prefs);
	nConfig = CFArrayGetCount(config);

	/* active Bonds */
	active  = _SCBondInterfaceCopyActive();
	nActive = CFArrayGetCount(active);

	/*
	 * remove any no-longer-configured bond interfaces and
	 * any devices associated with a bond that are no longer
	 * associated with a bond.
	 */
	for (i = 0; i < nActive; i++) {
		SCBondInterfaceRef	a_bond;
		CFStringRef		a_bond_if;
		CFIndex			j;
		Boolean			found	= FALSE;

		a_bond    = CFArrayGetValueAtIndex(active, i);
		a_bond_if = SCNetworkInterfaceGetBSDName(a_bond);

		for (j = 0; j < nConfig; j++) {
			SCBondInterfaceRef	c_bond;
			CFStringRef		c_bond_if;

			c_bond    = CFArrayGetValueAtIndex(config, j);
			c_bond_if = SCNetworkInterfaceGetBSDName(c_bond);

			if (CFEqual(a_bond_if, c_bond_if)) {
				CFIndex		a;
				CFArrayRef	a_bond_interfaces;
				CFIndex		a_count;
				CFArrayRef	c_bond_interfaces;
				CFIndex		c_count;

				c_bond_interfaces = SCBondInterfaceGetMemberInterfaces(c_bond);
				c_count           = (c_bond_interfaces != NULL) ? CFArrayGetCount(c_bond_interfaces) : 0;

				a_bond_interfaces = SCBondInterfaceGetMemberInterfaces(a_bond);
				a_count           = (a_bond_interfaces != NULL) ? CFArrayGetCount(a_bond_interfaces) : 0;

				for (a = 0; a < a_count; a++) {
					SCNetworkInterfaceRef	a_interface;
					CFStringRef		a_interface_if;

					a_interface = CFArrayGetValueAtIndex(a_bond_interfaces, a);
					if ((c_count == 0) ||
					    !CFArrayContainsValue(c_bond_interfaces,
								  CFRangeMake(0, c_count),
								  a_interface)) {
						/*
						 * if this device is no longer part
						 * of the bond.
						 */
						if (s == -1) {
							s = inet_dgram_socket();
							if (s == -1) {
								_SCErrorSet(errno);
								ok = FALSE;
								goto done;
							}
						}

						a_interface_if = SCNetworkInterfaceGetBSDName(a_interface);
						if (!__bond_remove_interface(s, a_bond_if, a_interface_if)) {
							ok = FALSE;
						}
					}
				}

				found = TRUE;
				break;
			}
		}

		if (!found) {
			/*
			 * if this interface is no longer configured
			 */
			if (s == -1) {
				s = inet_dgram_socket();
				if (s == -1) {
					_SCErrorSet(errno);
					ok = FALSE;
					goto done;
				}
			}

			if (!__destroyInterface(s, a_bond_if)) {
				_SCErrorSet(errno);
				ok = FALSE;
			}
		}
	}

	/*
	 * add any newly-configured bond interfaces and add any
	 * devices that should now be associated with the bond.
	 */
	for (i = 0; i < nConfig; i++) {
		CFNumberRef		c_bond_mode;
		SCBondInterfaceRef	c_bond;
		CFArrayRef		c_bond_interfaces;
		CFStringRef		c_bond_if;
		CFIndex			c_count;
		Boolean			found		= FALSE;
		CFIndex			j;

		c_bond            = CFArrayGetValueAtIndex(config, i);
		c_bond_if         = SCNetworkInterfaceGetBSDName(c_bond);
		c_bond_interfaces = SCBondInterfaceGetMemberInterfaces(c_bond);
		c_bond_mode		  = SCBondInterfaceGetMode(c_bond);
		c_count           = (c_bond_interfaces != NULL) ? CFArrayGetCount(c_bond_interfaces) : 0;

		for (j = 0; j < nActive; j++) {
			SCBondInterfaceRef	a_bond;
			CFArrayRef		a_bond_interfaces;
			CFNumberRef		a_bond_mode;
			CFStringRef		a_bond_if;
			CFIndex			a_count;

			a_bond            = CFArrayGetValueAtIndex(active, j);
			a_bond_if         = SCNetworkInterfaceGetBSDName(a_bond);
			a_bond_interfaces = SCBondInterfaceGetMemberInterfaces(a_bond);
			a_bond_mode		  = SCBondInterfaceGetMode(a_bond);
			a_count           = (a_bond_interfaces != NULL) ? CFArrayGetCount(a_bond_interfaces) : 0;

			if (CFEqual(c_bond_if, a_bond_if)) {
				CFIndex	c;
				Boolean	if_list_change = FALSE;
				Boolean mode_change = FALSE;

				found = TRUE;

				if (!_SC_CFEqual(a_bond_mode, c_bond_mode)) {
					mode_change = TRUE;
				}

				if (!_SC_CFEqual(c_bond_interfaces, a_bond_interfaces)) {
					if_list_change = TRUE;
				}
				if (!mode_change && !if_list_change) {
					break;	// if no change
				}
				if (s == -1) {
					s = inet_dgram_socket();
					if (s == -1) {
						_SCErrorSet(errno);
						ok = FALSE;
						goto done;
					}
				}
				if (mode_change) {
					__bond_set_mode(s, a_bond_if, c_bond_mode);
				}
				if (!if_list_change) {
					break; // no if list changes
				}

				/*
				 * ensure that the first device of the bond matches, if
				 * not then we remove all current devices and add them
				 * back in the preferred order.
				 */
				if ((c_count > 0) &&
				    (a_count > 0) &&
				    !CFEqual(CFArrayGetValueAtIndex(c_bond_interfaces, 0),
					     CFArrayGetValueAtIndex(a_bond_interfaces, 0))) {
					CFIndex	a;

					for (a = 0; a < a_count; a++) {
						SCNetworkInterfaceRef	a_interface;
						CFStringRef		a_interface_if;

						a_interface = CFArrayGetValueAtIndex(a_bond_interfaces, a);
						if (!CFArrayContainsValue(c_bond_interfaces,
									 CFRangeMake(0, c_count),
									 a_interface)) {
							continue;	// if already removed
						}

						a_interface_if = SCNetworkInterfaceGetBSDName(a_interface);
						if (!__bond_remove_interface(s, a_bond_if, a_interface_if)) {
							ok = FALSE;
						}
					}

					a_count = 0;	// all active devices have been removed
				}

				/*
				 * add any devices which are not currently associated
				 * with the bond interface.
				 */
				for (c = 0; c < c_count; c++) {
					SCNetworkInterfaceRef		c_interface;
					SCNetworkInterfacePrivateRef	c_interfacePrivate;
					CFStringRef			c_interface_if;

					c_interface = CFArrayGetValueAtIndex(c_bond_interfaces, c);
					if ((a_count == 0) ||
					    !CFArrayContainsValue(a_bond_interfaces,
								  CFRangeMake(0, a_count),
								  c_interface)) {
						/*
						 * check if this member interface can be added to a bond.
						 */
						c_interfacePrivate = (SCNetworkInterfacePrivateRef)c_interface;
						if (!c_interfacePrivate->supportsBond) {
							// if member not supported
							continue;
						}

						/*
						 * if this member interface is not currently part of the bond.
						 */
						c_interface_if = SCNetworkInterfaceGetBSDName(c_interface);
						if (!__bond_add_interface(s, c_bond_if, c_interface_if)) {
							// if member could not be added
							ok = FALSE;
						}
					}
				}

				break;
			}
		}

		if (!found) {
			CFIndex	c;

			if (s == -1) {
				s = inet_dgram_socket();
				if (s == -1) {
					_SCErrorSet(errno);
					ok = FALSE;
					goto done;
				}
			}

			/*
			 * establish the new bond interface.
			 */
			if (!__createInterface(s, c_bond_if)) {
				_SCErrorSet(errno);
				ok = FALSE;
				continue;
			}

			/* set the mode */
			__bond_set_mode(s, c_bond_if, c_bond_mode);

			/*
			 * add the member interfaces
			 */
			for (c = 0; c < c_count; c++) {
				SCNetworkInterfaceRef		c_interface;
				SCNetworkInterfacePrivateRef	c_interfacePrivate;
				CFStringRef			c_interface_if;

				c_interface = CFArrayGetValueAtIndex(c_bond_interfaces, c);
				c_interfacePrivate = (SCNetworkInterfacePrivateRef)c_interface;
				if (!c_interfacePrivate->supportsBond) {
					// if member not supported
					continue;
				}

				c_interface_if = SCNetworkInterfaceGetBSDName(c_interface);
				if (!__bond_add_interface(s, c_bond_if, c_interface_if)) {
					// if member could not be added
					ok = FALSE;
				}
			}
		}

	}

    done :

	if (active != NULL)	CFRelease(active);
	if (config != NULL)	CFRelease(config);
	if (s != -1)		(void) close(s);

	return ok;
}


#pragma mark -
#pragma mark Deprecated SPIs (remove when no longer referenced)


/* ---------- Bond "device" ---------- */

Boolean
IsBondSupported(CFStringRef device)
{
	SCNetworkInterfaceRef		interface;
	SCNetworkInterfacePrivateRef	interfacePrivate;
	Boolean				isBond		= FALSE;

	interface = _SCNetworkInterfaceCreateWithBSDName(NULL, device,
							 kIncludeNoVirtualInterfaces);
	interfacePrivate = (SCNetworkInterfacePrivateRef)interface;
	isBond = interfacePrivate->supportsBond;
	CFRelease(interface);

	return isBond;
}

/* ---------- BondInterface ---------- */

typedef struct {

	/* base CFType information */
	CFRuntimeBase			cfBase;

	/* bond interface configuration */
	CFStringRef			ifname;		// e.g. bond0, bond1, ...
	CFArrayRef			devices;	// e.g. en0, en1, ...
	CFDictionaryRef			options;	// e.g. UserDefinedName

} BondInterfacePrivate, * BondInterfacePrivateRef;


static CFStringRef	__BondInterfaceCopyDescription	(CFTypeRef cf);
static void		__BondInterfaceDeallocate	(CFTypeRef cf);
static Boolean		__BondInterfaceEqual		(CFTypeRef cf1, CFTypeRef cf2);


static const CFRuntimeClass __BondInterfaceClass = {
	0,					// version
	"BondInterface",			// className
	NULL,					// init
	NULL,					// copy
	__BondInterfaceDeallocate,		// dealloc
	__BondInterfaceEqual,			// equal
	NULL,					// hash
	NULL,					// copyFormattingDesc
	__BondInterfaceCopyDescription		// copyDebugDesc
};


static CFTypeID		__kBondInterfaceTypeID	= _kCFRuntimeNotATypeID;


static pthread_once_t	bondInterface_init	= PTHREAD_ONCE_INIT;


static CFStringRef
__BondInterfaceCopyDescription(CFTypeRef cf)
{
	CFAllocatorRef		allocator	= CFGetAllocator(cf);
	CFMutableStringRef	result;
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)cf;

	result = CFStringCreateMutable(allocator, 0);
	CFStringAppendFormat(result, NULL, CFSTR("<BondInterface %p [%p]> {"), cf, allocator);
	CFStringAppendFormat(result, NULL, CFSTR("if = %@"), bondPrivate->ifname);
	if (bondPrivate->devices != NULL) {
		CFIndex	i;
		CFIndex	n;

		CFStringAppendFormat(result, NULL, CFSTR(", devices ="));

		n = CFArrayGetCount(bondPrivate->devices);
		for (i = 0; i < n; i++) {
			CFStringAppendFormat(result,
					     NULL,
					     CFSTR(" %@"),
					     CFArrayGetValueAtIndex(bondPrivate->devices, i));
		}
	}
	if (bondPrivate->options != NULL) {
		CFStringAppendFormat(result, NULL, CFSTR(", options = %@"), bondPrivate->options);
	}
	CFStringAppendFormat(result, NULL, CFSTR("}"));

	return result;
}


static void
__BondInterfaceDeallocate(CFTypeRef cf)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)cf;

	/* release resources */

	CFRelease(bondPrivate->ifname);
	if (bondPrivate->devices)	CFRelease(bondPrivate->devices);
	if (bondPrivate->options)	CFRelease(bondPrivate->options);

	return;
}


static Boolean
__BondInterfaceEquiv(CFTypeRef cf1, CFTypeRef cf2)
{
	BondInterfacePrivateRef	bond1	= (BondInterfacePrivateRef)cf1;
	BondInterfacePrivateRef	bond2	= (BondInterfacePrivateRef)cf2;

	if (bond1 == bond2)
		return TRUE;

	if (!CFEqual(bond1->ifname, bond2->ifname))
		return FALSE;	// if not the same interface

	if (!CFEqual(bond1->devices, bond2->devices))
		return FALSE;	// if not the same device

	return TRUE;
}


static Boolean
__BondInterfaceEqual(CFTypeRef cf1, CFTypeRef cf2)
{
	BondInterfacePrivateRef	bond1	= (BondInterfacePrivateRef)cf1;
	BondInterfacePrivateRef	bond2	= (BondInterfacePrivateRef)cf2;

	if (!__BondInterfaceEquiv(bond1, bond2))
		return FALSE;	// if not the same Bond interface/devices

	if (bond1->options != bond2->options) {
		// if the options may differ
		if ((bond1->options != NULL) && (bond2->options != NULL)) {
			// if both Bonds have options
			if (!CFEqual(bond1->options, bond2->options)) {
				// if the options are not equal
				return FALSE;
			}
		} else {
			// if only one Bond has options
			return FALSE;
		}
	}

	return TRUE;
}


static void
__BondInterfaceInitialize(void)
{
	__kBondInterfaceTypeID = _CFRuntimeRegisterClass(&__BondInterfaceClass);
	return;
}


static __inline__ CFTypeRef
isA_BondInterface(CFTypeRef obj)
{
	return (isA_CFType(obj, BondInterfaceGetTypeID()));
}


CFTypeID
BondInterfaceGetTypeID(void)
{
	pthread_once(&bondInterface_init, __BondInterfaceInitialize);	/* initialize runtime */
	return __kBondInterfaceTypeID;
}


static BondInterfaceRef
__BondInterfaceCreatePrivate(CFAllocatorRef	allocator,
			     CFStringRef	ifname)
{
	BondInterfacePrivateRef		bondPrivate;
	uint32_t			size;

	/* initialize runtime */
	pthread_once(&bondInterface_init, __BondInterfaceInitialize);

	/* allocate bond */
	size        = sizeof(BondInterfacePrivate) - sizeof(CFRuntimeBase);
	bondPrivate = (BondInterfacePrivateRef)_CFRuntimeCreateInstance(allocator,
									__kBondInterfaceTypeID,
									size,
									NULL);
	if (bondPrivate == NULL) {
		return NULL;
	}

	/* establish the bond */

	bondPrivate->ifname  = CFStringCreateCopy(allocator, ifname);
	bondPrivate->devices = NULL;
	bondPrivate->options = NULL;

	return (BondInterfaceRef)bondPrivate;
}


CFStringRef
BondInterfaceGetInterface(BondInterfaceRef bond)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)bond;
	CFStringRef		bond_if		= NULL;

	if (isA_BondInterface(bond)) {
		bond_if = bondPrivate->ifname;
	}

	return bond_if;
}


CFArrayRef
BondInterfaceGetDevices(BondInterfaceRef bond)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)bond;
	CFArrayRef		bond_devices	= NULL;

	if (isA_BondInterface(bond)) {
		bond_devices = bondPrivate->devices;
	}

	return bond_devices;
}


CFDictionaryRef
BondInterfaceGetOptions(BondInterfaceRef bond)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)bond;
	CFDictionaryRef		bond_options	= NULL;

	if (isA_BondInterface(bond)) {
		bond_options = bondPrivate->options;
	}

	return bond_options;
}


static void
BondInterfaceSetDevices(BondInterfaceRef bond, CFArrayRef newDevices)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)bond;

	if (isA_BondInterface(bond)) {
		CFAllocatorRef	allocator	= CFGetAllocator(bond);

		if (bondPrivate->devices != NULL)	CFRelease(bondPrivate->devices);
		if ((newDevices != NULL) && (CFArrayGetCount(newDevices) > 0)) {
			bondPrivate->devices = CFArrayCreateCopy(allocator, newDevices);
		} else {
			bondPrivate->devices = NULL;
		}
	}

	return;
}


static void
BondInterfaceSetOptions(BondInterfaceRef bond, CFDictionaryRef newOptions)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)bond;

	if (isA_BondInterface(bond)) {
		CFAllocatorRef	allocator	= CFGetAllocator(bond);

		if (bondPrivate->options)	CFRelease(bondPrivate->options);
		if (newOptions != NULL) {
			bondPrivate->options = CFDictionaryCreateCopy(allocator, newOptions);
		} else {
			bondPrivate->options = NULL;
		}
	}

	return;
}


/* ---------- BondPreferences ---------- */

typedef struct {

	/* base CFType information */
	CFRuntimeBase			cfBase;

	/* lock */
	pthread_mutex_t			lock;

	/* underlying preferences */
	SCPreferencesRef		prefs;

	/* base Bonds (before any commits) */
	CFArrayRef			bBase;

} BondPreferencesPrivate, * BondPreferencesPrivateRef;


static CFStringRef	__BondPreferencesCopyDescription	(CFTypeRef cf);
static void		__BondPreferencesDeallocate		(CFTypeRef cf);


static const CFRuntimeClass __BondPreferencesClass = {
	0,					// version
	"BondPreferences",			// className
	NULL,					// init
	NULL,					// copy
	__BondPreferencesDeallocate,		// dealloc
	NULL,					// equal
	NULL,					// hash
	NULL,					// copyFormattingDesc
	__BondPreferencesCopyDescription	// copyDebugDesc
};


static CFTypeID		__kBondPreferencesTypeID	= _kCFRuntimeNotATypeID;


static pthread_once_t	bondPreferences_init		= PTHREAD_ONCE_INIT;


static CFStringRef
__BondPreferencesCopyDescription(CFTypeRef cf)
{
	CFAllocatorRef			allocator	= CFGetAllocator(cf);
	CFIndex				i;
	CFArrayRef			keys;
	CFIndex				n;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)cf;
	CFMutableStringRef		result;

	result = CFStringCreateMutable(allocator, 0);
	CFStringAppendFormat(result, NULL, CFSTR("<BondPreferences %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;
}


static void
__BondPreferencesDeallocate(CFTypeRef cf)
{
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)cf;

	/* release resources */

	pthread_mutex_destroy(&prefsPrivate->lock);

	if (prefsPrivate->prefs)	CFRelease(prefsPrivate->prefs);
	if (prefsPrivate->bBase)	CFRelease(prefsPrivate->bBase);

	return;
}


static void
__BondPreferencesInitialize(void)
{
	__kBondPreferencesTypeID = _CFRuntimeRegisterClass(&__BondPreferencesClass);
	return;
}


static __inline__ CFTypeRef
isA_BondPreferences(CFTypeRef obj)
{
	return (isA_CFType(obj, BondPreferencesGetTypeID()));
}


CFArrayRef
_BondPreferencesCopyActiveInterfaces()
{
	CFArrayCallBacks	callbacks;
	struct ifaddrs		*ifap;
	struct ifaddrs		*ifp;
	int			s;
	CFMutableArrayRef	bonds	= NULL;

	if (getifaddrs(&ifap) == -1) {
		_SCErrorSet(errno);
		SCLog(TRUE, LOG_ERR, CFSTR("getifaddrs() failed: %s"), strerror(errno));
		return NULL;
	}

	s = inet_dgram_socket();
	if (s == -1) {
		_SCErrorSet(errno);
		goto done;
	}

	callbacks = kCFTypeArrayCallBacks;
	callbacks.equal = __BondInterfaceEquiv;
	bonds = CFArrayCreateMutable(NULL, 0, &callbacks);

	for (ifp = ifap; ifp != NULL; ifp = ifp->ifa_next) {
		BondInterfaceRef		bond;
		CFStringRef			bond_if;
		CFMutableArrayRef		devices	= NULL;
		struct if_bond_status_req	*ibsr_p;
		struct if_data			*if_data;

		if_data = (struct if_data *)ifp->ifa_data;
		if (if_data == NULL
		    || ifp->ifa_addr->sa_family != AF_LINK
		    || if_data->ifi_type != IFT_IEEE8023ADLAG) {
			continue;
		}
		ibsr_p = if_bond_status_req_copy(s, ifp->ifa_name);
		if (ibsr_p == NULL) {
			if (errno == EBUSY) {
				continue;
			}

			_SCErrorSet(errno);
			SCLog(TRUE, LOG_ERR,
			      CFSTR("if_bond_status_req_copy(%s) failed: %s"),
			      ifp->ifa_name,
			      strerror(errno));
			CFRelease(bonds);
			bonds = NULL;
			goto done;
		}
		if (ibsr_p->ibsr_total > 0) {
			int 			i;
			struct if_bond_status *	ibs_p;
			devices = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);

			// iterate over each member device
			ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer;
			for (i = 0; i < ibsr_p->ibsr_total; i++) {
				CFStringRef	device;
				char		if_name[IFNAMSIZ + 1];

				bzero(&if_name, sizeof(if_name));
				bcopy(ibs_p[i].ibs_if_name, if_name, IFNAMSIZ);
				device = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
				CFArrayAppendValue(devices, device);
				CFRelease(device);
			}
		}
		free(ibsr_p);
		bond_if = CFStringCreateWithCString(NULL, ifp->ifa_name, kCFStringEncodingASCII);
		bond    = __BondInterfaceCreatePrivate(NULL, bond_if);
		CFRelease(bond_if);

		if (devices != NULL) {
			BondInterfaceSetDevices(bond, devices);
			CFRelease(devices);
		}
		CFArrayAppendValue(bonds, bond);
		CFRelease(bond);
	}

    done :

	(void) close(s);
	freeifaddrs(ifap);
	return bonds;
}


static CFIndex
findBond(CFArrayRef bonds, CFStringRef interface)
{
	CFIndex	found	= kCFNotFound;
	CFIndex	i;
	CFIndex	n;

	n = isA_CFArray(bonds) ? CFArrayGetCount(bonds) : 0;
	for (i = 0; i < n; i++) {
		CFDictionaryRef	bond_dict;
		CFStringRef	bond_if;

		bond_dict = CFArrayGetValueAtIndex(bonds, i);
		if (!isA_CFDictionary(bond_dict)) {
			break;	// if the prefs are confused
		}

		bond_if = CFDictionaryGetValue(bond_dict, __kBondInterface_interface);
		if (!isA_CFString(bond_if)) {
			break;	// if the prefs are confused
		}

		if (!CFEqual(bond_if, interface)) {
			continue;	// if not a match
		}

		// if we have found a match
		found = i;
		break;
	}

	return found;
}


static void
setConfigurationChanged(BondPreferencesRef prefs)
{
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	/*
	 * to facilitate device configuration we will take
	 * a snapshot of the Bond 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->bBase == NULL) {
		prefsPrivate->bBase = BondPreferencesCopyInterfaces(prefs);
	}

	return;
}


CFTypeID
BondPreferencesGetTypeID(void)
{
	pthread_once(&bondPreferences_init, __BondPreferencesInitialize);	/* initialize runtime */
	return __kBondPreferencesTypeID;
}


BondPreferencesRef
BondPreferencesCreate(CFAllocatorRef allocator)
{
	CFBundleRef			bundle;
	CFStringRef			bundleID	= NULL;
	CFStringRef			name		= CFSTR("BondConfiguration");
	BondPreferencesPrivateRef	prefsPrivate;
	uint32_t			size;

	/* initialize runtime */
	pthread_once(&bondPreferences_init, __BondPreferencesInitialize);

	/* allocate preferences */
	size         = sizeof(BondPreferencesPrivate) - sizeof(CFRuntimeBase);
	prefsPrivate = (BondPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
									   __kBondPreferencesTypeID,
									   size,
									   NULL);
	if (prefsPrivate == NULL) {
		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, BOND_PREFERENCES_ID);
	CFRelease(name);

	prefsPrivate->bBase = NULL;

	return (BondPreferencesRef)prefsPrivate;
}


CFArrayRef
BondPreferencesCopyInterfaces(BondPreferencesRef prefs)
{
	CFAllocatorRef			allocator;
	CFArrayCallBacks		callbacks;
	CFIndex				i;
	CFIndex				n;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;
	CFMutableArrayRef		result;
	CFArrayRef			bonds;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}

	allocator = CFGetAllocator(prefs);
	callbacks = kCFTypeArrayCallBacks;
	callbacks.equal = __BondInterfaceEquiv;
	result = CFArrayCreateMutable(allocator, 0, &callbacks);

	bonds = SCPreferencesGetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS);
	if ((bonds != NULL) && !isA_CFArray(bonds)) {
		goto error;	// if the prefs are confused
	}

	n = (bonds != NULL) ? CFArrayGetCount(bonds) : 0;
	for (i = 0; i < n; i++) {
		BondInterfaceRef	bond;
		CFDictionaryRef		bond_dict;
		CFStringRef		bond_if;
		CFArrayRef		devices;
		CFDictionaryRef		options;

		bond_dict = CFArrayGetValueAtIndex(bonds, i);
		if (!isA_CFDictionary(bond_dict)) {
			goto error;	// if the prefs are confused
		}

		bond_if = CFDictionaryGetValue(bond_dict, __kBondInterface_interface);
		if (!isA_CFString(bond_if)) {
			goto error;	// if the prefs are confused
		}


		devices = CFDictionaryGetValue(bond_dict, __kBondInterface_devices);
		if ((devices != NULL) && !isA_CFArray(devices)) {
			goto error;	// if the prefs are confused
		}

		options = CFDictionaryGetValue(bond_dict, __kBondInterface_options);
		if ((options != NULL) && !isA_CFDictionary(options)) {
			goto error;	// if the prefs are confused
		}

		bond = __BondInterfaceCreatePrivate(allocator, bond_if);
		BondInterfaceSetDevices(bond, devices);
		BondInterfaceSetOptions(bond, options);
		CFArrayAppendValue(result, bond);
		CFRelease(bond);
	}

	return result;

    error :

	_SCErrorSet(kSCStatusFailed);
	CFRelease(result);
	return NULL;
}


BondInterfaceRef
BondPreferencesCreateInterface(BondPreferencesRef	prefs)
{
	CFArrayRef			active_bonds	= NULL;
	CFAllocatorRef			allocator;
	CFArrayRef			config_bonds;
	CFIndex				i;
	CFIndex				nActive;
	CFIndex				nConfig;
	BondInterfaceRef		newBond		= NULL;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return NULL;
	}

	pthread_mutex_lock(&prefsPrivate->lock);

	/* get "configured" Bonds (and check to ensure the device is available) */
	config_bonds = SCPreferencesGetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS);
	if ((config_bonds != NULL) && !isA_CFArray(config_bonds)) {
		// if the prefs are confused
		_SCErrorSet(kSCStatusFailed);
		goto done;
	}

	nConfig = (config_bonds != NULL) ? CFArrayGetCount(config_bonds) : 0;

	/* get "active" Bonds */
	active_bonds = _BondPreferencesCopyActiveInterfaces();
	nActive      = isA_CFArray(active_bonds) ? CFArrayGetCount(active_bonds) : 0;

	/* create a new bond using an unused interface name */
	allocator = CFGetAllocator(prefs);

	for (i = 0; newBond == NULL; i++) {
		CFIndex			j;
		CFMutableDictionaryRef	newDict;
		CFMutableArrayRef	newBonds;
		CFStringRef		bond_if;

		bond_if = CFStringCreateWithFormat(allocator, NULL, CFSTR("bond%d"), i);

		for (j = 0; j < nActive; j++) {
			CFStringRef		active_if;
			BondInterfaceRef	active_bond;

			active_bond = CFArrayGetValueAtIndex(active_bonds, j);
			active_if   = BondInterfaceGetInterface(active_bond);

			if (CFEqual(bond_if, active_if)) {
				goto next_if;	// if bond interface name not available
			}
		}

		for (j = 0; j < nConfig; j++) {
			CFDictionaryRef	config;
			CFStringRef	config_if;

			config = CFArrayGetValueAtIndex(config_bonds, j);
			if (!isA_CFDictionary(config)) {
				// if the prefs are confused
				_SCErrorSet(kSCStatusFailed);
				CFRelease(bond_if);
				goto done;
			}

			config_if = CFDictionaryGetValue(config, __kBondInterface_interface);
			if (!isA_CFString(config_if)) {
				// if the prefs are confused
				_SCErrorSet(kSCStatusFailed);
				CFRelease(bond_if);
				goto done;
			}

			if (CFEqual(bond_if, config_if)) {
				goto next_if;	// if bond interface name not available
			}
		}

		/* create the bond */

		newDict = CFDictionaryCreateMutable(allocator,
						    0,
						    &kCFTypeDictionaryKeyCallBacks,
						    &kCFTypeDictionaryValueCallBacks);
		CFDictionaryAddValue(newDict, __kBondInterface_interface, bond_if);

		/* create the accessor handle to be returned */

		newBond = __BondInterfaceCreatePrivate(allocator, bond_if);

		/* save in the prefs */

		if (nConfig > 0) {
			newBonds = CFArrayCreateMutableCopy(allocator, 0, config_bonds);
		} else {
			newBonds = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
		}
		CFArrayAppendValue(newBonds, newDict);
		CFRelease(newDict);

		/* yes, we're going to be changing the configuration */
		setConfigurationChanged(prefs);

		(void) SCPreferencesSetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS, newBonds);
		CFRelease(newBonds);

	    next_if :
		CFRelease(bond_if);
	}

    done :

	if (active_bonds != NULL)	CFRelease(active_bonds);

	pthread_mutex_unlock(&prefsPrivate->lock);

	return (BondInterfaceRef) newBond;
}


static Boolean
_BondPreferencesUpdate(BondPreferencesRef prefs, BondInterfaceRef bond)
{
	CFAllocatorRef			allocator;
	CFIndex				bond_index;
	CFArrayRef			devices;
	CFStringRef			interface;
	CFMutableDictionaryRef		newDict;
	CFMutableArrayRef		newBonds;
	CFDictionaryRef			options;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;
	CFArrayRef			bonds;

	bonds = SCPreferencesGetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS);
	if ((bonds != NULL) && !isA_CFArray(bonds)) {
		// if the prefs are confused
		_SCErrorSet(kSCStatusFailed);
		return FALSE;
	}

	interface = BondInterfaceGetInterface(bond);
	bond_index = findBond(bonds, interface);
	if (bond_index == kCFNotFound) {
		_SCErrorSet(kSCStatusNoKey);
		return FALSE;
	}

	/* create the bond dictionary */

	allocator = CFGetAllocator(prefs);
	newDict = CFDictionaryCreateMutable(allocator,
					    0,
					    &kCFTypeDictionaryKeyCallBacks,
					    &kCFTypeDictionaryValueCallBacks);
	CFDictionaryAddValue(newDict, __kBondInterface_interface, interface);

	devices = BondInterfaceGetDevices(bond);
	if (devices != NULL) {
		CFDictionaryAddValue(newDict, __kBondInterface_devices, devices);
	}

	options = BondInterfaceGetOptions(bond);
	if (options != NULL) {
		CFDictionaryAddValue(newDict, __kBondInterface_options, options);
	}

	/* yes, we're going to be changing the configuration */
	setConfigurationChanged(prefs);

	/* update the prefs */

	newBonds = CFArrayCreateMutableCopy(allocator, 0, bonds);
	CFArraySetValueAtIndex(newBonds, bond_index, newDict);
	CFRelease(newDict);
	(void) SCPreferencesSetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS, newBonds);
	CFRelease(newBonds);

	return TRUE;
}


Boolean
BondPreferencesAddDevice(BondPreferencesRef	prefs,
			 BondInterfaceRef	bond,
			 CFStringRef		device)
{
	CFArrayRef			config_bonds;
	CFIndex				i;
	CFIndex				nConfig;
	Boolean				ok		= TRUE;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!isA_BondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!isA_CFString(device)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!IsBondSupported(device)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	pthread_mutex_lock(&prefsPrivate->lock);

	/* get "configured" bonds */
	config_bonds = SCPreferencesGetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS);
	if ((config_bonds != NULL) && !isA_CFArray(config_bonds)) {
		_SCErrorSet(kSCStatusFailed);
		ok = FALSE;
		goto done;
	}

	nConfig = (config_bonds != NULL) ? CFArrayGetCount(config_bonds) : 0;

	/* check to ensure the requested device is available */
	for (i = 0; ok && (i < nConfig); i++) {
		CFDictionaryRef	config_bond;
		CFArrayRef	devices;

		config_bond = CFArrayGetValueAtIndex(config_bonds, i);
		if (!isA_CFDictionary(config_bond)) {
			ok = FALSE;	// if the prefs are confused
			break;
		}

		devices = CFDictionaryGetValue(config_bond, __kBondInterface_devices);
		if ((devices != NULL) && !isA_CFArray(devices)) {
			ok = FALSE;	// if the prefs are confused
			break;
		}

		if (devices == NULL) {
			continue;	// if no devices
		}

		ok = !CFArrayContainsValue(devices,
					   CFRangeMake(0, CFArrayGetCount(devices)),
					   device);
	}

	if (ok) {
		CFArrayRef		devices;
		CFMutableArrayRef	newDevices;

		devices = BondInterfaceGetDevices(bond);
		if (devices != NULL) {
			devices = CFArrayCreateCopy(NULL, devices);
			newDevices = CFArrayCreateMutableCopy(NULL, 0, devices);
		} else {
			newDevices = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
		}
		CFArrayAppendValue(newDevices, device);
		BondInterfaceSetDevices(bond, newDevices);
		CFRelease(newDevices);

		ok = _BondPreferencesUpdate(prefs, bond);
		if (!ok) {
			BondInterfaceSetDevices(bond, devices);
		}

		if (devices != NULL) {
			CFRelease(devices);
		}
	} else {
		_SCErrorSet(kSCStatusKeyExists);
	}

    done :

	pthread_mutex_unlock(&prefsPrivate->lock);

	return ok;
}


Boolean
BondPreferencesRemoveDevice(BondPreferencesRef	prefs,
			    BondInterfaceRef	bond,
			    CFStringRef		device)
{
	CFIndex				bond_index;
	CFArrayRef			devices;
	Boolean				ok		= FALSE;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!isA_BondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!isA_CFString(device)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	pthread_mutex_lock(&prefsPrivate->lock);

	devices = BondInterfaceGetDevices(bond);
	if (devices != NULL) {
		bond_index = CFArrayGetFirstIndexOfValue(devices,
							 CFRangeMake(0, CFArrayGetCount(devices)),
							 device);
		if (bond_index != kCFNotFound) {
			CFMutableArrayRef	newDevices;

			devices = CFArrayCreateCopy(NULL, devices);
			newDevices = CFArrayCreateMutableCopy(NULL, 0, devices);
			CFArrayRemoveValueAtIndex(newDevices, bond_index);
			BondInterfaceSetDevices(bond, newDevices);
			CFRelease(newDevices);

			ok = _BondPreferencesUpdate(prefs, bond);
			if (!ok) {
				BondInterfaceSetDevices(bond, devices);
			}

			CFRelease(devices);
		} else {
			_SCErrorSet(kSCStatusNoKey);
		}
	}

	pthread_mutex_unlock(&prefsPrivate->lock);

	return ok;
}


Boolean
BondPreferencesSetOptions(BondPreferencesRef prefs, BondInterfaceRef bond, CFDictionaryRef newOptions)
{
	Boolean				ok		= FALSE;
	CFDictionaryRef			options;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!isA_BondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	pthread_mutex_lock(&prefsPrivate->lock);

	options = BondInterfaceGetOptions(bond);
	if (options != NULL) {
		options = CFDictionaryCreateCopy(NULL, options);
	}

	BondInterfaceSetOptions(bond, newOptions);
	ok = _BondPreferencesUpdate(prefs, bond);
	if (!ok) {
		BondInterfaceSetOptions(bond, options);
	}

	if (options != NULL) {
		CFRelease(options);
	}

	pthread_mutex_unlock(&prefsPrivate->lock);

	return ok;
}


Boolean
BondPreferencesRemoveInterface(BondPreferencesRef	prefs,
			       BondInterfaceRef		bond)
{
	CFIndex				bond_index;
	Boolean				ok		= FALSE;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;
	CFArrayRef			bonds;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	if (!isA_BondInterface(bond)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	pthread_mutex_lock(&prefsPrivate->lock);

	bonds = SCPreferencesGetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS);
	if (!isA_CFArray(bonds)) {
		// if the prefs are confused
		_SCErrorSet(kSCStatusFailed);
		goto done;
	}

	bond_index = findBond(bonds, BondInterfaceGetInterface(bond));
	if (bond_index == kCFNotFound) {
		_SCErrorSet(kSCStatusNoKey);
		goto done;
	}

	/* yes, we're going to be changing the configuration */
	setConfigurationChanged(prefs);

	/* remove the bond */

	if (CFArrayGetCount(bonds) > 1) {
		CFAllocatorRef		allocator;
		CFMutableArrayRef	newBonds;

		allocator = CFGetAllocator(prefs);
		newBonds = CFArrayCreateMutableCopy(allocator, 0, bonds);
		CFArrayRemoveValueAtIndex(newBonds, bond_index);
		(void) SCPreferencesSetValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS, newBonds);
		CFRelease(newBonds);
	} else {
		(void) SCPreferencesRemoveValue(prefsPrivate->prefs, BOND_PREFERENCES_BONDS);
	}

	ok = TRUE;

    done :

	pthread_mutex_unlock(&prefsPrivate->lock);

	return ok;
}


Boolean
BondPreferencesCommitChanges(BondPreferencesRef	prefs)
{
	Boolean				ok		= FALSE;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	ok = SCPreferencesCommitChanges(prefsPrivate->prefs);
	if (!ok) {
		return ok;
	}

	if (prefsPrivate->bBase != NULL)  {
		CFRelease(prefsPrivate->bBase);
		prefsPrivate->bBase = NULL;
	}

	return TRUE;
}


Boolean
_BondPreferencesUpdateConfiguration(BondPreferencesRef prefs)
{
	return TRUE;
}


Boolean
BondPreferencesApplyChanges(BondPreferencesRef prefs)
{
	SCPreferencesRef		defaultPrefs;
	Boolean				ok		= FALSE;
	BondPreferencesPrivateRef	prefsPrivate	= (BondPreferencesPrivateRef)prefs;

	if (!isA_BondPreferences(prefs)) {
		_SCErrorSet(kSCStatusInvalidArgument);
		return FALSE;
	}

	pthread_mutex_lock(&prefsPrivate->lock);

	/* apply the preferences */
	ok = SCPreferencesApplyChanges(prefsPrivate->prefs);
	if (!ok) {
		goto done;
	}

	/* apply the Bond configuration */
	defaultPrefs = SCPreferencesCreate(NULL, CFSTR("BondPreferencesApplyChanges"), NULL);
	{
		/*
		 * Note: In an ideal world, we'd simply call SCPreferencesApplyChanges()
		 *       Unfortunately, it's possible that the caller (e.g NetworkCfgTool)
		 *       is holding the lock on the default prefs and since "Apply" attempts
		 *       to grab the lock we could end up in a deadlock situation.
		 */
#include "SCPreferencesInternal.h"
		SCPreferencesPrivateRef		defaultPrefsPrivate;

		defaultPrefsPrivate = (SCPreferencesPrivateRef)defaultPrefs;

		pthread_mutex_lock(&defaultPrefsPrivate->lock);
		if (defaultPrefsPrivate->session == NULL) {
			__SCPreferencesAddSession(defaultPrefs);
		}
		pthread_mutex_unlock(&defaultPrefsPrivate->lock);

		ok = SCDynamicStoreNotifyValue(defaultPrefsPrivate->session, defaultPrefsPrivate->sessionKeyApply);
	}
	CFRelease(defaultPrefs);
	if (!ok) {
		goto done;
	}

    done :

	pthread_mutex_unlock(&prefsPrivate->lock);

	return ok;
}


/* ---------- BondStatus ---------- */

typedef struct {

	/* base CFType information */
	CFRuntimeBase			cfBase;

	/* bond interface */
	BondInterfaceRef		bond;

	/* bond status */
	CFDictionaryRef			status_interface;	// interface status
	CFArrayRef			devices;		// per-device status
	CFDictionaryRef			status_devices;

} BondStatusPrivate, * BondStatusPrivateRef;


static CFStringRef	__BondStatusCopyDescription	(CFTypeRef cf);
static void		__BondStatusDeallocate		(CFTypeRef cf);
static Boolean		__BondStatusEqual		(CFTypeRef cf1, CFTypeRef cf2);


static const CFRuntimeClass __BondStatusClass = {
	0,				// version
	"BondStatus",			// className
	NULL,				// init
	NULL,				// copy
	__BondStatusDeallocate,		// dealloc
	__BondStatusEqual,		// equal
	NULL,				// hash
	NULL,				// copyFormattingDesc
	__BondStatusCopyDescription	// copyDebugDesc
};


static CFTypeID		__kBondStatusTypeID	= _kCFRuntimeNotATypeID;


static pthread_once_t	bondStatus_init_X	= PTHREAD_ONCE_INIT;


static CFStringRef
__BondStatusCopyDescription(CFTypeRef cf)
{
	CFAllocatorRef		allocator	= CFGetAllocator(cf);
	CFMutableStringRef	result;
	BondStatusPrivateRef	statusPrivate	= (BondStatusPrivateRef)cf;

	result = CFStringCreateMutable(allocator, 0);
	CFStringAppendFormat(result, NULL, CFSTR("<BondStatus %p [%p]> {"), cf, allocator);
	CFStringAppendFormat(result, NULL, CFSTR(" bond = %@"), statusPrivate->bond);
	CFStringAppendFormat(result, NULL, CFSTR(" interface = %@"), statusPrivate->status_interface);
	CFStringAppendFormat(result, NULL, CFSTR(" devices = %@"),   statusPrivate->status_devices);
	CFStringAppendFormat(result, NULL, CFSTR(" }"));

	return result;
}


static void
__BondStatusDeallocate(CFTypeRef cf)
{
	BondStatusPrivateRef	statusPrivate	= (BondStatusPrivateRef)cf;

	/* release resources */

	CFRelease(statusPrivate->bond);
	CFRelease(statusPrivate->status_interface);
	if (statusPrivate->devices != NULL) CFRelease(statusPrivate->devices);
	CFRelease(statusPrivate->status_devices);
	return;
}


static Boolean
__BondStatusEqual(CFTypeRef cf1, CFTypeRef cf2)
{
	BondStatusPrivateRef	status1	= (BondStatusPrivateRef)cf1;
	BondStatusPrivateRef	status2	= (BondStatusPrivateRef)cf2;

	if (status1 == status2)
		return TRUE;

	if (!CFEqual(status1->bond, status2->bond))
		return FALSE;	// if not the same bond

	if (!CFEqual(status1->status_interface, status2->status_interface))
		return FALSE;	// if not the same interface status

	if (!CFEqual(status1->status_devices, status2->status_devices))
		return FALSE;	// if not the same device status

	return TRUE;
}


static void
__BondStatusInitialize(void)
{
	__kBondStatusTypeID = _CFRuntimeRegisterClass(&__BondStatusClass);
	return;
}


static __inline__ CFTypeRef
isA_BondStatus(CFTypeRef obj)
{
	return (isA_CFType(obj, BondStatusGetTypeID()));
}


CFTypeID
BondStatusGetTypeID(void)
{
	pthread_once(&bondStatus_init_X, __BondStatusInitialize);	/* initialize runtime */
	return __kBondStatusTypeID;
}


static BondStatusRef
__BondStatusCreatePrivate(CFAllocatorRef	allocator,
			  BondInterfaceRef	bond,
			  CFDictionaryRef	status_interface,
			  CFDictionaryRef	status_devices)
{
	BondStatusPrivateRef	statusPrivate;
	uint32_t		size;

	/* initialize runtime */
	pthread_once(&bondStatus_init_X, __BondStatusInitialize);

	/* allocate bond */
	size          = sizeof(BondStatusPrivate) - sizeof(CFRuntimeBase);
	statusPrivate = (BondStatusPrivateRef)_CFRuntimeCreateInstance(allocator,
								       __kBondStatusTypeID,
								       size,
								       NULL);
	if (statusPrivate == NULL) {
		return NULL;
	}

	/* establish the bond status */

	statusPrivate->bond             = CFRetain(bond);
	statusPrivate->status_interface = CFDictionaryCreateCopy(allocator, status_interface);
	statusPrivate->devices          = NULL;
	statusPrivate->status_devices   = CFDictionaryCreateCopy(allocator, status_devices);

	return (BondStatusRef)statusPrivate;
}


BondStatusRef
BondInterfaceCopyStatus(BondInterfaceRef bond)
{
	BondInterfacePrivateRef	bondPrivate	= (BondInterfacePrivateRef)bond;
	int			bond_if_active;
	int			bond_if_status;
	CFIndex			i;
	struct if_bond_status_req *ibsr_p = NULL;
	char			if_name[IFNAMSIZ + 1];
	CFIndex			n;
	CFNumberRef		num;
	int			s;
	struct if_bond_status * scan_p;
	BondStatusRef		status		= NULL;
	CFMutableDictionaryRef	status_devices;
	CFMutableDictionaryRef	status_interface;

	if (!isA_BondInterface(bond)) {
		return NULL;
	}

	s = inet_dgram_socket();
	if (s == -1) {
		_SCErrorSet(errno);
		goto done;
	}
	_SC_cfstring_to_cstring(bondPrivate->ifname, if_name,
				sizeof(if_name), kCFStringEncodingASCII);
	(void)siocgifmedia(s, if_name, &bond_if_status, &bond_if_active);
	ibsr_p = if_bond_status_req_copy(s, if_name);
	if (ibsr_p == NULL) {
		_SCErrorSet(errno);
		goto done;
	}
	status_interface = CFDictionaryCreateMutable(NULL,
						     0,
						     &kCFTypeDictionaryKeyCallBacks,
						     &kCFTypeDictionaryValueCallBacks);

	status_devices = CFDictionaryCreateMutable(NULL,
						   0,
						   &kCFTypeDictionaryKeyCallBacks,
						   &kCFTypeDictionaryValueCallBacks);
	n = ibsr_p->ibsr_total;
	for (i = 0, scan_p = (struct if_bond_status *)ibsr_p->ibsr_buffer; i < n; i++, scan_p++) {
		CFStringRef			bond_if;
		int				collecting = 0;
		int				distributing = 0;
		struct if_bond_partner_state *	ps;
		CFMutableDictionaryRef		status_device;
		int				status_val;

		ps = &scan_p->ibs_partner_state;

		status_device = CFDictionaryCreateMutable(NULL,
							  0,
							  &kCFTypeDictionaryKeyCallBacks,
							  &kCFTypeDictionaryValueCallBacks);
		if (lacp_actor_partner_state_in_sync(scan_p->ibs_state)) {
			/* we're in-sync */
			status_val = kSCBondStatusOK;
			if (lacp_actor_partner_state_in_sync(ps->ibps_state)) {
				/* partner is also in-sync */
				if (lacp_actor_partner_state_collecting(scan_p->ibs_state)
				    && lacp_actor_partner_state_distributing(ps->ibps_state)) {
					/* we're able to collect (receive) frames */
					collecting = 1;
				}
				if (lacp_actor_partner_state_distributing(scan_p->ibs_state)
				    && lacp_actor_partner_state_collecting(ps->ibps_state)) {
					/* we're able to distribute (transmit) frames */
					distributing = 1;
				}
			}
		}
		else {
			int			active = 0;
			int			status = 0;
			static lacp_system	zeroes = {{0,0,0,0,0,0}};

			(void)siocgifmedia(s, scan_p->ibs_if_name, &status, &active);
			if ((status & IFM_AVALID) == 0 || (status & IFM_ACTIVE) == 0
			    || (active & IFM_FDX) == 0) {
				/* link down or not full-duplex */
				status_val = kSCBondStatusLinkInvalid;
			}
			else if (ps->ibps_system_priority == 0
				 && bcmp(&zeroes, &ps->ibps_system, sizeof(zeroes)) == 0) {
				/* no one on the other end of the link */
				status_val = kSCBondStatusNoPartner;
			}
			else if (active != bond_if_active) {
				/* the link speed was different */
				status_val = kSCBondStatusLinkInvalid;
			}
			else {
				/* partner is not in the active group */
				status_val = kSCBondStatusNotInActiveGroup;
			}
		}
		num = CFNumberCreate(NULL, kCFNumberIntType, &status_val);
		CFDictionarySetValue(status_device, kSCBondStatusDeviceAggregationStatus, num);
		CFRelease(num);
		num = CFNumberCreate(NULL, kCFNumberIntType, &collecting);
		CFDictionarySetValue(status_device, kSCBondStatusDeviceCollecting, num);
		CFRelease(num);
		num = CFNumberCreate(NULL, kCFNumberIntType, &distributing);
		CFDictionarySetValue(status_device, kSCBondStatusDeviceDistributing, num);
		CFRelease(num);
		bond_if = CFArrayGetValueAtIndex(bondPrivate->devices, i);
		CFDictionarySetValue(status_devices, bond_if, status_device);
		CFRelease(status_device);
	}

	status = __BondStatusCreatePrivate(NULL, bond, status_interface, status_devices);

	CFRelease(status_interface);
	CFRelease(status_devices);
 done:
	if (s != -1) {
		close(s);
	}
	if (ibsr_p != NULL) {
		free(ibsr_p);
	}
	return status;
}


CFArrayRef
BondStatusGetDevices(BondStatusRef bondStatus)
{
	BondStatusPrivateRef	statusPrivate	= (BondStatusPrivateRef)bondStatus;

	if (!isA_BondStatus(bondStatus)) {
		return NULL;
	}

	if (statusPrivate->devices == NULL) {
		const void *	keys_q[N_QUICK];
		const void **	keys	= keys_q;
		CFIndex		n;

		n = CFDictionaryGetCount(statusPrivate->status_devices);
		if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
			keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
		}
		CFDictionaryGetKeysAndValues(statusPrivate->status_devices, keys, NULL);
		statusPrivate->devices = CFArrayCreate(NULL, keys, n, &kCFTypeArrayCallBacks);
		if (keys != keys_q) {
			CFAllocatorDeallocate(NULL, keys);
		}
	}

	return statusPrivate->devices;
}


CFDictionaryRef
BondStatusGetInterfaceStatus(BondStatusRef bondStatus)
{
	BondStatusPrivateRef	statusPrivate	= (BondStatusPrivateRef)bondStatus;

	if (!isA_BondStatus(bondStatus)) {
		return NULL;
	}

	return statusPrivate->status_interface;
}


CFDictionaryRef
BondStatusGetDeviceStatus(BondStatusRef bondStatus, CFStringRef device)
{
	BondStatusPrivateRef	statusPrivate	= (BondStatusPrivateRef)bondStatus;

	if (!isA_BondStatus(bondStatus)) {
		return NULL;
	}

	return CFDictionaryGetValue(statusPrivate->status_devices, device);
}