1/*
2 * Copyright (c) 2004-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * November 28, 2005		Allan Nathanson <ajn@apple.com>
28 * - public API
29 *
30 * July 22, 2004		Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34
35#include <CoreFoundation/CoreFoundation.h>
36#include <CoreFoundation/CFRuntime.h>
37
38#include <SystemConfiguration/SystemConfiguration.h>
39#include "SCNetworkConfigurationInternal.h"
40#include "SCPreferencesInternal.h"
41#include <SystemConfiguration/SCValidation.h>
42#include <SystemConfiguration/SCPrivate.h>
43
44#include <ifaddrs.h>
45#include <pthread.h>
46#include <unistd.h>
47#include <sys/types.h>
48#include <sys/ioctl.h>
49#include <sys/socket.h>
50#include <sys/sysctl.h>
51#include <net/ethernet.h>
52#define	KERNEL_PRIVATE
53#include <net/if.h>
54#include <net/if_var.h>
55#undef	KERNEL_PRIVATE
56#include <net/if_bond_var.h>
57#include <net/if_types.h>
58#include <net/if_media.h>
59#include <net/route.h>
60
61/* ---------- Bond support ---------- */
62
63static int
64inet_dgram_socket()
65{
66	int	s;
67
68	s = socket(AF_INET, SOCK_DGRAM, 0);
69	if (s == -1) {
70		SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
71	}
72
73	return s;
74}
75
76static int
77siocgifmedia(int s, const char * ifname, int * status, int * active)
78{
79	struct ifmediareq	ifmr;
80
81	*status = 0;
82	*active = 0;
83	bzero(&ifmr, sizeof(ifmr));
84	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
85	if (ioctl(s, SIOCGIFMEDIA, &ifmr) == -1) {
86		return (-1);
87	}
88	if (ifmr.ifm_count != 0) {
89		*status = ifmr.ifm_status;
90		*active = ifmr.ifm_active;
91	}
92	return (0);
93}
94
95static struct if_bond_status_req *
96if_bond_status_req_copy(int s, const char * ifname)
97{
98	void *				buf = NULL;
99	struct if_bond_req		ibr;
100	struct if_bond_status_req *	ibsr_p;
101	struct ifreq			ifr;
102
103	bzero(&ifr, sizeof(ifr));
104	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
105	bzero((char *)&ibr, sizeof(ibr));
106	ibr.ibr_op = IF_BOND_OP_GET_STATUS;
107	ibsr_p = &ibr.ibr_ibru.ibru_status;
108	ibsr_p->ibsr_version = IF_BOND_STATUS_REQ_VERSION;
109	ifr.ifr_data = (caddr_t)&ibr;
110
111	/* how many of them are there? */
112	if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) == -1) {
113		goto failed;
114	}
115	buf = malloc(sizeof(struct if_bond_status) * ibsr_p->ibsr_total + sizeof(*ibsr_p));
116	if (buf == NULL) {
117		goto failed;
118	}
119	if (ibsr_p->ibsr_total == 0) {
120		goto done;
121	}
122	ibsr_p->ibsr_count = ibsr_p->ibsr_total;
123	ibsr_p->ibsr_buffer = buf + sizeof(*ibsr_p);
124
125	/* get the list */
126	if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) == -1) {
127		goto failed;
128	}
129 done:
130	(*(struct if_bond_status_req *)buf) = *ibsr_p;
131	return ((struct if_bond_status_req *)buf);
132
133 failed:
134	if (buf != NULL) {
135		free(buf);
136	}
137	return (NULL);
138}
139
140
141static void
142add_interface(CFMutableArrayRef *interfaces, CFStringRef if_name, SCPreferencesRef ni_prefs)
143{
144	SCNetworkInterfaceRef	interface = NULL;
145
146	if (*interfaces == NULL) {
147		*interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
148	}
149	if (ni_prefs != NULL) {
150		interface = __SCNetworkInterfaceCreateWithNIPreferencesUsingBSDName(NULL, ni_prefs, if_name);
151	}
152	if (interface == NULL) {
153		interface = _SCNetworkInterfaceCreateWithBSDName(NULL, if_name,
154								 kIncludeNoVirtualInterfaces);
155	}
156
157	if (interface != NULL) {
158		CFArrayAppendValue(*interfaces, interface);
159		CFRelease(interface);
160	}
161}
162
163
164static Boolean
165_SCBondInterfaceSetMode(SCBondInterfaceRef bond, CFNumberRef mode);
166
167
168typedef struct {
169	CFMutableArrayRef	bonds;
170	SCPreferencesRef	ni_prefs;
171	SCPreferencesRef	prefs;
172} addContext, *addContextRef;
173
174
175static void
176add_configured_interface(const void *key, const void *value, void *context)
177{
178	SCBondInterfaceRef		bond;
179	CFStringRef			bond_if		= (CFStringRef)key;
180	CFDictionaryRef			bond_info	= (CFDictionaryRef)value;
181	CFDictionaryRef			bond_options;
182	CFIndex				i;
183	CFArrayRef			interfaces;
184	SCNetworkInterfacePrivateRef	interfacePrivate;
185	CFMutableArrayRef		members		= NULL;
186	CFNumberRef			mode;
187	addContextRef			myContext	= (addContextRef)context;
188	CFStringRef			name;
189	CFIndex				n;
190
191	// create the bond interface
192	bond = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(NULL, bond_if);
193
194	// add member interfaces
195	interfaces = CFDictionaryGetValue(bond_info, kSCPropVirtualNetworkInterfacesBondInterfaces);
196	n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
197	for (i = 0; i < n; i++) {
198		CFStringRef	member;
199
200		member = CFArrayGetValueAtIndex(interfaces, i);
201		if (isA_CFString(member)) {
202			add_interface(&members, member, myContext->ni_prefs);
203		}
204	}
205	if (members != NULL) {
206		__SCBondInterfaceSetMemberInterfaces(bond, members);
207		CFRelease(members);
208	}
209
210	// set display name
211	name = CFDictionaryGetValue(bond_info, kSCPropUserDefinedName);
212	if (isA_CFString(name)) {
213		SCBondInterfaceSetLocalizedDisplayName(bond, name);
214	}
215
216	// set options
217	bond_options = CFDictionaryGetValue(bond_info, kSCPropVirtualNetworkInterfacesBondOptions);
218	if (isA_CFDictionary(bond_options)) {
219		SCBondInterfaceSetOptions(bond, bond_options);
220	}
221
222	// set the mode
223	mode = CFDictionaryGetValue(bond_info, kSCPropVirtualNetworkInterfacesBondMode);
224	_SCBondInterfaceSetMode(bond, isA_CFNumber(mode));
225
226	// estabish link to the stored configuration
227	interfacePrivate = (SCNetworkInterfacePrivateRef)bond;
228	interfacePrivate->prefs = CFRetain(myContext->prefs);
229
230	CFArrayAppendValue(myContext->bonds, bond);
231	CFRelease(bond);
232
233	return;
234}
235
236
237
238#pragma mark -
239#pragma mark SCBondInterface APIs
240
241
242static __inline__ void
243my_CFDictionaryApplyFunction(CFDictionaryRef			theDict,
244			     CFDictionaryApplierFunction	applier,
245			     void				*context)
246{
247	CFAllocatorRef	myAllocator;
248	CFDictionaryRef	myDict;
249
250	myAllocator = CFGetAllocator(theDict);
251	myDict      = CFDictionaryCreateCopy(myAllocator, theDict);
252	CFDictionaryApplyFunction(myDict, applier, context);
253	CFRelease(myDict);
254	return;
255}
256
257
258CFArrayRef
259SCBondInterfaceCopyAll(SCPreferencesRef prefs)
260{
261	addContext		context;
262	CFDictionaryRef		dict;
263	SCPreferencesRef	ni_prefs;
264	CFStringRef		path;
265
266	if ((prefs == NULL) ||
267	    (__SCPreferencesUsingDefaultPrefs(prefs) == TRUE)) {
268		ni_prefs = NULL;
269	}
270	else {
271		ni_prefs = __SCPreferencesCreateNIPrefsFromPrefs(prefs);
272	}
273
274	context.bonds = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
275	context.prefs = prefs;
276	context.ni_prefs = ni_prefs;
277
278	path = CFStringCreateWithFormat(NULL,
279					NULL,
280					CFSTR("/%@/%@"),
281					kSCPrefVirtualNetworkInterfaces,
282					kSCNetworkInterfaceTypeBond);
283	dict = SCPreferencesPathGetValue(prefs, path);
284	CFRelease(path);
285	if (isA_CFDictionary(dict)) {
286		my_CFDictionaryApplyFunction(dict, add_configured_interface, &context);
287	}
288
289	if (ni_prefs != NULL) {
290		CFRelease(ni_prefs);
291	}
292	return context.bonds;
293}
294
295
296__private_extern__ void
297__SCBondInterfaceListCollectMembers(CFArrayRef interfaces, CFMutableSetRef set)
298{
299	CFIndex	i;
300	CFIndex	n;
301
302	n = CFArrayGetCount(interfaces);
303	for (i = 0; i < n; i++) {
304		SCBondInterfaceRef	bondInterface;
305		CFArrayRef		members;
306
307		bondInterface = CFArrayGetValueAtIndex(interfaces, i);
308		members = SCBondInterfaceGetMemberInterfaces(bondInterface);
309		if (members != NULL) {
310			CFIndex	j;
311			CFIndex	n_members;
312
313			// exclude the member interfaces of this bond
314			n_members = CFArrayGetCount(members);
315			for (j = 0; j < n_members; j++) {
316				SCNetworkInterfaceRef	member;
317
318				member = CFArrayGetValueAtIndex(members, j);
319				CFSetAddValue(set, member);
320			}
321		}
322
323	}
324	return;
325}
326
327
328CFArrayRef /* of SCNetworkInterfaceRef's */
329SCBondInterfaceCopyAvailableMemberInterfaces(SCPreferencesRef prefs)
330{
331	CFMutableArrayRef	available;
332	CFMutableSetRef		excluded;
333	CFArrayRef		interfaces;
334
335	available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
336	excluded  = CFSetCreateMutable  (NULL, 0, &kCFTypeSetCallBacks);
337
338	// exclude Bond [member] interfaces
339	interfaces = SCBondInterfaceCopyAll(prefs);
340	if (interfaces != NULL) {
341		__SCBondInterfaceListCollectMembers(interfaces, excluded);
342		CFRelease(interfaces);
343	}
344
345	// exclude Bridge [member] interfaces
346	interfaces = SCBridgeInterfaceCopyAll(prefs);
347	if (interfaces != NULL) {
348		__SCBridgeInterfaceListCollectMembers(interfaces, excluded);
349		CFRelease(interfaces);
350	}
351
352	// exclude VLAN [physical] interfaces
353	interfaces = SCVLANInterfaceCopyAll(prefs);
354	if (interfaces != NULL) {
355		CFIndex	i;
356		CFIndex	n;
357
358		n = CFArrayGetCount(interfaces);
359		for (i = 0; i < n; i++) {
360			SCVLANInterfaceRef	vlanInterface;
361			SCNetworkInterfaceRef	physical;
362
363			// exclude the physical interface of this VLAN
364			vlanInterface = CFArrayGetValueAtIndex(interfaces, i);
365			physical = SCVLANInterfaceGetPhysicalInterface(vlanInterface);
366			CFSetAddValue(excluded, physical);
367		}
368		CFRelease(interfaces);
369	}
370
371	// identify available interfaces
372	interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
373	if (interfaces != NULL) {
374		CFIndex	i;
375		CFIndex	n;
376
377		n = CFArrayGetCount(interfaces);
378		for (i = 0; i < n; i++) {
379			SCNetworkInterfaceRef		interface;
380			SCNetworkInterfacePrivateRef	interfacePrivate;
381
382			interface = CFArrayGetValueAtIndex(interfaces, i);
383			interfacePrivate = (SCNetworkInterfacePrivateRef)interface;
384
385			if (!interfacePrivate->supportsBond) {
386				// if this interface is not available
387				continue;
388			}
389
390			if (CFSetContainsValue(excluded, interface)) {
391				// if excluded
392				continue;
393			}
394
395			CFArrayAppendValue(available, interface);
396		}
397		CFRelease(interfaces);
398	}
399
400	CFRelease(excluded);
401
402	return available;
403}
404
405
406CFArrayRef
407_SCBondInterfaceCopyActive(void)
408{
409	struct ifaddrs		*ifap;
410	struct ifaddrs		*ifp;
411	int			s;
412	CFMutableArrayRef	bonds	= NULL;
413
414	if (getifaddrs(&ifap) == -1) {
415		_SCErrorSet(errno);
416		SCLog(TRUE, LOG_ERR, CFSTR("getifaddrs() failed: %s"), strerror(errno));
417		return NULL;
418	}
419
420	s = inet_dgram_socket();
421	if (s == -1) {
422		_SCErrorSet(errno);
423		goto done;
424	}
425
426	bonds = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
427
428	for (ifp = ifap; ifp != NULL; ifp = ifp->ifa_next) {
429		SCBondInterfaceRef		bond;
430		CFStringRef			bond_if;
431		struct if_bond_status_req	*ibsr_p;
432		struct if_data			*if_data;
433		int				int_val;
434		CFNumberRef			mode;
435		CFMutableArrayRef		members		= NULL;
436
437		if_data = (struct if_data *)ifp->ifa_data;
438		if (if_data == NULL
439		    || ifp->ifa_addr->sa_family != AF_LINK
440		    || if_data->ifi_type != IFT_IEEE8023ADLAG) {
441			continue;
442		}
443
444		ibsr_p = if_bond_status_req_copy(s, ifp->ifa_name);
445		if (ibsr_p == NULL) {
446			if (errno == EBUSY) {
447				continue;
448			}
449			_SCErrorSet(errno);
450			SCLog(TRUE, LOG_ERR,
451			      CFSTR("if_bond_status_req_copy(%s) failed: %s"),
452			      ifp->ifa_name,
453			      strerror(errno));
454			CFRelease(bonds);
455			bonds = NULL;
456			goto done;
457		}
458
459		// create the bond interface
460		bond_if = CFStringCreateWithCString(NULL, ifp->ifa_name, kCFStringEncodingASCII);
461		bond    = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(NULL, bond_if);
462		CFRelease(bond_if);
463
464		// set the mode
465		int_val = ibsr_p->ibsr_mode;
466		mode = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
467		assert(mode != NULL);
468		_SCBondInterfaceSetMode(bond, mode);
469		CFRelease(mode);
470
471		// add member interfaces
472		if (ibsr_p->ibsr_total > 0) {
473			int 			i;
474			struct if_bond_status *	ibs_p;
475
476			// iterate over each member interface
477			ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer;
478			for (i = 0; i < ibsr_p->ibsr_total; i++) {
479				CFStringRef	member;
480
481				member = CFStringCreateWithCString(NULL, ibs_p[i].ibs_if_name, kCFStringEncodingASCII);
482				add_interface(&members, member, NULL);
483				CFRelease(member);
484			}
485		}
486		free(ibsr_p);
487
488		if (members != NULL) {
489			__SCBondInterfaceSetMemberInterfaces(bond, members);
490			CFRelease(members);
491		}
492
493		// add bond
494		CFArrayAppendValue(bonds, bond);
495		CFRelease(bond);
496	}
497
498    done :
499
500	if (s != -1) {
501		(void) close(s);
502	}
503	freeifaddrs(ifap);
504	return bonds;
505}
506
507
508SCBondInterfaceRef
509SCBondInterfaceCreate(SCPreferencesRef prefs)
510{
511	CFAllocatorRef		allocator;
512	SCBondInterfaceRef	bond		= NULL;
513	CFIndex			i;
514
515	if (prefs == NULL) {
516		_SCErrorSet(kSCStatusInvalidArgument);
517		return NULL;
518	}
519
520	allocator = CFGetAllocator(prefs);
521
522	// create a new bond using an unused interface name
523	for (i = 0; bond == NULL; i++) {
524		CFDictionaryRef			dict;
525		CFStringRef			bond_if;
526		SCNetworkInterfacePrivateRef	interfacePrivate;
527		CFMutableDictionaryRef		newDict;
528		CFArrayRef			newInterfaces;
529		Boolean				ok;
530		CFStringRef			path;
531
532		bond_if = CFStringCreateWithFormat(allocator, NULL, CFSTR("bond%ld"), i);
533		path    = CFStringCreateWithFormat(allocator,
534						   NULL,
535						   CFSTR("/%@/%@/%@"),
536						   kSCPrefVirtualNetworkInterfaces,
537						   kSCNetworkInterfaceTypeBond,
538						   bond_if);
539		dict = SCPreferencesPathGetValue(prefs, path);
540		if (dict != NULL) {
541			// if bond interface name not available
542			CFRelease(path);
543			CFRelease(bond_if);
544			continue;
545		}
546
547		// add the bond to the stored preferences
548		newDict = CFDictionaryCreateMutable(allocator,
549						    0,
550						    &kCFTypeDictionaryKeyCallBacks,
551						    &kCFTypeDictionaryValueCallBacks);
552		newInterfaces = CFArrayCreate(allocator, NULL, 0, &kCFTypeArrayCallBacks);
553		CFDictionaryAddValue(newDict, kSCPropVirtualNetworkInterfacesBondInterfaces, newInterfaces);
554		CFRelease(newInterfaces);
555		ok = SCPreferencesPathSetValue(prefs, path, newDict);
556		CFRelease(newDict);
557		CFRelease(path);
558		if (!ok) {
559			// if the bond could not be saved
560			CFRelease(bond_if);
561			break;
562		}
563
564		// create the SCBondInterfaceRef
565		bond = (SCBondInterfaceRef)_SCBondInterfaceCreatePrivate(allocator, bond_if);
566		CFRelease(bond_if);
567
568		// estabish link to the stored configuration
569		interfacePrivate = (SCNetworkInterfacePrivateRef)bond;
570		interfacePrivate->prefs = CFRetain(prefs);
571	}
572
573	return bond;
574}
575
576
577Boolean
578SCBondInterfaceRemove(SCBondInterfaceRef bond)
579{
580	CFStringRef			bond_if;
581	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
582	Boolean				ok;
583	CFStringRef			path;
584
585	if (!isA_SCBondInterface(bond)) {
586		_SCErrorSet(kSCStatusInvalidArgument);
587		return FALSE;
588	}
589
590	if (interfacePrivate->prefs == NULL) {
591		_SCErrorSet(kSCStatusInvalidArgument);
592		return FALSE;
593	}
594
595	bond_if = SCNetworkInterfaceGetBSDName(bond);
596	path    = CFStringCreateWithFormat(NULL,
597					   NULL,
598					   CFSTR("/%@/%@/%@"),
599					   kSCPrefVirtualNetworkInterfaces,
600					   kSCNetworkInterfaceTypeBond,
601					   bond_if);
602	ok = SCPreferencesPathRemoveValue(interfacePrivate->prefs, path);
603	CFRelease(path);
604
605	return ok;
606}
607
608
609CFArrayRef
610SCBondInterfaceGetMemberInterfaces(SCBondInterfaceRef bond)
611{
612	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
613
614	if (!isA_SCBondInterface(bond)) {
615		_SCErrorSet(kSCStatusInvalidArgument);
616		return NULL;
617	}
618
619	return interfacePrivate->bond.interfaces;
620}
621
622
623CFDictionaryRef
624SCBondInterfaceGetOptions(SCBondInterfaceRef bond)
625{
626	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
627
628	if (!isA_SCBondInterface(bond)) {
629		_SCErrorSet(kSCStatusInvalidArgument);
630		return NULL;
631	}
632
633	return interfacePrivate->bond.options;
634}
635
636
637__private_extern__
638Boolean
639__SCBondInterfaceSetMemberInterfaces(SCBondInterfaceRef bond, CFArrayRef members)
640{
641	CFIndex				i;
642	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
643	CFIndex				n;
644	CFMutableArrayRef		newMembers;
645	Boolean				ok			= TRUE;
646
647	n = (members != NULL) ? CFArrayGetCount(members) : 0;
648
649	// set member interfaces in the stored preferences
650	if (interfacePrivate->prefs != NULL) {
651		CFDictionaryRef		dict;
652		CFMutableDictionaryRef	newDict;
653		CFStringRef		path;
654
655		path = CFStringCreateWithFormat(NULL,
656						NULL,
657						CFSTR("/%@/%@/%@"),
658						kSCPrefVirtualNetworkInterfaces,
659						kSCNetworkInterfaceTypeBond,
660						interfacePrivate->entity_device);
661		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
662		if (!isA_CFDictionary(dict)) {
663			// if the prefs are confused
664			CFRelease(path);
665			_SCErrorSet(kSCStatusFailed);
666			return FALSE;
667		}
668
669		newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
670		for (i = 0; i < n; i++) {
671			SCNetworkInterfaceRef	interface;
672			CFStringRef		memberName;
673
674			interface = CFArrayGetValueAtIndex(members, i);
675			memberName = SCNetworkInterfaceGetBSDName(interface);
676			CFArrayAppendValue(newMembers, memberName);
677		}
678
679		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
680		CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesBondInterfaces, newMembers);
681		CFRelease(newMembers);
682		if (!CFEqual(dict, newDict)) {
683			ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
684		}
685		CFRelease(newDict);
686		CFRelease(path);
687	}
688
689	if (ok) {
690		newMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
691		for (i = 0; i < n; i++) {
692			SCNetworkInterfaceRef		member;
693			SCNetworkInterfacePrivateRef	newMember;
694
695			member = CFArrayGetValueAtIndex(members, i);
696			newMember = __SCNetworkInterfaceCreateCopy(NULL,
697								   member,
698								   interfacePrivate->prefs,
699								   interfacePrivate->serviceID);
700			CFArrayAppendValue(newMembers, newMember);
701			CFRelease(newMember);
702		}
703		CFRelease(interfacePrivate->bond.interfaces);
704		interfacePrivate->bond.interfaces = newMembers;
705	}
706
707	return ok;
708}
709
710
711Boolean
712SCBondInterfaceSetMemberInterfaces(SCBondInterfaceRef bond, CFArrayRef members)
713{
714	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
715	Boolean				ok;
716	int				sc_status		= kSCStatusOK;
717
718	if (!isA_SCBondInterface(bond)) {
719		_SCErrorSet(kSCStatusInvalidArgument);
720		return FALSE;
721	}
722
723	if ((members != NULL) && !isA_CFArray(members)) {
724		_SCErrorSet(kSCStatusInvalidArgument);
725		return FALSE;
726	}
727
728	if (interfacePrivate->prefs != NULL) {
729		CFArrayRef	available;
730		CFArrayRef	current;
731		CFIndex		i;
732		CFIndex		n_available;
733		CFIndex		n_current;
734		CFIndex		n_members;
735		CFArrayRef	services	= NULL;
736
737		current     = SCBondInterfaceGetMemberInterfaces(bond);
738		n_current   = (current != NULL) ? CFArrayGetCount(current) : 0;
739
740		available   = SCBondInterfaceCopyAvailableMemberInterfaces(interfacePrivate->prefs);
741		n_available = (available != NULL) ? CFArrayGetCount(available) : 0;
742
743		n_members = (members != NULL) ? CFArrayGetCount(members) : 0;
744		for (i = 0; i < n_members; i++) {
745			SCNetworkInterfaceRef	member;
746
747			member = CFArrayGetValueAtIndex(members, i);
748
749			if ((current != NULL) &&
750			    CFArrayContainsValue(current, CFRangeMake(0, n_current), member)) {
751				// current members are allowed
752				continue;
753			}
754
755			if ((available != NULL) &&
756			    CFArrayContainsValue(available, CFRangeMake(0, n_available), member)) {
757				// available members are allowed but cannot be associated
758				// with any other network services.
759
760				if (services == NULL) {
761					services = __SCNetworkServiceCopyAllEnabled(interfacePrivate->prefs);
762				}
763				if ((services != NULL) &&
764				    __SCNetworkServiceExistsForInterface(services, member)) {
765					sc_status = kSCStatusKeyExists;
766					break;
767				}
768
769				// if available
770				continue;
771			}
772
773			// if member not allowed
774			sc_status = kSCStatusInvalidArgument;
775			break;
776		}
777
778		if (available != NULL) CFRelease(available);
779		if (services != NULL) CFRelease(services);
780	}
781
782	if (sc_status != kSCStatusOK) {
783		_SCErrorSet(sc_status);
784		return FALSE;
785	}
786
787	ok = __SCBondInterfaceSetMemberInterfaces(bond, members);
788	return ok;
789}
790
791
792Boolean
793SCBondInterfaceSetLocalizedDisplayName(SCBondInterfaceRef bond, CFStringRef newName)
794{
795	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
796	Boolean				ok			= TRUE;
797
798	if (!isA_SCBondInterface(bond)) {
799		_SCErrorSet(kSCStatusInvalidArgument);
800		return FALSE;
801	}
802
803	if ((newName != NULL) && !isA_CFString(newName)) {
804		_SCErrorSet(kSCStatusInvalidArgument);
805		return FALSE;
806	}
807
808	// set name in the stored preferences
809	if (interfacePrivate->prefs != NULL) {
810		CFDictionaryRef		dict;
811		CFMutableDictionaryRef	newDict;
812		CFStringRef		path;
813
814		path = CFStringCreateWithFormat(NULL,
815						NULL,
816						CFSTR("/%@/%@/%@"),
817						kSCPrefVirtualNetworkInterfaces,
818						kSCNetworkInterfaceTypeBond,
819						interfacePrivate->entity_device);
820		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
821		if (!isA_CFDictionary(dict)) {
822			// if the prefs are confused
823			CFRelease(path);
824			_SCErrorSet(kSCStatusFailed);
825			return FALSE;
826		}
827
828		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
829		if (newName != NULL) {
830			CFDictionarySetValue(newDict, kSCPropUserDefinedName, newName);
831		} else {
832			CFDictionaryRemoveValue(newDict, kSCPropUserDefinedName);
833		}
834		if (!CFEqual(dict, newDict)) {
835			ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
836		}
837		CFRelease(newDict);
838		CFRelease(path);
839	}
840
841	// set name in the SCBondInterfaceRef
842	if (ok) {
843		if (interfacePrivate->localized_name != NULL) {
844			CFRelease(interfacePrivate->localized_name);
845			interfacePrivate->localized_name = NULL;
846		}
847		if (newName != NULL) {
848			interfacePrivate->localized_name = CFStringCreateCopy(NULL, newName);
849		}
850	}
851
852	return ok;
853}
854
855
856Boolean
857SCBondInterfaceSetOptions(SCBondInterfaceRef bond, CFDictionaryRef newOptions)
858{
859	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
860	Boolean				ok			= TRUE;
861
862	if (!isA_SCBondInterface(bond)) {
863		_SCErrorSet(kSCStatusInvalidArgument);
864		return FALSE;
865	}
866
867	if ((newOptions != NULL) && !isA_CFDictionary(newOptions)) {
868		_SCErrorSet(kSCStatusInvalidArgument);
869		return FALSE;
870	}
871
872	// set options in the stored preferences
873	if (interfacePrivate->prefs != NULL) {
874		CFDictionaryRef		dict;
875		CFMutableDictionaryRef	newDict;
876		CFStringRef		path;
877
878		path = CFStringCreateWithFormat(NULL,
879						NULL,
880						CFSTR("/%@/%@/%@"),
881						kSCPrefVirtualNetworkInterfaces,
882						kSCNetworkInterfaceTypeBond,
883						interfacePrivate->entity_device);
884		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
885		if (!isA_CFDictionary(dict)) {
886			// if the prefs are confused
887			CFRelease(path);
888			_SCErrorSet(kSCStatusFailed);
889			return FALSE;
890		}
891
892		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
893		if (newOptions != NULL) {
894			CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesBondOptions, newOptions);
895		} else {
896			CFDictionaryRemoveValue(newDict, kSCPropVirtualNetworkInterfacesBondOptions);
897		}
898		if (!CFEqual(dict, newDict)) {
899			ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
900		}
901		CFRelease(newDict);
902		CFRelease(path);
903	}
904
905	// set options in the SCBondInterfaceRef
906	if (ok) {
907		if (interfacePrivate->bond.options != NULL) {
908			CFRelease(interfacePrivate->bond.options);
909			interfacePrivate->bond.options = NULL;
910		}
911		if (newOptions != NULL) {
912			interfacePrivate->bond.options = CFDictionaryCreateCopy(NULL, newOptions);
913		}
914	}
915
916	return ok;
917}
918
919
920static Boolean
921_SCBondInterfaceSetMode(SCBondInterfaceRef bond, CFNumberRef mode)
922{
923	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
924	Boolean				needs_release 		= FALSE;
925	Boolean				ok			= TRUE;
926
927	assert(bond != NULL);
928
929	if (mode == NULL) {
930		int	mode_num	= IF_BOND_MODE_LACP;
931
932		mode = CFNumberCreate(NULL, kCFNumberIntType, &mode_num);
933		needs_release = TRUE;
934	}
935
936	// set mode in the stored preferences
937	if (interfacePrivate->prefs != NULL) {
938		CFDictionaryRef		dict;
939		CFMutableDictionaryRef	newDict;
940		CFStringRef		path;
941
942		path = CFStringCreateWithFormat(NULL,
943						NULL,
944						CFSTR("/%@/%@/%@"),
945						kSCPrefVirtualNetworkInterfaces,
946						kSCNetworkInterfaceTypeBond,
947						interfacePrivate->entity_device);
948		dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
949		if (!isA_CFDictionary(dict)) {
950			// if the prefs are confused
951			CFRelease(path);
952			_SCErrorSet(kSCStatusFailed);
953			ok = FALSE;
954			goto done;
955		}
956		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
957		CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesBondMode, mode);
958		if (!CFEqual(dict, newDict)) {
959			ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
960		}
961		CFRelease(newDict);
962		CFRelease(path);
963	}
964
965	if (ok) {
966		CFRetain(mode);
967		if (interfacePrivate->bond.mode != NULL) {
968			CFRelease(interfacePrivate->bond.mode);
969		}
970		interfacePrivate->bond.mode = mode;
971	}
972
973    done :
974
975	if (needs_release) CFRelease(mode);
976	return ok;
977}
978
979Boolean
980SCBondInterfaceSetMode(SCBondInterfaceRef bond, CFNumberRef mode)
981{
982	int	mode_num;
983
984	if (!isA_SCBondInterface(bond) || !isA_CFNumber(mode)) {
985		_SCErrorSet(kSCStatusInvalidArgument);
986		return FALSE;
987	}
988
989	if (CFNumberGetValue(mode, kCFNumberIntType, &mode_num) == FALSE) {
990		_SCErrorSet(kSCStatusInvalidArgument);
991		return FALSE;
992	}
993
994	switch (mode_num) {
995		case IF_BOND_MODE_LACP:
996		case IF_BOND_MODE_STATIC:
997			break;
998		default:
999			_SCErrorSet(kSCStatusInvalidArgument);
1000			return FALSE;
1001	}
1002
1003	return (_SCBondInterfaceSetMode(bond, mode));
1004}
1005
1006CFNumberRef
1007SCBondInterfaceGetMode(SCBondInterfaceRef bond)
1008{
1009	SCNetworkInterfacePrivateRef	interfacePrivate	= (SCNetworkInterfacePrivateRef)bond;
1010
1011	if (!isA_SCBondInterface(bond)) {
1012		_SCErrorSet(kSCStatusInvalidArgument);
1013		return NULL;
1014	}
1015	return (interfacePrivate->bond.mode);
1016}
1017
1018
1019#pragma mark -
1020#pragma mark SCBondStatus APIs
1021
1022
1023typedef struct {
1024
1025	// base CFType information
1026	CFRuntimeBase			cfBase;
1027
1028	// bond status
1029	SCBondInterfaceRef		bond;
1030	CFDictionaryRef			status_bond;
1031
1032	// member interfaces and status
1033	CFArrayRef			interfaces;		// of SCNetworkInterfaceRef's
1034	CFDictionaryRef			status_interfaces;	// key = interface, val = interface status)
1035
1036} SCBondStatusPrivate, * SCBondStatusPrivateRef;
1037
1038
1039const CFStringRef kSCBondStatusDeviceAggregationStatus	= CFSTR("AggregationStatus");
1040const CFStringRef kSCBondStatusDeviceCollecting		= CFSTR("Collecting");
1041const CFStringRef kSCBondStatusDeviceDistributing	= CFSTR("Distributing");
1042
1043
1044static CFStringRef	__SCBondStatusCopyDescription	(CFTypeRef cf);
1045static void		__SCBondStatusDeallocate	(CFTypeRef cf);
1046static Boolean		__SCBondStatusEqual		(CFTypeRef cf1, CFTypeRef cf2);
1047
1048
1049static const CFRuntimeClass __SCBondStatusClass = {
1050	0,				// version
1051	"BondStatus",			// className
1052	NULL,				// init
1053	NULL,				// copy
1054	__SCBondStatusDeallocate,	// dealloc
1055	__SCBondStatusEqual,		// equal
1056	NULL,				// hash
1057	NULL,				// copyFormattingDesc
1058	__SCBondStatusCopyDescription	// copyDebugDesc
1059};
1060
1061
1062static CFTypeID		__kSCBondStatusTypeID	= _kCFRuntimeNotATypeID;
1063
1064
1065static pthread_once_t	bondStatus_init		= PTHREAD_ONCE_INIT;
1066
1067
1068static CFStringRef
1069__SCBondStatusCopyDescription(CFTypeRef cf)
1070{
1071	CFAllocatorRef		allocator	= CFGetAllocator(cf);
1072	CFMutableStringRef	result;
1073	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)cf;
1074
1075	result = CFStringCreateMutable(allocator, 0);
1076	CFStringAppendFormat(result, NULL, CFSTR("<SCBondStatus %p [%p]> {"), cf, allocator);
1077	CFStringAppendFormat(result, NULL, CFSTR(" bond = %@"), statusPrivate->bond);
1078	CFStringAppendFormat(result, NULL, CFSTR(", interface = %@"), statusPrivate->status_bond);
1079	CFStringAppendFormat(result, NULL, CFSTR(", members = %@"),   statusPrivate->status_interfaces);
1080	CFStringAppendFormat(result, NULL, CFSTR(" }"));
1081
1082	return result;
1083}
1084
1085
1086static void
1087__SCBondStatusDeallocate(CFTypeRef cf)
1088{
1089	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)cf;
1090
1091	/* release resources */
1092
1093	CFRelease(statusPrivate->bond);
1094	CFRelease(statusPrivate->status_bond);
1095	if (statusPrivate->interfaces != NULL) CFRelease(statusPrivate->interfaces);
1096	CFRelease(statusPrivate->status_interfaces);
1097	return;
1098}
1099
1100
1101static Boolean
1102__SCBondStatusEqual(CFTypeRef cf1, CFTypeRef cf2)
1103{
1104	SCBondStatusPrivateRef	status1	= (SCBondStatusPrivateRef)cf1;
1105	SCBondStatusPrivateRef	status2	= (SCBondStatusPrivateRef)cf2;
1106
1107	if (status1 == status2)
1108		return TRUE;
1109
1110	if (!CFEqual(status1->bond, status2->bond))
1111		return FALSE;	// if not the same bond
1112
1113	if (!CFEqual(status1->status_bond, status2->status_bond))
1114		return FALSE;	// if not the same interface status
1115
1116	if (!CFEqual(status1->status_interfaces, status2->status_interfaces))
1117		return FALSE;	// if not the same status of the member interfaces
1118
1119	return TRUE;
1120}
1121
1122
1123static void
1124__SCBondStatusInitialize(void)
1125{
1126	__kSCBondStatusTypeID = _CFRuntimeRegisterClass(&__SCBondStatusClass);
1127	return;
1128}
1129
1130
1131static SCBondStatusRef
1132__SCBondStatusCreatePrivate(CFAllocatorRef	allocator,
1133			    SCBondInterfaceRef	bond,
1134			    CFDictionaryRef	status_bond,
1135			    CFDictionaryRef	status_interfaces)
1136{
1137	SCBondStatusPrivateRef	statusPrivate;
1138	uint32_t		size;
1139
1140	/* initialize runtime */
1141	pthread_once(&bondStatus_init, __SCBondStatusInitialize);
1142
1143	/* allocate bond */
1144	size          = sizeof(SCBondStatusPrivate) - sizeof(CFRuntimeBase);
1145	statusPrivate = (SCBondStatusPrivateRef)_CFRuntimeCreateInstance(allocator,
1146									 __kSCBondStatusTypeID,
1147									 size,
1148									 NULL);
1149	if (statusPrivate == NULL) {
1150		return NULL;
1151	}
1152
1153	/* establish the bond status */
1154
1155	statusPrivate->bond		 = CFRetain(bond);
1156	statusPrivate->status_bond       = CFDictionaryCreateCopy(NULL, status_bond);
1157
1158	statusPrivate->interfaces        = NULL;
1159	statusPrivate->status_interfaces = CFDictionaryCreateCopy(NULL, status_interfaces);
1160
1161	return (SCBondStatusRef)statusPrivate;
1162}
1163
1164
1165static __inline__ CFTypeRef
1166isA_SCBondStatus(CFTypeRef obj)
1167{
1168	return (isA_CFType(obj, SCBondStatusGetTypeID()));
1169}
1170
1171
1172CFTypeID
1173SCBondStatusGetTypeID()
1174{
1175	pthread_once(&bondStatus_init, __SCBondStatusInitialize);	/* initialize runtime */
1176	return __kSCBondStatusTypeID;
1177}
1178
1179
1180#define	N_QUICK	16
1181
1182
1183CFArrayRef /* of SCNetworkInterfaceRef's */
1184SCBondStatusGetMemberInterfaces(SCBondStatusRef bondStatus)
1185{
1186	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)bondStatus;
1187
1188	if (!isA_SCBondStatus(bondStatus)) {
1189		return NULL;
1190	}
1191
1192	if (statusPrivate->interfaces == NULL) {
1193		const void *	keys_q[N_QUICK];
1194		const void **	keys	= keys_q;
1195		CFIndex		n;
1196
1197		n = CFDictionaryGetCount(statusPrivate->status_interfaces);
1198		if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
1199			keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
1200		}
1201		CFDictionaryGetKeysAndValues(statusPrivate->status_interfaces, keys, NULL);
1202		statusPrivate->interfaces = CFArrayCreate(NULL, keys, n, &kCFTypeArrayCallBacks);
1203		if (keys != keys_q) {
1204			CFAllocatorDeallocate(NULL, keys);
1205		}
1206	}
1207
1208	return statusPrivate->interfaces;
1209}
1210
1211
1212CFDictionaryRef
1213SCBondStatusGetInterfaceStatus(SCBondStatusRef bondStatus, SCNetworkInterfaceRef interface)
1214{
1215	CFDictionaryRef		status		= NULL;
1216	SCBondStatusPrivateRef	statusPrivate	= (SCBondStatusPrivateRef)bondStatus;
1217
1218	if (!isA_SCBondStatus(bondStatus)) {
1219		return NULL;
1220	}
1221
1222	if (interface == NULL) {
1223		// return status of the bond
1224		status = statusPrivate->status_bond;
1225	} else {
1226		// return status of the member interface
1227		status = CFDictionaryGetValue(statusPrivate->status_interfaces, interface);
1228	}
1229
1230	return status;
1231}
1232
1233
1234SCBondStatusRef
1235SCBondInterfaceCopyStatus(SCBondInterfaceRef bond)
1236{
1237	int				bond_if_active;
1238	int				bond_if_status;
1239	CFIndex				i;
1240	struct if_bond_status_req	*ibsr_p		= NULL;
1241	char				if_name[IFNAMSIZ];
1242	CFIndex				n;
1243	CFNumberRef			num;
1244	int				s;
1245	struct if_bond_status		*scan_p;
1246	SCBondStatusRef			status		= NULL;
1247	CFMutableDictionaryRef		status_bond;
1248	CFMutableDictionaryRef		status_interfaces;
1249
1250	if (!isA_SCBondInterface(bond)) {
1251		_SCErrorSet(kSCStatusInvalidArgument);
1252		return NULL;
1253	}
1254
1255	s = inet_dgram_socket();
1256	if (s == -1) {
1257		_SCErrorSet(errno);
1258		goto done;
1259	}
1260
1261	_SC_cfstring_to_cstring(SCNetworkInterfaceGetBSDName(bond),
1262				if_name,
1263				sizeof(if_name),
1264				kCFStringEncodingASCII);
1265	if (siocgifmedia(s, if_name, &bond_if_status, &bond_if_active) == -1) {
1266		_SCErrorSet(errno);
1267		switch (errno) {
1268			case EBUSY :
1269			case ENXIO :
1270				break;
1271			default :
1272				SCLog(TRUE, LOG_ERR,
1273				      CFSTR("siocgifmedia(%s) failed: %s"),
1274				      if_name,
1275				      strerror(errno));
1276		}
1277		goto done;
1278	}
1279	ibsr_p = if_bond_status_req_copy(s, if_name);
1280	if (ibsr_p == NULL) {
1281		_SCErrorSet(errno);
1282		goto done;
1283	}
1284
1285	status_bond = CFDictionaryCreateMutable(NULL,
1286						0,
1287						&kCFTypeDictionaryKeyCallBacks,
1288						&kCFTypeDictionaryValueCallBacks);
1289
1290	status_interfaces = CFDictionaryCreateMutable(NULL,
1291						      0,
1292						      &kCFTypeDictionaryKeyCallBacks,
1293						      &kCFTypeDictionaryValueCallBacks);
1294	n = ibsr_p->ibsr_total;
1295	for (i = 0, scan_p = (struct if_bond_status *)ibsr_p->ibsr_buffer; i < n; i++, scan_p++) {
1296		int				collecting	= 0;
1297		int				distributing	= 0;
1298		SCNetworkInterfaceRef		interface;
1299		CFStringRef			interface_name;
1300		struct if_bond_partner_state *	ps;
1301		CFMutableDictionaryRef		status_interface;
1302		int				status_val;
1303
1304		ps = &scan_p->ibs_partner_state;
1305
1306		if (lacp_actor_partner_state_in_sync(scan_p->ibs_state)) {
1307			/* we're in-sync */
1308			status_val = kSCBondStatusOK;
1309			if (lacp_actor_partner_state_in_sync(ps->ibps_state)) {
1310				/* partner is also in-sync */
1311				if (lacp_actor_partner_state_collecting(scan_p->ibs_state)
1312				    && lacp_actor_partner_state_distributing(ps->ibps_state)) {
1313					/* we're able to collect (receive) frames */
1314					collecting = 1;
1315				}
1316				if (lacp_actor_partner_state_distributing(scan_p->ibs_state)
1317				    && lacp_actor_partner_state_collecting(ps->ibps_state)) {
1318					/* we're able to distribute (transmit) frames */
1319					distributing = 1;
1320				}
1321			}
1322		} else {
1323			int			active = 0;
1324			int			status = 0;
1325			static lacp_system	zeroes = { {0, 0, 0, 0, 0, 0}};
1326
1327			if (siocgifmedia(s, scan_p->ibs_if_name, &status, &active) == -1) {
1328				switch (errno) {
1329					case EBUSY :
1330					case ENXIO :
1331						break;
1332					default :
1333						SCLog(TRUE, LOG_ERR,
1334						      CFSTR("siocgifmedia(%s) failed: %s"),
1335						      if_name,
1336						      strerror(errno));
1337						break;
1338				}
1339			}
1340			if (((status & IFM_AVALID) == 0) ||
1341			    ((status & IFM_ACTIVE) == 0) ||
1342			    ((active & IFM_FDX   ) == 0)) {
1343				/* link down or not full-duplex */
1344				status_val = kSCBondStatusLinkInvalid;
1345			} else if ((ps->ibps_system_priority == 0) &&
1346				   (bcmp(&zeroes, &ps->ibps_system, sizeof(zeroes)) == 0)) {
1347				/* no one on the other end of the link */
1348				status_val = kSCBondStatusNoPartner;
1349			} else if (active != bond_if_active) {
1350				/* the link speed was different */
1351				status_val = kSCBondStatusLinkInvalid;
1352			} else {
1353				/* partner is not in the active group */
1354				status_val = kSCBondStatusNotInActiveGroup;
1355			}
1356		}
1357
1358		// interface
1359		strlcpy(if_name, scan_p->ibs_if_name, sizeof(if_name));
1360		interface_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
1361		interface = _SCNetworkInterfaceCreateWithBSDName(NULL, interface_name,
1362								 kIncludeNoVirtualInterfaces);
1363		CFRelease(interface_name);
1364
1365		// interface status
1366		status_interface = CFDictionaryCreateMutable(NULL,
1367							     0,
1368							     &kCFTypeDictionaryKeyCallBacks,
1369							     &kCFTypeDictionaryValueCallBacks);
1370		num = CFNumberCreate(NULL, kCFNumberIntType, &status_val);
1371		CFDictionarySetValue(status_interface, kSCBondStatusDeviceAggregationStatus, num);
1372		CFRelease(num);
1373		num = CFNumberCreate(NULL, kCFNumberIntType, &collecting);
1374		CFDictionarySetValue(status_interface, kSCBondStatusDeviceCollecting, num);
1375		CFRelease(num);
1376		num = CFNumberCreate(NULL, kCFNumberIntType, &distributing);
1377		CFDictionarySetValue(status_interface, kSCBondStatusDeviceDistributing, num);
1378		CFRelease(num);
1379
1380		CFDictionarySetValue(status_interfaces, interface, status_interface);
1381		CFRelease(interface);
1382		CFRelease(status_interface);
1383	}
1384
1385	status = __SCBondStatusCreatePrivate(NULL, bond, status_bond, status_interfaces);
1386	CFRelease(status_bond);
1387	CFRelease(status_interfaces);
1388
1389    done:
1390
1391	if (s != -1) {
1392		close(s);
1393	}
1394	if (ibsr_p != NULL) {
1395		free(ibsr_p);
1396	}
1397	return (SCBondStatusRef)status;
1398}
1399
1400
1401#pragma mark -
1402#pragma mark SCBondInterface management
1403
1404
1405static Boolean
1406__bond_set_mode(int s, CFStringRef bond_if, CFNumberRef mode)
1407{
1408	struct if_bond_req	breq;
1409	struct ifreq		ifr;
1410	int					mode_num;
1411
1412	mode_num = IF_BOND_MODE_LACP;
1413	if (mode != NULL) {
1414		CFNumberGetValue(mode, kCFNumberIntType, &mode_num);
1415	}
1416
1417	// bond interface
1418	bzero(&ifr, sizeof(ifr));
1419	(void) _SC_cfstring_to_cstring(bond_if,
1420				       ifr.ifr_name,
1421				       sizeof(ifr.ifr_name),
1422				       kCFStringEncodingASCII);
1423	ifr.ifr_data = (caddr_t)&breq;
1424	bzero(&breq, sizeof(breq));
1425	breq.ibr_op = IF_BOND_OP_SET_MODE;
1426	breq.ibr_ibru.ibru_int_val = mode_num;
1427	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) {
1428		_SCErrorSet(errno);
1429		SCLog(TRUE, LOG_ERR,
1430		      CFSTR("could not set mode to %@ on bond \"%@\": %s"),
1431		      mode,
1432		      bond_if,
1433		      strerror(errno));
1434		return FALSE;
1435	}
1436
1437	return TRUE;
1438}
1439
1440static Boolean
1441__bond_add_interface(int s, CFStringRef bond_if, CFStringRef interface_if)
1442{
1443	struct if_bond_req	breq;
1444	struct ifreq		ifr;
1445
1446	// bond interface
1447	bzero(&ifr, sizeof(ifr));
1448	(void) _SC_cfstring_to_cstring(bond_if,
1449				       ifr.ifr_name,
1450				       sizeof(ifr.ifr_name),
1451				       kCFStringEncodingASCII);
1452	ifr.ifr_data = (caddr_t)&breq;
1453
1454	// new bond member
1455	bzero(&breq, sizeof(breq));
1456	breq.ibr_op = IF_BOND_OP_ADD_INTERFACE;
1457	(void) _SC_cfstring_to_cstring(interface_if,
1458				       breq.ibr_ibru.ibru_if_name,
1459				       sizeof(breq.ibr_ibru.ibru_if_name),
1460				       kCFStringEncodingASCII);
1461
1462	// add new bond member
1463	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) {
1464		_SCErrorSet(errno);
1465		SCLog(TRUE, LOG_ERR,
1466		      CFSTR("could not add interface \"%@\" to bond \"%@\": %s"),
1467		      interface_if,
1468		      bond_if,
1469		      strerror(errno));
1470		return FALSE;
1471	}
1472
1473	return TRUE;
1474}
1475
1476
1477static Boolean
1478__bond_remove_interface(int s, CFStringRef bond_if, CFStringRef interface_if)
1479{
1480	struct if_bond_req	breq;
1481	struct ifreq		ifr;
1482
1483	// bond interface
1484	bzero(&ifr, sizeof(ifr));
1485	(void) _SC_cfstring_to_cstring(bond_if,
1486				       ifr.ifr_name,
1487				       sizeof(ifr.ifr_name),
1488				       kCFStringEncodingASCII);
1489	ifr.ifr_data = (caddr_t)&breq;
1490
1491	// bond member to remove
1492	bzero(&breq, sizeof(breq));
1493	breq.ibr_op = IF_BOND_OP_REMOVE_INTERFACE;
1494	(void) _SC_cfstring_to_cstring(interface_if,
1495				       breq.ibr_ibru.ibru_if_name,
1496				       sizeof(breq.ibr_ibru.ibru_if_name),
1497				       kCFStringEncodingASCII);
1498
1499	// remove bond member
1500	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) {
1501		_SCErrorSet(errno);
1502		SCLog(TRUE, LOG_ERR,
1503		      CFSTR("could not remove interface \"%@\" from bond \"%@\": %s"),
1504		      interface_if,
1505		      bond_if,
1506		      strerror(errno));
1507		return FALSE;
1508	}
1509
1510	return TRUE;
1511}
1512
1513
1514Boolean
1515_SCBondInterfaceUpdateConfiguration(SCPreferencesRef prefs)
1516{
1517	CFArrayRef			active		= NULL;
1518	CFArrayRef			config		= NULL;
1519	CFIndex				i;
1520	CFIndex				nActive;
1521	CFIndex				nConfig;
1522	Boolean				ok		= TRUE;
1523	int				s		= -1;
1524
1525	if (prefs == NULL) {
1526		_SCErrorSet(kSCStatusInvalidArgument);
1527		return FALSE;
1528	}
1529
1530	/* configured Bonds */
1531	config  = SCBondInterfaceCopyAll(prefs);
1532	nConfig = (config != NULL) ? CFArrayGetCount(config) : 0;
1533
1534	/* active Bonds */
1535	active  = _SCBondInterfaceCopyActive();
1536	nActive = (active != NULL) ? CFArrayGetCount(active) : 0;
1537
1538	/*
1539	 * remove any no-longer-configured bond interfaces and
1540	 * any devices associated with a bond that are no longer
1541	 * associated with a bond.
1542	 */
1543	for (i = 0; i < nActive; i++) {
1544		SCBondInterfaceRef	a_bond;
1545		CFStringRef		a_bond_if;
1546		CFIndex			j;
1547		Boolean			found	= FALSE;
1548
1549		a_bond    = CFArrayGetValueAtIndex(active, i);
1550		a_bond_if = SCNetworkInterfaceGetBSDName(a_bond);
1551
1552		for (j = 0; j < nConfig; j++) {
1553			SCBondInterfaceRef	c_bond;
1554			CFStringRef		c_bond_if;
1555
1556			c_bond    = CFArrayGetValueAtIndex(config, j);
1557			c_bond_if = SCNetworkInterfaceGetBSDName(c_bond);
1558
1559			if (CFEqual(a_bond_if, c_bond_if)) {
1560				CFIndex		a;
1561				CFArrayRef	a_bond_interfaces;
1562				CFIndex		a_count;
1563				CFArrayRef	c_bond_interfaces;
1564				CFIndex		c_count;
1565
1566				c_bond_interfaces = SCBondInterfaceGetMemberInterfaces(c_bond);
1567				c_count           = (c_bond_interfaces != NULL) ? CFArrayGetCount(c_bond_interfaces) : 0;
1568
1569				a_bond_interfaces = SCBondInterfaceGetMemberInterfaces(a_bond);
1570				a_count           = (a_bond_interfaces != NULL) ? CFArrayGetCount(a_bond_interfaces) : 0;
1571
1572				for (a = 0; a < a_count; a++) {
1573					SCNetworkInterfaceRef	a_interface;
1574					CFStringRef		a_interface_if;
1575
1576					a_interface = CFArrayGetValueAtIndex(a_bond_interfaces, a);
1577					if ((c_count == 0) ||
1578					    !CFArrayContainsValue(c_bond_interfaces,
1579								  CFRangeMake(0, c_count),
1580								  a_interface)) {
1581						/*
1582						 * if this device is no longer part
1583						 * of the bond.
1584						 */
1585						if (s == -1) {
1586							s = inet_dgram_socket();
1587							if (s == -1) {
1588								_SCErrorSet(errno);
1589								ok = FALSE;
1590								goto done;
1591							}
1592						}
1593
1594						a_interface_if = SCNetworkInterfaceGetBSDName(a_interface);
1595						if (!__bond_remove_interface(s, a_bond_if, a_interface_if)) {
1596							ok = FALSE;
1597						}
1598					}
1599				}
1600
1601				found = TRUE;
1602				break;
1603			}
1604		}
1605
1606		if (!found) {
1607			/*
1608			 * if this interface is no longer configured
1609			 */
1610			if (s == -1) {
1611				s = inet_dgram_socket();
1612				if (s == -1) {
1613					_SCErrorSet(errno);
1614					ok = FALSE;
1615					goto done;
1616				}
1617			}
1618
1619			if (!__destroyInterface(s, a_bond_if)) {
1620				_SCErrorSet(errno);
1621				ok = FALSE;
1622			}
1623		}
1624	}
1625
1626	/*
1627	 * add any newly-configured bond interfaces and add any
1628	 * devices that should now be associated with the bond.
1629	 */
1630	for (i = 0; i < nConfig; i++) {
1631		CFNumberRef		c_bond_mode;
1632		SCBondInterfaceRef	c_bond;
1633		CFArrayRef		c_bond_interfaces;
1634		CFStringRef		c_bond_if;
1635		CFIndex			c_count;
1636		Boolean			found		= FALSE;
1637		CFIndex			j;
1638
1639		c_bond            = CFArrayGetValueAtIndex(config, i);
1640		c_bond_if         = SCNetworkInterfaceGetBSDName(c_bond);
1641		c_bond_interfaces = SCBondInterfaceGetMemberInterfaces(c_bond);
1642		c_bond_mode       = SCBondInterfaceGetMode(c_bond);
1643		c_count           = (c_bond_interfaces != NULL) ? CFArrayGetCount(c_bond_interfaces) : 0;
1644
1645		for (j = 0; j < nActive; j++) {
1646			SCBondInterfaceRef	a_bond;
1647			CFArrayRef		a_bond_interfaces;
1648			CFNumberRef		a_bond_mode;
1649			CFStringRef		a_bond_if;
1650			CFIndex			a_count;
1651
1652			a_bond            = CFArrayGetValueAtIndex(active, j);
1653			a_bond_if         = SCNetworkInterfaceGetBSDName(a_bond);
1654			a_bond_interfaces = SCBondInterfaceGetMemberInterfaces(a_bond);
1655			a_bond_mode		  = SCBondInterfaceGetMode(a_bond);
1656			a_count           = (a_bond_interfaces != NULL) ? CFArrayGetCount(a_bond_interfaces) : 0;
1657
1658			if (CFEqual(c_bond_if, a_bond_if)) {
1659				CFIndex	c;
1660				Boolean	if_list_change = FALSE;
1661				Boolean mode_change = FALSE;
1662
1663				found = TRUE;
1664
1665				if (!_SC_CFEqual(a_bond_mode, c_bond_mode)) {
1666					mode_change = TRUE;
1667				}
1668
1669				if (!_SC_CFEqual(c_bond_interfaces, a_bond_interfaces)) {
1670					if_list_change = TRUE;
1671				}
1672				if (!mode_change && !if_list_change) {
1673					break;	// if no change
1674				}
1675				if (s == -1) {
1676					s = inet_dgram_socket();
1677					if (s == -1) {
1678						_SCErrorSet(errno);
1679						ok = FALSE;
1680						goto done;
1681					}
1682				}
1683				if (mode_change) {
1684					__bond_set_mode(s, a_bond_if, c_bond_mode);
1685				}
1686				if (!if_list_change) {
1687					break; // no if list changes
1688				}
1689
1690				/*
1691				 * ensure that the first device of the bond matches, if
1692				 * not then we remove all current devices and add them
1693				 * back in the preferred order.
1694				 */
1695				if ((c_count > 0) &&
1696				    (a_count > 0) &&
1697				    !CFEqual(CFArrayGetValueAtIndex(c_bond_interfaces, 0),
1698					     CFArrayGetValueAtIndex(a_bond_interfaces, 0))) {
1699					CFIndex	a;
1700
1701					for (a = 0; a < a_count; a++) {
1702						SCNetworkInterfaceRef	a_interface;
1703						CFStringRef		a_interface_if;
1704
1705						a_interface = CFArrayGetValueAtIndex(a_bond_interfaces, a);
1706						if (!CFArrayContainsValue(c_bond_interfaces,
1707									 CFRangeMake(0, c_count),
1708									 a_interface)) {
1709							continue;	// if already removed
1710						}
1711
1712						a_interface_if = SCNetworkInterfaceGetBSDName(a_interface);
1713						if (!__bond_remove_interface(s, a_bond_if, a_interface_if)) {
1714							ok = FALSE;
1715						}
1716					}
1717
1718					a_count = 0;	// all active devices have been removed
1719				}
1720
1721				/*
1722				 * add any devices which are not currently associated
1723				 * with the bond interface.
1724				 */
1725				for (c = 0; c < c_count; c++) {
1726					SCNetworkInterfaceRef		c_interface;
1727					SCNetworkInterfacePrivateRef	c_interfacePrivate;
1728					CFStringRef			c_interface_if;
1729
1730					c_interface = CFArrayGetValueAtIndex(c_bond_interfaces, c);
1731					if ((a_count == 0) ||
1732					    !CFArrayContainsValue(a_bond_interfaces,
1733								  CFRangeMake(0, a_count),
1734								  c_interface)) {
1735						/*
1736						 * check if this member interface can be added to a bond.
1737						 */
1738						c_interfacePrivate = (SCNetworkInterfacePrivateRef)c_interface;
1739						if (!c_interfacePrivate->supportsBond) {
1740							// if member not supported
1741							continue;
1742						}
1743
1744						/*
1745						 * if this member interface is not currently part of the bond.
1746						 */
1747						c_interface_if = SCNetworkInterfaceGetBSDName(c_interface);
1748						if (!__bond_add_interface(s, c_bond_if, c_interface_if)) {
1749							// if member could not be added
1750							ok = FALSE;
1751						}
1752					}
1753				}
1754
1755				break;
1756			}
1757		}
1758
1759		if (!found) {
1760			CFIndex	c;
1761
1762			if (s == -1) {
1763				s = inet_dgram_socket();
1764				if (s == -1) {
1765					_SCErrorSet(errno);
1766					ok = FALSE;
1767					goto done;
1768				}
1769			}
1770
1771			/*
1772			 * establish the new bond interface.
1773			 */
1774			if (!__createInterface(s, c_bond_if)) {
1775				_SCErrorSet(errno);
1776				ok = FALSE;
1777				continue;
1778			}
1779
1780			/* set the mode */
1781			__bond_set_mode(s, c_bond_if, c_bond_mode);
1782
1783			/*
1784			 * add the member interfaces
1785			 */
1786			for (c = 0; c < c_count; c++) {
1787				SCNetworkInterfaceRef		c_interface;
1788				SCNetworkInterfacePrivateRef	c_interfacePrivate;
1789				CFStringRef			c_interface_if;
1790
1791				c_interface = CFArrayGetValueAtIndex(c_bond_interfaces, c);
1792				c_interfacePrivate = (SCNetworkInterfacePrivateRef)c_interface;
1793				if (!c_interfacePrivate->supportsBond) {
1794					// if member not supported
1795					continue;
1796				}
1797
1798				c_interface_if = SCNetworkInterfaceGetBSDName(c_interface);
1799				if (!__bond_add_interface(s, c_bond_if, c_interface_if)) {
1800					// if member could not be added
1801					ok = FALSE;
1802				}
1803			}
1804		}
1805
1806	}
1807
1808    done :
1809
1810	if (active != NULL)	CFRelease(active);
1811	if (config != NULL)	CFRelease(config);
1812	if (s != -1)		(void) close(s);
1813
1814	return ok;
1815}
1816