]> git.saurik.com Git - apple/configd.git/blob - sctest/SCTestInterfaceNamer.m
bbf42ffe63b024cd4e8d95a740854de23cabf0ae
[apple/configd.git] / sctest / SCTestInterfaceNamer.m
1 /*
2 * Copyright (c) 2020 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 #import "SCTest.h"
25 #import "SCTestUtils.h"
26
27 #include <net/ethernet.h>
28 #include <IOKit/IOKitLib.h>
29 #include <IOKit/IOBSD.h>
30 #include <IOKit/network/IONetworkController.h>
31 #include <IOKit/network/IOUserEthernetController.h>
32 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
33 #include <IOKit/usb/USB.h>
34
35 #define INTERFACES_KEY @"State:/Network/Interface"
36 #define PLUGIN_INTERFACE_NAMER_KEY @"Plugin:InterfaceNamer"
37
38 #define WAIT_TIME (30 * NSEC_PER_SEC)
39
40
41 @interface SCTestInterfaceNamer : SCTest
42 @property NSArray *interfaces_all;
43 @property NSArray *interfaces_preconfigured;
44 @property dispatch_queue_t queue;
45 @property dispatch_semaphore_t sem_interfaces_all;
46 @property dispatch_semaphore_t sem_interfaces_preconfigured;
47 @property SCDynamicStoreRef store;
48 @end
49
50 @implementation SCTestInterfaceNamer
51
52 + (NSString *)command
53 {
54 return @"InterfaceNamer";
55 }
56
57 + (NSString *)commandDescription
58 {
59 return @"Tests the InterfaceNamer.bundle code paths";
60 }
61
62 static void
63 storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
64 {
65 @autoreleasepool {
66 NSDictionary *dict;
67 NSArray *interfaces;
68 SCTestInterfaceNamer *test = (__bridge SCTestInterfaceNamer *)info;
69
70 if ([(__bridge NSArray *)changedKeys containsObject:INTERFACES_KEY]) {
71 // copy list of interfaces
72 dict = (__bridge_transfer NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)INTERFACES_KEY);
73 interfaces = [dict objectForKey:(__bridge NSString *)kSCPropNetInterfaces];
74 if (!_SC_CFEqual((__bridge CFArrayRef)interfaces, (__bridge CFArrayRef)test.interfaces_all)) {
75 test.interfaces_all = interfaces;
76 dispatch_semaphore_signal(test.sem_interfaces_all);
77 }
78 }
79
80 if ([(__bridge NSArray *)changedKeys containsObject:PLUGIN_INTERFACE_NAMER_KEY]) {
81 // copy list of [pre-configured] interfaces
82 dict = (__bridge_transfer NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)PLUGIN_INTERFACE_NAMER_KEY);
83 interfaces = [dict objectForKey:@"_PreConfigured_"];
84 if (!_SC_CFEqual((__bridge CFArrayRef)interfaces, (__bridge CFArrayRef)test.interfaces_preconfigured)) {
85 test.interfaces_preconfigured = interfaces;
86 dispatch_semaphore_signal(test.sem_interfaces_preconfigured);
87 }
88 }
89 }
90
91 return;
92 }
93
94 - (instancetype)initWithOptions:(NSDictionary *)options
95 {
96 self = [super initWithOptions:options];
97 // if (self) {
98 // }
99 return self;
100 }
101
102 - (void)dealloc
103 {
104 [self watchForChanges:FALSE];
105 }
106
107 - (void)start
108 {
109 [self cleanupAndExitWithErrorCode:0];
110 }
111
112 - (void)cleanupAndExitWithErrorCode:(int)error
113 {
114 [super cleanupAndExitWithErrorCode:error];
115 }
116
117 - (BOOL)setup
118 {
119 return YES;
120 }
121
122 - (BOOL)unitTest
123 {
124 if(![self setup]) {
125 return NO;
126 }
127
128 BOOL allUnitTestsPassed = YES;
129 allUnitTestsPassed &= [self unitTestInsertRemoveOneInterface];
130
131 if(![self tearDown]) {
132 return NO;
133 }
134
135 return allUnitTestsPassed;
136 }
137
138 - (BOOL)tearDown
139 {
140 return YES;
141 }
142
143 - (void)watchForChanges:(BOOL)enable
144 {
145 if (enable) {
146 SCDynamicStoreContext context = {0, NULL, CFRetain, CFRelease, NULL};
147 Boolean ok;
148
149 self.queue = dispatch_queue_create("SCTestInterfaceNamer callback queue", NULL);
150
151 self.sem_interfaces_all = dispatch_semaphore_create(0);
152
153 self.sem_interfaces_preconfigured = dispatch_semaphore_create(0);
154
155 context.info = (__bridge void * _Nullable)self;
156 self.store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("SCTest"), storeCallback, &context);
157
158 ok = SCDynamicStoreSetNotificationKeys(self.store,
159 (__bridge CFArrayRef)@[INTERFACES_KEY,
160 PLUGIN_INTERFACE_NAMER_KEY],
161 NULL);
162 assert(ok);
163
164 ok = SCDynamicStoreSetDispatchQueue(self.store, self.queue);
165 assert(ok);
166 } else {
167 if (self.store != NULL) {
168 (void)SCDynamicStoreSetDispatchQueue(self.store, NULL);
169 CFRelease(self.store);
170 self.store = NULL;
171 }
172
173 self.queue = NULL;
174 self.sem_interfaces_all = NULL;
175 self.sem_interfaces_preconfigured = NULL;
176 }
177 }
178
179 - (BOOL)isPreconfiguredInterface:(NSString *)bsdName
180 {
181 return [self.interfaces_preconfigured containsObject:bsdName];;
182 }
183
184 #pragma mark -
185 #pragma mark IOEthernetController support
186
187 static CFStringRef
188 copy_interface_name(IOEthernetControllerRef controller)
189 {
190 CFStringRef bsdName;
191 io_object_t interface;
192
193 interface = IOEthernetControllerGetIONetworkInterfaceObject(controller);
194 if (interface == MACH_PORT_NULL) {
195 SCTestLog("*** could not get interface for controller");
196 return NULL;
197 }
198
199 bsdName = IORegistryEntryCreateCFProperty(interface, CFSTR(kIOBSDNameKey), NULL, kNilOptions);
200 if (bsdName == NULL) {
201 SCTestLog("*** IOEthernetController with no BSD interface name");
202 return NULL;
203 }
204
205 return bsdName;
206 }
207
208 static CFDataRef
209 copy_interface_mac(IOEthernetControllerRef controller)
210 {
211 CFDataRef macAddress;
212 io_object_t interface;
213
214 interface = IOEthernetControllerGetIONetworkInterfaceObject(controller);
215 if (interface == MACH_PORT_NULL) {
216 SCTestLog("*** could not get interface for controller");
217 return NULL;
218 }
219
220 macAddress = IORegistryEntrySearchCFProperty(interface,
221 kIOServicePlane,
222 CFSTR(kIOMACAddress),
223 NULL,
224 kIORegistryIterateRecursively | kIORegistryIterateParents);
225 if (macAddress == NULL) {
226 SCTestLog("*** IOEthernetController with no BSD interface name");
227 return NULL;
228 }
229
230 return macAddress;
231 }
232
233 static IOEthernetControllerRef
234 create_hidden_interface(u_char ea_unique)
235 {
236 IOEthernetControllerRef controller;
237 CFDataRef data;
238 struct ether_addr ea = { .octet = { 0x02, 'F', 'A', 'K', 'E', ea_unique } };
239 CFMutableDictionaryRef merge;
240 CFNumberRef num;
241 CFMutableDictionaryRef props;
242 const int usb_vid_apple = kIOUSBAppleVendorID;
243
244 props = CFDictionaryCreateMutable(NULL, 0,
245 &kCFTypeDictionaryKeyCallBacks,
246 &kCFTypeDictionaryValueCallBacks);
247
248 data = CFDataCreate(NULL, ea.octet, ETHER_ADDR_LEN);
249 CFDictionarySetValue(props, kIOEthernetHardwareAddress, data);
250 CFRelease(data);
251
252 merge = CFDictionaryCreateMutable(NULL, 0,
253 &kCFTypeDictionaryKeyCallBacks,
254 &kCFTypeDictionaryValueCallBacks);
255 CFDictionarySetValue(merge, CFSTR(kIOPropertyProductNameKey), CFSTR("Hidden Ethernet"));
256 CFDictionarySetValue(merge, kIOUserEthernetInterfaceRole, CFSTR("hidden-ethernet"));
257 CFDictionarySetValue(merge, kSCNetworkInterfaceHiddenConfigurationKey, kCFBooleanTrue);
258 num = CFNumberCreate(NULL, kCFNumberIntType, &usb_vid_apple);
259 CFDictionarySetValue(merge, CFSTR(kUSBVendorID), num);
260 CFRelease(num);
261 CFDictionarySetValue(props, kIOUserEthernetInterfaceMergeProperties, merge);
262 CFRelease(merge);
263
264 controller = IOEthernetControllerCreate(NULL, props);
265 CFRelease(props);
266 if (controller == NULL) {
267 SCTestLog("*** could not create ethernet controller for \"%s\"", ether_ntoa(&ea));
268 return NULL;
269 }
270
271 return controller;
272 }
273
274 - (BOOL)interfaceAdd:(u_char)ea_unique controller:(IOEthernetControllerRef *)newController
275 {
276 BOOL ok = FALSE;
277
278 do {
279 NSString *bsdName;
280 NSData *macAddress;
281 long status;
282
283 // add an interface
284 *newController = create_hidden_interface(ea_unique);
285 if (*newController == NULL) {
286 SCTestLog("*** could not create controller");
287 break;
288 }
289
290 // wait for the [BSD] interface to show up
291 status = dispatch_semaphore_wait(self.sem_interfaces_all, dispatch_time(DISPATCH_TIME_NOW, WAIT_TIME));
292 if (status != 0) {
293 // if timeout
294 SCTestLog("*** no KernelEventMonitor change posted, interface not? created");
295 break;
296 }
297
298 bsdName = (__bridge_transfer NSString *)copy_interface_name(*newController);
299 if (bsdName == NULL) {
300 break;
301 }
302
303 macAddress = (__bridge_transfer NSData *)copy_interface_mac(*newController);
304 if (macAddress == NULL) {
305 break;
306 }
307
308 SCTestLog(" Interface \"%@\" added, mac=%s",
309 bsdName,
310 ether_ntoa((struct ether_addr *)macAddress.bytes));
311
312 // check if pre-configured
313 status = dispatch_semaphore_wait(self.sem_interfaces_preconfigured, dispatch_time(DISPATCH_TIME_NOW, WAIT_TIME));
314 if (status != 0) {
315 // if timeout
316 SCTestLog("*** no InterfaceNamer change posted, not? preconfigured");
317 break;
318 }
319
320 if (![self isPreconfiguredInterface:bsdName]) {
321 SCTestLog("*** Interface \"%@\" is not pre-configured", bsdName);
322 break;
323 }
324
325 SCTestLog(" Interface \"%@\" is pre-configured", bsdName);
326
327 ok = TRUE;
328 } while (0);
329
330 if (!ok) {
331 if (*newController != NULL) {
332 CFRelease(*newController);
333 *newController = NULL;
334 }
335 }
336
337 return ok;
338 }
339
340 - (BOOL)interfaceRemove:(IOEthernetControllerRef)controller
341 {
342 BOOL ok = FALSE;
343
344 do {
345 NSString *bsdName = (__bridge_transfer NSString *)copy_interface_name(controller);
346 long status;
347
348 // remove the interface
349 CFRelease(controller);
350
351 // wait for the [BSD] interface to go away
352 status = dispatch_semaphore_wait(self.sem_interfaces_all, dispatch_time(DISPATCH_TIME_NOW, WAIT_TIME));
353 if (status != 0) {
354 // if timeout
355 SCTestLog("*** no KernelEventMonitor change posted, interface not? removed");
356 break;
357 }
358
359 SCTestLog(" Interface \"%@\" removed", bsdName);
360
361 // check if [still] pre-configured
362 status = dispatch_semaphore_wait(self.sem_interfaces_preconfigured, dispatch_time(DISPATCH_TIME_NOW, WAIT_TIME));
363 if (status != 0) {
364 // if timeout
365 SCTestLog("*** no InterfaceNamer change posted, still? preconfigured");
366 break;
367 }
368
369 if ([self isPreconfiguredInterface:bsdName]) {
370 SCTestLog("*** interface \"%@\" is still pre-configured", bsdName);
371 break;
372 }
373
374 ok = TRUE;
375 } while (0);
376
377 return ok;
378 }
379
380 #pragma mark -
381 #pragma mark unitTestInsertRemoveOneInterface
382
383 #define N_INTERFACES 5
384
385 - (BOOL)unitTestInsertRemoveOneInterface
386 {
387 NSString *bsdName1 = nil;
388 BOOL ok;
389 SCTestInterfaceNamer *test;
390
391 test = [[SCTestInterfaceNamer alloc] initWithOptions:self.options];
392 [test watchForChanges:TRUE];
393
394 for (size_t i = 0; i < N_INTERFACES; i++) {
395 NSString *bsdName;
396 IOEthernetControllerRef controller;
397
398 SCTestLog("Interface #%zd", i + 1);
399
400 // add an interface
401 ok = [test interfaceAdd:i controller:&controller];
402 if (!ok) {
403 break;
404 }
405
406 // check the assigned interface name
407 bsdName = (__bridge_transfer NSString *)copy_interface_name(controller);
408 if (i == 0) {
409 bsdName1 = bsdName;
410 } else if ([bsdName isNotEqualTo:bsdName1]) {
411 SCTestLog("*** interface name not re-assigned, expected \"%@\", assigned \"%@\"", bsdName1, bsdName);
412 break;
413 }
414
415 // remove the interface
416 ok = [test interfaceRemove:controller];
417 if (!ok) {
418 break;
419 }
420 };
421
422 if (ok) {
423 SCTestLog("Successfully completed unitTestInsertRemoveOneInterface unit test");
424 }
425
426 return ok;
427 }
428
429 #pragma mark -
430 #pragma mark unitTestInsertRemoveMultipleInterfaces
431
432 #define N_INTERFACES_SLOT_X (N_INTERFACES / 2)
433
434 - (BOOL)unitTestInsertRemoveMultipleInterfaces
435 {
436 NSString *bsdName;
437 IOEthernetControllerRef controller;
438 struct {
439 NSString *bsdName;
440 IOEthernetControllerRef controller;
441 } interfaces[N_INTERFACES] = { { } };
442 BOOL ok;
443 SCTestInterfaceNamer *test;
444
445 test = [[SCTestInterfaceNamer alloc] initWithOptions:self.options];
446 [test watchForChanges:TRUE];
447
448 SCTestLog("Adding %d interfaces", N_INTERFACES);
449
450 for (size_t i = 0; i < N_INTERFACES; i++) {
451 // add an interface
452 ok = [test interfaceAdd:i controller:&controller];
453 if (!ok) {
454 break;
455 }
456
457 interfaces[i].controller = controller;
458 interfaces[i].bsdName = (__bridge_transfer NSString *)copy_interface_name(controller);
459 }
460
461 if (ok) {
462 SCTestLog("Removing interfaces");
463
464 for (size_t i = 0; i < N_INTERFACES; i++) {
465 // remove the interface
466 ok = [test interfaceRemove:interfaces[i].controller];
467 interfaces[i].controller = NULL;
468 if (!ok) {
469 break;
470 }
471 }
472 }
473
474 if (ok) {
475 SCTestLog("Re-adding %d interfaces", N_INTERFACES);
476
477 for (size_t i = 0; i < N_INTERFACES; i++) {
478 // add an interface
479 ok = [test interfaceAdd:i controller:&controller];
480 if (!ok) {
481 break;
482 }
483
484 interfaces[i].controller = controller;
485
486 bsdName = (__bridge_transfer NSString *)copy_interface_name(controller);
487 ok = [bsdName isEqualTo:interfaces[i].bsdName];
488 if (!ok) {
489 SCTestLog("*** interface %zd not assigned the same name, expected \"%@\", assigned \"%@\"",
490 i,
491 interfaces[i].bsdName,
492 bsdName);
493 break;
494 }
495
496 interfaces[i].bsdName = bsdName;
497 }
498 }
499
500 if (ok) {
501 SCTestLog("Removing one interface");
502
503 ok = [test interfaceRemove:interfaces[N_INTERFACES_SLOT_X].controller];
504 interfaces[N_INTERFACES_SLOT_X].controller = NULL;
505 }
506
507 if (ok) {
508 SCTestLog("Adding a new interface");
509
510 do {
511 // add a new interface (should re-use the first available name)
512 ok = [test interfaceAdd:N_INTERFACES controller:&controller];
513 if (!ok) {
514 break;
515 }
516
517 interfaces[N_INTERFACES_SLOT_X].controller = controller;
518
519 bsdName = (__bridge_transfer NSString *)copy_interface_name(controller);
520 ok = [bsdName isEqualTo:interfaces[N_INTERFACES_SLOT_X].bsdName];
521 if (!ok) {
522 SCTestLog("*** interface %d not assigned an old name, expected \"%@\", assigned \"%@\"",
523 N_INTERFACES,
524 interfaces[N_INTERFACES_SLOT_X].bsdName,
525 bsdName);
526 break;
527 }
528
529 interfaces[N_INTERFACES_SLOT_X].bsdName = bsdName;
530 } while (0);
531 }
532
533 if (ok) {
534 SCTestLog("Removing interfaces (again)");
535
536 for (size_t i = 0; i < N_INTERFACES; i++) {
537 // remove the interface
538 ok = [test interfaceRemove:interfaces[i].controller];
539 interfaces[i].controller = NULL;
540 if (!ok) {
541 break;
542 }
543 }
544 }
545
546 // cleanup
547 for (size_t i = 0; i < N_INTERFACES; i++) {
548 if (interfaces[i].controller != NULL) {
549 CFRelease(interfaces[i].controller);
550 }
551 }
552
553 return ok;
554 }
555
556 @end