]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/VLANConfiguration.c
ed8bd369f54d45f258de0ea9fadbed341c80f9e3
[apple/configd.git] / SystemConfiguration.fproj / VLANConfiguration.c
1 /*
2 * Copyright (c) 2003-2009 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 * November 14, 2003 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 <SystemConfiguration/SCValidation.h>
41 #include <SystemConfiguration/SCPrivate.h>
42
43 #include <ifaddrs.h>
44 #include <pthread.h>
45 #include <unistd.h>
46 #include <sys/types.h>
47 #include <sys/ioctl.h>
48 #include <sys/socket.h>
49 #include <net/ethernet.h>
50 #define KERNEL_PRIVATE
51 #include <net/if.h>
52 #include <net/if_var.h>
53 #undef KERNEL_PRIVATE
54 #include <net/if_vlan_var.h>
55 #include <net/if_types.h>
56
57 /* ---------- VLAN support ---------- */
58
59 static int
60 inet_dgram_socket()
61 {
62 int s;
63
64 s = socket(AF_INET, SOCK_DGRAM, 0);
65 if (s == -1) {
66 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
67 }
68
69 return s;
70 }
71
72
73 typedef struct {
74 CFMutableArrayRef vlans;
75 SCPreferencesRef prefs;
76 } addContext, *addContextRef;
77
78
79 static void
80 add_configured_interface(const void *key, const void *value, void *context)
81 {
82 SCNetworkInterfacePrivateRef interfacePrivate;
83 addContextRef myContext = (addContextRef)context;
84 SCVLANInterfaceRef vlan;
85 CFStringRef vlan_if = (CFStringRef)key;
86 CFDictionaryRef vlan_info = (CFDictionaryRef)value;
87 CFStringRef vlan_name;
88 CFDictionaryRef vlan_options;
89 SCNetworkInterfaceRef vlan_physical;
90 CFStringRef vlan_physical_if;
91 CFNumberRef vlan_tag;
92
93 vlan_physical_if = CFDictionaryGetValue(vlan_info, kSCPropVirtualNetworkInterfacesVLANInterface);
94 if (!isA_CFString(vlan_physical_if)) {
95 // if prefs are confused
96 return;
97 }
98
99 vlan_tag = CFDictionaryGetValue(vlan_info, kSCPropVirtualNetworkInterfacesVLANTag);
100 if (!isA_CFNumber(vlan_tag)) {
101 // if prefs are confused
102 return;
103 }
104
105 // create the VLAN interface
106 vlan = (SCVLANInterfaceRef)_SCVLANInterfaceCreatePrivate(NULL, vlan_if);
107
108 // set physical interface and tag
109 vlan_physical = _SCNetworkInterfaceCreateWithBSDName(NULL, vlan_physical_if,
110 kIncludeBondInterfaces);
111 SCVLANInterfaceSetPhysicalInterfaceAndTag(vlan, vlan_physical, vlan_tag);
112 CFRelease(vlan_physical);
113
114 // set display name
115 vlan_name = CFDictionaryGetValue(vlan_info, kSCPropUserDefinedName);
116 if (isA_CFString(vlan_name)) {
117 SCVLANInterfaceSetLocalizedDisplayName(vlan, vlan_name);
118 }
119
120 // set options
121 vlan_options = CFDictionaryGetValue(vlan_info, kSCPropVirtualNetworkInterfacesVLANOptions);
122 if (isA_CFDictionary(vlan_options)) {
123 SCVLANInterfaceSetOptions(vlan, vlan_options);
124 }
125
126 // estabish link to the stored configuration
127 interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
128 interfacePrivate->prefs = CFRetain(myContext->prefs);
129
130 CFArrayAppendValue(myContext->vlans, vlan);
131 CFRelease(vlan);
132
133 return;
134 }
135
136
137 static void
138 add_legacy_configuration(addContextRef myContext)
139 {
140 CFIndex i;
141 CFIndex n;
142 SCPreferencesRef prefs;
143 CFArrayRef vlans;
144
145 #define VLAN_PREFERENCES_ID CFSTR("VirtualNetworkInterfaces.plist")
146 #define VLAN_PREFERENCES_VLANS CFSTR("VLANs")
147 #define __kVLANInterface_interface CFSTR("interface") // e.g. vlan0, vlan1, ...
148 #define __kVLANInterface_device CFSTR("device") // e.g. en0, en1, ...
149 #define __kVLANInterface_tag CFSTR("tag") // e.g. 1 <= tag <= 4094
150 #define __kVLANInterface_options CFSTR("options") // e.g. UserDefinedName
151
152 prefs = SCPreferencesCreate(NULL, CFSTR("SCVLANInterfaceCopyAll"), VLAN_PREFERENCES_ID);
153 if (prefs == NULL) {
154 return;
155 }
156
157 vlans = SCPreferencesGetValue(prefs, VLAN_PREFERENCES_VLANS);
158 if ((vlans != NULL) && !isA_CFArray(vlans)) {
159 CFRelease(prefs); // if the prefs are confused
160 return;
161 }
162
163 n = (vlans != NULL) ? CFArrayGetCount(vlans) : 0;
164 for (i = 0; i < n; i++) {
165 CFDictionaryRef dict;
166 SCNetworkInterfacePrivateRef interfacePrivate;
167 Boolean ok;
168 CFDictionaryRef options;
169 CFStringRef path;
170 SCVLANInterfaceRef vlan;
171 CFStringRef vlan_if;
172 CFDictionaryRef vlan_dict;
173 SCNetworkInterfaceRef vlan_physical;
174 CFStringRef vlan_physical_if;
175 CFNumberRef vlan_tag;
176
177 vlan_dict = CFArrayGetValueAtIndex(vlans, i);
178 if (!isA_CFDictionary(vlan_dict)) {
179 continue; // if the prefs are confused
180 }
181
182 vlan_if = CFDictionaryGetValue(vlan_dict, __kVLANInterface_interface);
183 if (!isA_CFString(vlan_if)) {
184 continue; // if the prefs are confused
185 }
186
187 vlan_physical_if = CFDictionaryGetValue(vlan_dict, __kVLANInterface_device);
188 if (!isA_CFString(vlan_physical_if)) {
189 continue; // if the prefs are confused
190 }
191
192 vlan_tag = CFDictionaryGetValue(vlan_dict, __kVLANInterface_tag);
193 if (!isA_CFNumber(vlan_tag)) {
194 continue; // if the prefs are confused
195 }
196
197 // check if this VLAN interface has already been allocated
198 path = CFStringCreateWithFormat(NULL,
199 NULL,
200 CFSTR("/%@/%@/%@"),
201 kSCPrefVirtualNetworkInterfaces,
202 kSCNetworkInterfaceTypeVLAN,
203 vlan_if);
204 dict = SCPreferencesPathGetValue(myContext->prefs, path);
205 if (dict != NULL) {
206 // if VLAN interface name not available
207 CFRelease(path);
208 continue;
209 }
210
211 // add a placeholder for the VLAN in the stored preferences
212 dict = CFDictionaryCreate(NULL,
213 NULL, NULL, 0,
214 &kCFTypeDictionaryKeyCallBacks,
215 &kCFTypeDictionaryValueCallBacks);
216 ok = SCPreferencesPathSetValue(myContext->prefs, path, dict);
217 CFRelease(dict);
218 CFRelease(path);
219 if (!ok) {
220 // if the VLAN could not be saved
221 continue;
222 }
223
224 // create the VLAN interface
225 vlan = (SCVLANInterfaceRef)_SCVLANInterfaceCreatePrivate(NULL, vlan_if);
226
227 // estabish link to the stored configuration
228 interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
229 interfacePrivate->prefs = CFRetain(myContext->prefs);
230
231 // set the interface and tag (which updates the stored preferences)
232 vlan_physical = _SCNetworkInterfaceCreateWithBSDName(NULL, vlan_physical_if,
233 kIncludeBondInterfaces);
234 SCVLANInterfaceSetPhysicalInterfaceAndTag(vlan, vlan_physical, vlan_tag);
235 CFRelease(vlan_physical);
236
237 // set display name (which updates the stored preferences)
238 options = CFDictionaryGetValue(vlan_dict, __kVLANInterface_options);
239 if (isA_CFDictionary(options)) {
240 CFStringRef vlan_name;
241
242 vlan_name = CFDictionaryGetValue(options, CFSTR("VLAN Name"));
243 if (isA_CFString(vlan_name)) {
244 SCVLANInterfaceSetLocalizedDisplayName(vlan, vlan_name);
245 }
246 }
247
248 CFArrayAppendValue(myContext->vlans, vlan);
249 CFRelease(vlan);
250 }
251
252 CFRelease(prefs);
253 return;
254 }
255
256
257 static SCVLANInterfaceRef
258 findVLANInterfaceAndTag(SCPreferencesRef prefs, SCNetworkInterfaceRef physical, CFNumberRef tag)
259 {
260 CFIndex i;
261 CFIndex n;
262 SCVLANInterfaceRef vlan = NULL;
263 CFArrayRef vlans;
264
265 vlans = SCVLANInterfaceCopyAll(prefs);
266
267 n = CFArrayGetCount(vlans);
268 for (i = 0; i < n; i++) {
269 SCVLANInterfaceRef config_vlan;
270 SCNetworkInterfaceRef config_physical;
271 CFNumberRef config_tag;
272
273 config_vlan = CFArrayGetValueAtIndex(vlans, i);
274 config_physical = SCVLANInterfaceGetPhysicalInterface(config_vlan);
275 config_tag = SCVLANInterfaceGetTag(config_vlan);
276
277 if ((config_physical != NULL) && (config_tag != NULL)) {
278 if (!CFEqual(physical, config_physical)) {
279 // if this VLAN has a different physical interface
280 continue;
281 }
282
283 if (!CFEqual(tag, config_tag)) {
284 // if this VLAN has a different tag
285 continue;
286 }
287
288 vlan = CFRetain(config_vlan);
289 break;
290 }
291 }
292 CFRelease(vlans);
293
294 return vlan;
295 }
296
297
298 #pragma mark -
299 #pragma mark SCVLANInterface APIs
300
301
302 CFArrayRef
303 SCVLANInterfaceCopyAll(SCPreferencesRef prefs)
304 {
305 addContext context;
306 CFDictionaryRef dict;
307 CFStringRef path;
308
309 context.vlans = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
310 context.prefs = prefs;
311
312 path = CFStringCreateWithFormat(NULL,
313 NULL,
314 CFSTR("/%@/%@"),
315 kSCPrefVirtualNetworkInterfaces,
316 kSCNetworkInterfaceTypeVLAN);
317 dict = SCPreferencesPathGetValue(prefs, path);
318 if (isA_CFDictionary(dict)) {
319 CFDictionaryApplyFunction(dict, add_configured_interface, &context);
320 } else {
321 // no VLAN configuration, upgrade from legacy configuration
322 dict = CFDictionaryCreate(NULL,
323 NULL, NULL, 0,
324 &kCFTypeDictionaryKeyCallBacks,
325 &kCFTypeDictionaryValueCallBacks);
326 (void) SCPreferencesPathSetValue(prefs, path, dict);
327 CFRelease(dict);
328
329 add_legacy_configuration(&context);
330 }
331 CFRelease(path);
332
333 return context.vlans;
334 }
335
336
337 static void
338 addAvailableInterfaces(CFMutableArrayRef available, CFArrayRef interfaces,
339 CFSetRef exclude)
340 {
341 CFIndex i;
342 CFIndex n;
343
344 n = CFArrayGetCount(interfaces);
345 for (i = 0; i < n; i++) {
346 SCNetworkInterfaceRef interface;
347 SCNetworkInterfacePrivateRef interfacePrivate;
348
349 interface = CFArrayGetValueAtIndex(interfaces, i);
350 interfacePrivate = (SCNetworkInterfacePrivateRef)interface;
351
352 if (exclude != NULL
353 && CFSetContainsValue(exclude, interface)) {
354 // exclude this interface
355 continue;
356 }
357 if (interfacePrivate->supportsVLAN) {
358 // if this interface is available
359 CFArrayAppendValue(available, interface);
360 }
361 }
362
363 return;
364 }
365
366
367 CFArrayRef
368 SCVLANInterfaceCopyAvailablePhysicalInterfaces()
369 {
370 CFMutableArrayRef available;
371 CFArrayRef bond_interfaces = NULL;
372 CFMutableSetRef exclude = NULL;
373 CFArrayRef interfaces;
374 SCPreferencesRef prefs;
375
376 available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
377
378 prefs = SCPreferencesCreate(NULL, CFSTR("SCVLANInterfaceCopyAvailablePhysicalInterfaces"), NULL);
379 if (prefs != NULL) {
380 bond_interfaces = SCBondInterfaceCopyAll(prefs);
381 CFRelease(prefs);
382 if (bond_interfaces != NULL) {
383 exclude = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
384 __SCBondInterfaceListCopyMembers(bond_interfaces, exclude);
385 }
386 }
387
388 // add real interfaces that aren't part of a bond
389 interfaces = __SCNetworkInterfaceCopyAll_IONetworkInterface();
390 if (interfaces != NULL) {
391 addAvailableInterfaces(available, interfaces, exclude);
392 CFRelease(interfaces);
393 }
394
395 // add bond interfaces
396 if (bond_interfaces != NULL) {
397 addAvailableInterfaces(available, bond_interfaces, NULL);
398 CFRelease(bond_interfaces);
399 }
400 if (exclude != NULL) {
401 CFRelease(exclude);
402 }
403
404 return available;
405 }
406
407
408 CFArrayRef
409 _SCVLANInterfaceCopyActive(void)
410 {
411 struct ifaddrs *ifap;
412 struct ifaddrs *ifp;
413 int s;
414 CFMutableArrayRef vlans = NULL;
415
416 if (getifaddrs(&ifap) == -1) {
417 SCLog(TRUE, LOG_ERR, CFSTR("getifaddrs() failed: %s"), strerror(errno));
418 _SCErrorSet(kSCStatusFailed);
419 return NULL;
420 }
421
422 s = inet_dgram_socket();
423 if (s == -1) {
424 _SCErrorSet(errno);
425 goto done;
426 }
427
428 vlans = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
429
430 for (ifp = ifap; ifp != NULL; ifp = ifp->ifa_next) {
431 struct if_data *if_data;
432 struct ifreq ifr;
433 SCVLANInterfaceRef vlan;
434 CFStringRef vlan_if;
435 SCNetworkInterfaceRef vlan_physical;
436 CFStringRef vlan_physical_if;
437 CFNumberRef vlan_tag;
438 char vlr_parent[IFNAMSIZ + 1];
439 int vlr_tag;
440 struct vlanreq vreq;
441
442 if_data = (struct if_data *)ifp->ifa_data;
443 if (if_data == NULL
444 || ifp->ifa_addr->sa_family != AF_LINK
445 || if_data->ifi_type != IFT_L2VLAN) {
446 continue;
447 }
448
449 bzero(&ifr, sizeof(ifr));
450 bzero(&vreq, sizeof(vreq));
451 strncpy(ifr.ifr_name, ifp->ifa_name, sizeof(ifr.ifr_name));
452 ifr.ifr_data = (caddr_t)&vreq;
453
454 if (ioctl(s, SIOCGIFVLAN, (caddr_t)&ifr) == -1) {
455 SCLog(TRUE, LOG_ERR, CFSTR("ioctl() failed: %s"), strerror(errno));
456 CFRelease(vlans);
457 vlans = NULL;
458 _SCErrorSet(kSCStatusFailed);
459 goto done;
460 }
461
462 // create the VLAN interface
463 vlan_if = CFStringCreateWithCString(NULL, ifp->ifa_name, kCFStringEncodingASCII);
464 vlan = (SCVLANInterfaceRef)_SCVLANInterfaceCreatePrivate(NULL, vlan_if);
465 CFRelease(vlan_if);
466
467 // set the physical interface and tag
468 bzero(&vlr_parent, sizeof(vlr_parent));
469 bcopy(vreq.vlr_parent, vlr_parent, IFNAMSIZ);
470 vlan_physical_if = CFStringCreateWithCString(NULL, vlr_parent, kCFStringEncodingASCII);
471 vlan_physical = _SCNetworkInterfaceCreateWithBSDName(NULL, vlan_physical_if,
472 kIncludeBondInterfaces);
473 CFRelease(vlan_physical_if);
474
475 vlr_tag = vreq.vlr_tag;
476 vlan_tag = CFNumberCreate(NULL, kCFNumberIntType, &vlr_tag);
477
478 SCVLANInterfaceSetPhysicalInterfaceAndTag(vlan, vlan_physical, vlan_tag);
479 CFRelease(vlan_physical);
480 CFRelease(vlan_tag);
481
482 // add VLAN
483 CFArrayAppendValue(vlans, vlan);
484 CFRelease(vlan);
485 }
486
487 done :
488
489 (void) close(s);
490 freeifaddrs(ifap);
491 return vlans;
492 }
493
494
495 SCVLANInterfaceRef
496 SCVLANInterfaceCreate(SCPreferencesRef prefs, SCNetworkInterfaceRef physical, CFNumberRef tag)
497 {
498 CFAllocatorRef allocator;
499 CFIndex i;
500 SCNetworkInterfacePrivateRef interfacePrivate;
501 SCVLANInterfaceRef vlan;
502
503 if (prefs == NULL) {
504 _SCErrorSet(kSCStatusInvalidArgument);
505 return NULL;
506 }
507
508 if (!isA_SCNetworkInterface(physical)) {
509 _SCErrorSet(kSCStatusInvalidArgument);
510 return NULL;
511 }
512
513 interfacePrivate = (SCNetworkInterfacePrivateRef)physical;
514 if (!interfacePrivate->supportsVLAN) {
515 _SCErrorSet(kSCStatusInvalidArgument);
516 return NULL;
517 }
518
519 if (isA_CFNumber(tag)) {
520 int tag_val;
521
522 CFNumberGetValue(tag, kCFNumberIntType, &tag_val);
523 if ((tag_val < 1) || (tag_val > 4094)) {
524 _SCErrorSet(kSCStatusInvalidArgument);
525 return NULL;
526 }
527 } else {
528 _SCErrorSet(kSCStatusInvalidArgument);
529 return NULL;
530 }
531
532 // make sure that physical interface and tag are not used
533 vlan = findVLANInterfaceAndTag(prefs, physical, tag);
534 if (vlan != NULL) {
535 CFRelease(vlan);
536 _SCErrorSet(kSCStatusKeyExists);
537 return NULL;
538 }
539
540 allocator = CFGetAllocator(prefs);
541
542 // create a new VLAN using an unused interface name
543 for (i = 0; vlan == NULL; i++) {
544 CFDictionaryRef dict;
545 CFStringRef vlan_if;
546 Boolean ok;
547 CFStringRef path;
548
549 vlan_if = CFStringCreateWithFormat(allocator, NULL, CFSTR("vlan%d"), i);
550 path = CFStringCreateWithFormat(allocator,
551 NULL,
552 CFSTR("/%@/%@/%@"),
553 kSCPrefVirtualNetworkInterfaces,
554 kSCNetworkInterfaceTypeVLAN,
555 vlan_if);
556 dict = SCPreferencesPathGetValue(prefs, path);
557 if (dict != NULL) {
558 // if VLAN interface name not available
559 CFRelease(path);
560 CFRelease(vlan_if);
561 continue;
562 }
563
564 // add the VLAN to the stored preferences
565 dict = CFDictionaryCreate(allocator,
566 NULL, NULL, 0,
567 &kCFTypeDictionaryKeyCallBacks,
568 &kCFTypeDictionaryValueCallBacks);
569 ok = SCPreferencesPathSetValue(prefs, path, dict);
570 CFRelease(dict);
571 CFRelease(path);
572 if (!ok) {
573 // if the VLAN could not be saved
574 CFRelease(vlan_if);
575 _SCErrorSet(kSCStatusFailed);
576 break;
577 }
578
579 // create the SCVLANInterfaceRef
580 vlan = (SCVLANInterfaceRef)_SCVLANInterfaceCreatePrivate(allocator, vlan_if);
581 CFRelease(vlan_if);
582
583 // estabish link to the stored configuration
584 interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
585 interfacePrivate->prefs = CFRetain(prefs);
586
587 // set physical interface and tag
588 SCVLANInterfaceSetPhysicalInterfaceAndTag(vlan, physical, tag);
589 }
590
591 return vlan;
592 }
593
594
595 Boolean
596 SCVLANInterfaceRemove(SCVLANInterfaceRef vlan)
597 {
598 CFStringRef vlan_if;
599 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
600 Boolean ok;
601 CFStringRef path;
602
603 if (!isA_SCVLANInterface(vlan)) {
604 _SCErrorSet(kSCStatusInvalidArgument);
605 return FALSE;
606 }
607
608 if (interfacePrivate->prefs == NULL) {
609 _SCErrorSet(kSCStatusInvalidArgument);
610 return FALSE;
611 }
612
613 vlan_if = SCNetworkInterfaceGetBSDName(vlan);
614 path = CFStringCreateWithFormat(NULL,
615 NULL,
616 CFSTR("/%@/%@/%@"),
617 kSCPrefVirtualNetworkInterfaces,
618 kSCNetworkInterfaceTypeVLAN,
619 vlan_if);
620 ok = SCPreferencesPathRemoveValue(interfacePrivate->prefs, path);
621 CFRelease(path);
622
623 return ok;
624 }
625
626
627 SCNetworkInterfaceRef
628 SCVLANInterfaceGetPhysicalInterface(SCVLANInterfaceRef vlan)
629 {
630 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
631
632 if (!isA_SCVLANInterface(vlan)) {
633 _SCErrorSet(kSCStatusInvalidArgument);
634 return NULL;
635 }
636
637 return interfacePrivate->vlan.interface;
638 }
639
640
641 CFNumberRef
642 SCVLANInterfaceGetTag(SCVLANInterfaceRef vlan)
643 {
644 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
645
646 if (!isA_SCVLANInterface(vlan)) {
647 _SCErrorSet(kSCStatusInvalidArgument);
648 return NULL;
649 }
650
651 return interfacePrivate->vlan.tag;
652 }
653
654
655 CFDictionaryRef
656 SCVLANInterfaceGetOptions(SCVLANInterfaceRef vlan)
657 {
658 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
659
660 if (!isA_SCVLANInterface(vlan)) {
661 _SCErrorSet(kSCStatusInvalidArgument);
662 return NULL;
663 }
664
665 return interfacePrivate->vlan.options;
666 }
667
668
669 Boolean
670 SCVLANInterfaceSetPhysicalInterfaceAndTag(SCVLANInterfaceRef vlan, SCNetworkInterfaceRef physical, CFNumberRef tag)
671 {
672 SCNetworkInterfacePrivateRef interfacePrivate;
673 Boolean ok = TRUE;
674
675 if (!isA_SCVLANInterface(vlan)) {
676 _SCErrorSet(kSCStatusInvalidArgument);
677 return FALSE;
678 }
679
680 if (!isA_SCNetworkInterface(physical)) {
681 _SCErrorSet(kSCStatusInvalidArgument);
682 return FALSE;
683 }
684
685 interfacePrivate = (SCNetworkInterfacePrivateRef)physical;
686 if (!interfacePrivate->supportsVLAN) {
687 _SCErrorSet(kSCStatusInvalidArgument);
688 return FALSE;
689 }
690
691 if (isA_CFNumber(tag)) {
692 int tag_val;
693
694 CFNumberGetValue(tag, kCFNumberIntType, &tag_val);
695 if ((tag_val < 1) || (tag_val > 4094)) {
696 _SCErrorSet(kSCStatusInvalidArgument);
697 return FALSE;
698 }
699 } else {
700 _SCErrorSet(kSCStatusInvalidArgument);
701 return FALSE;
702 }
703
704 interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
705 if (interfacePrivate->prefs != NULL) {
706 SCVLANInterfaceRef config_vlan;
707 CFDictionaryRef dict;
708 CFMutableDictionaryRef newDict;
709 CFStringRef path;
710
711 // make sure that physical interface and tag are not used
712 config_vlan = findVLANInterfaceAndTag(interfacePrivate->prefs, physical, tag);
713 if (config_vlan != NULL) {
714 if (!CFEqual(vlan, config_vlan)) {
715 CFRelease(config_vlan);
716 _SCErrorSet(kSCStatusKeyExists);
717 return FALSE;
718 }
719 CFRelease(config_vlan);
720 }
721
722 // set interface/tag in the stored preferences
723 path = CFStringCreateWithFormat(NULL,
724 NULL,
725 CFSTR("/%@/%@/%@"),
726 kSCPrefVirtualNetworkInterfaces,
727 kSCNetworkInterfaceTypeVLAN,
728 interfacePrivate->entity_device);
729 dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
730 if (!isA_CFDictionary(dict)) {
731 // if the prefs are confused
732 CFRelease(path);
733 _SCErrorSet(kSCStatusFailed);
734 return FALSE;
735 }
736
737 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
738 CFDictionarySetValue(newDict,
739 kSCPropVirtualNetworkInterfacesVLANInterface,
740 SCNetworkInterfaceGetBSDName(physical));
741 CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesVLANTag, tag);
742 ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
743 CFRelease(newDict);
744 CFRelease(path);
745 }
746
747 if (ok) {
748 SCNetworkInterfacePrivateRef newInterface;
749 CFTypeRef save;
750
751 // set physical interface
752 newInterface = __SCNetworkInterfaceCreateCopy(NULL,
753 physical,
754 interfacePrivate->prefs,
755 interfacePrivate->serviceID);
756 save = interfacePrivate->vlan.interface;
757 interfacePrivate->vlan.interface = (SCNetworkInterfaceRef)newInterface;
758 if (save != NULL) CFRelease(save);
759
760 // set tag
761 save = interfacePrivate->vlan.tag;
762 interfacePrivate->vlan.tag = CFRetain(tag);
763 if (save != NULL) CFRelease(save);
764 }
765
766 return ok;
767 }
768
769
770 Boolean
771 SCVLANInterfaceSetLocalizedDisplayName(SCVLANInterfaceRef vlan, CFStringRef newName)
772 {
773 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
774 Boolean ok = TRUE;
775
776 if (!isA_SCVLANInterface(vlan)) {
777 _SCErrorSet(kSCStatusInvalidArgument);
778 return FALSE;
779 }
780
781 if ((newName != NULL) && !isA_CFString(newName)) {
782 _SCErrorSet(kSCStatusInvalidArgument);
783 return FALSE;
784 }
785
786 // set name in the stored preferences
787 if (interfacePrivate->prefs != NULL) {
788 CFDictionaryRef dict;
789 CFMutableDictionaryRef newDict;
790 CFStringRef path;
791
792 path = CFStringCreateWithFormat(NULL,
793 NULL,
794 CFSTR("/%@/%@/%@"),
795 kSCPrefVirtualNetworkInterfaces,
796 kSCNetworkInterfaceTypeVLAN,
797 interfacePrivate->entity_device);
798 dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
799 if (!isA_CFDictionary(dict)) {
800 // if the prefs are confused
801 CFRelease(path);
802 _SCErrorSet(kSCStatusFailed);
803 return FALSE;
804 }
805
806 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
807 if (newName != NULL) {
808 CFDictionarySetValue(newDict, kSCPropUserDefinedName, newName);
809 } else {
810 CFDictionaryRemoveValue(newDict, kSCPropUserDefinedName);
811 }
812 ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
813 CFRelease(newDict);
814 CFRelease(path);
815 }
816
817 // set name in the SCVLANInterfaceRef
818 if (ok) {
819 if (interfacePrivate->localized_name != NULL) {
820 CFRelease(interfacePrivate->localized_name);
821 interfacePrivate->localized_name = NULL;
822 }
823 if (newName != NULL) {
824 interfacePrivate->localized_name = CFStringCreateCopy(NULL, newName);
825 }
826 }
827
828 return ok;
829 }
830
831
832 Boolean
833 SCVLANInterfaceSetOptions(SCVLANInterfaceRef vlan, CFDictionaryRef newOptions)
834 {
835 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)vlan;
836 Boolean ok = TRUE;
837
838 if (!isA_SCVLANInterface(vlan)) {
839 _SCErrorSet(kSCStatusInvalidArgument);
840 return FALSE;
841 }
842
843 if ((newOptions != NULL) && !isA_CFDictionary(newOptions)) {
844 _SCErrorSet(kSCStatusInvalidArgument);
845 return FALSE;
846 }
847
848 // set options in the stored preferences
849 if (interfacePrivate->prefs != NULL) {
850 CFDictionaryRef dict;
851 CFMutableDictionaryRef newDict;
852 CFStringRef path;
853
854 path = CFStringCreateWithFormat(NULL,
855 NULL,
856 CFSTR("/%@/%@/%@"),
857 kSCPrefVirtualNetworkInterfaces,
858 kSCNetworkInterfaceTypeVLAN,
859 interfacePrivate->entity_device);
860 dict = SCPreferencesPathGetValue(interfacePrivate->prefs, path);
861 if (!isA_CFDictionary(dict)) {
862 // if the prefs are confused
863 CFRelease(path);
864 _SCErrorSet(kSCStatusFailed);
865 return FALSE;
866 }
867
868 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
869 if (newOptions != NULL) {
870 CFDictionarySetValue(newDict, kSCPropVirtualNetworkInterfacesVLANOptions, newOptions);
871 } else {
872 CFDictionaryRemoveValue(newDict, kSCPropVirtualNetworkInterfacesVLANOptions);
873 }
874 ok = SCPreferencesPathSetValue(interfacePrivate->prefs, path, newDict);
875 CFRelease(newDict);
876 CFRelease(path);
877 }
878
879 // set options in the SCVLANInterfaceRef
880 if (ok) {
881 if (interfacePrivate->vlan.options != NULL) {
882 CFRelease(interfacePrivate->vlan.options);
883 interfacePrivate->vlan.options = NULL;
884 }
885 if (newOptions != NULL) {
886 interfacePrivate->vlan.options = CFDictionaryCreateCopy(NULL, newOptions);
887 }
888 }
889
890 return ok;
891 }
892
893
894 #pragma mark -
895 #pragma mark SCVLANInterface management
896
897
898 static Boolean
899 __vlan_set(int s, CFStringRef interface_if, CFStringRef physical_if, CFNumberRef tag)
900 {
901 struct ifreq ifr;
902 int tag_val;
903 struct vlanreq vreq;
904
905 bzero(&ifr, sizeof(ifr));
906 bzero(&vreq, sizeof(vreq));
907
908 // interface
909 (void) _SC_cfstring_to_cstring(interface_if,
910 ifr.ifr_name,
911 sizeof(ifr.ifr_name),
912 kCFStringEncodingASCII);
913 ifr.ifr_data = (caddr_t)&vreq;
914
915 // physical interface
916 (void) _SC_cfstring_to_cstring(physical_if,
917 vreq.vlr_parent,
918 sizeof(vreq.vlr_parent),
919 kCFStringEncodingASCII);
920
921 // tag
922 CFNumberGetValue(tag, kCFNumberIntType, &tag_val);
923 vreq.vlr_tag = tag_val;
924
925 // update physical interface and tag
926 if (ioctl(s, SIOCSIFVLAN, (caddr_t)&ifr) == -1) {
927 SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCSIFVLAN) failed: %s"), strerror(errno));
928 _SCErrorSet(kSCStatusFailed);
929 return FALSE;
930 }
931
932 return TRUE;
933 }
934
935
936 static Boolean
937 __vlan_clear(int s, CFStringRef interface_if)
938 {
939 struct ifreq ifr;
940 struct vlanreq vreq;
941
942 bzero(&ifr, sizeof(ifr));
943 bzero(&vreq, sizeof(vreq));
944
945 // interface
946 (void) _SC_cfstring_to_cstring(interface_if,
947 ifr.ifr_name,
948 sizeof(ifr.ifr_name),
949 kCFStringEncodingASCII);
950 ifr.ifr_data = (caddr_t)&vreq;
951
952 // clear physical interface
953 bzero(&vreq.vlr_parent, sizeof(vreq.vlr_parent));
954
955 // clear tag
956 vreq.vlr_tag = 0;
957
958 // update physical interface and tag
959 if (ioctl(s, SIOCSIFVLAN, (caddr_t)&ifr) == -1) {
960 SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCSIFVLAN) failed: %s"), strerror(errno));
961 _SCErrorSet(kSCStatusFailed);
962 return FALSE;
963 }
964
965 return TRUE;
966 }
967
968
969 Boolean
970 _SCVLANInterfaceUpdateConfiguration(SCPreferencesRef prefs)
971 {
972 CFArrayRef active = NULL;
973 CFArrayRef config = NULL;
974 CFMutableDictionaryRef devices = NULL;
975 CFIndex i;
976 CFIndex nActive;
977 CFIndex nConfig;
978 Boolean ok = TRUE;
979 int s = -1;
980
981 if (prefs == NULL) {
982 _SCErrorSet(kSCStatusInvalidArgument);
983 return FALSE;
984 }
985
986 /* configured VLANs */
987 config = SCVLANInterfaceCopyAll(prefs);
988 nConfig = CFArrayGetCount(config);
989
990 /* physical interfaces */
991 devices = CFDictionaryCreateMutable(NULL,
992 0,
993 &kCFTypeDictionaryKeyCallBacks,
994 &kCFTypeDictionaryValueCallBacks);
995
996 /* active VLANs */
997 active = _SCVLANInterfaceCopyActive();
998 nActive = CFArrayGetCount(active);
999
1000 /* remove any no-longer-configured VLAN interfaces */
1001 for (i = 0; i < nActive; i++) {
1002 SCVLANInterfaceRef a_vlan;
1003 CFStringRef a_vlan_if;
1004 CFIndex j;
1005 Boolean found = FALSE;
1006
1007 a_vlan = CFArrayGetValueAtIndex(active, i);
1008 a_vlan_if = SCNetworkInterfaceGetBSDName(a_vlan);
1009
1010 for (j = 0; j < nConfig; j++) {
1011 SCVLANInterfaceRef c_vlan;
1012 CFStringRef c_vlan_if;
1013
1014 c_vlan = CFArrayGetValueAtIndex(config, j);
1015 c_vlan_if = SCNetworkInterfaceGetBSDName(c_vlan);
1016
1017 if (CFEqual(a_vlan_if, c_vlan_if)) {
1018 found = TRUE;
1019 break;
1020 }
1021 }
1022
1023 if (!found) {
1024 // remove VLAN interface
1025 if (s == -1) {
1026 s = inet_dgram_socket();
1027 if (s == -1) {
1028 _SCErrorSet(errno);
1029 ok = FALSE;
1030 goto done;
1031 }
1032 }
1033 if (!__destroyInterface(s, a_vlan_if)) {
1034 ok = FALSE;
1035 _SCErrorSet(errno);
1036 }
1037 }
1038 }
1039
1040 /* create (and update) configured VLAN interfaces */
1041 for (i = 0; i < nConfig; i++) {
1042 SCVLANInterfaceRef c_vlan;
1043 CFStringRef c_vlan_if;
1044 SCNetworkInterfaceRef c_vlan_physical;
1045 Boolean found = FALSE;
1046 CFIndex j;
1047 CFBooleanRef supported;
1048
1049 c_vlan = CFArrayGetValueAtIndex(config, i);
1050 c_vlan_if = SCNetworkInterfaceGetBSDName(c_vlan);
1051 c_vlan_physical = SCVLANInterfaceGetPhysicalInterface(c_vlan);
1052
1053 if (c_vlan_physical == NULL) {
1054 continue;
1055 }
1056 // determine if the physical interface supports VLANs
1057 supported = CFDictionaryGetValue(devices, c_vlan_physical);
1058 if (supported == NULL) {
1059 SCNetworkInterfacePrivateRef c_vlan_physicalPrivate = (SCNetworkInterfacePrivateRef)c_vlan_physical;
1060
1061 supported = c_vlan_physicalPrivate->supportsVLAN ? kCFBooleanTrue
1062 : kCFBooleanFalse;
1063 CFDictionaryAddValue(devices, c_vlan_physical, supported);
1064 }
1065
1066 for (j = 0; j < nActive; j++) {
1067 SCVLANInterfaceRef a_vlan;
1068 CFStringRef a_vlan_if;
1069
1070 a_vlan = CFArrayGetValueAtIndex(active, j);
1071 a_vlan_if = SCNetworkInterfaceGetBSDName(a_vlan);
1072
1073 if (CFEqual(c_vlan_if, a_vlan_if)) {
1074 if (!CFEqual(c_vlan, a_vlan)) {
1075 // update VLAN interface
1076 if (s == -1) {
1077 s = inet_dgram_socket();
1078 if (s == -1) {
1079 _SCErrorSet(errno);
1080 ok = FALSE;
1081 goto done;
1082 }
1083 }
1084
1085 if (!CFBooleanGetValue(supported)
1086 || !__vlan_clear(s, c_vlan_if)
1087 || !__vlan_set(s, c_vlan_if,
1088 SCNetworkInterfaceGetBSDName(c_vlan_physical),
1089 SCVLANInterfaceGetTag(c_vlan))) {
1090 // something went wrong, try to blow the VLAN away
1091 if (!CFBooleanGetValue(supported)) {
1092 _SCErrorSet(kSCStatusFailed);
1093 }
1094 (void)__destroyInterface(s, c_vlan_if);
1095 ok = FALSE;
1096 }
1097 }
1098
1099 found = TRUE;
1100 break;
1101 }
1102 }
1103
1104 if (!found && CFBooleanGetValue(supported)) {
1105 // if the physical interface supports VLANs, add new interface
1106 Boolean created;
1107
1108 if (s == -1) {
1109 s = inet_dgram_socket();
1110 if (s == -1) {
1111 _SCErrorSet(errno);
1112 ok = FALSE;
1113 goto done;
1114 }
1115 }
1116
1117 created = __createInterface(s, c_vlan_if);
1118 if (!created
1119 || !__vlan_set(s,
1120 c_vlan_if,
1121 SCNetworkInterfaceGetBSDName(c_vlan_physical),
1122 SCVLANInterfaceGetTag(c_vlan))) {
1123 if (created) {
1124 // something went wrong, try to blow the VLAN away
1125 (void)__destroyInterface(s, c_vlan_if);
1126 } else {
1127 _SCErrorSet(errno);
1128 }
1129 ok = FALSE;
1130 }
1131 }
1132
1133 }
1134
1135 done :
1136
1137 if (active) CFRelease(active);
1138 if (config) CFRelease(config);
1139 if (devices) CFRelease(devices);
1140 if (s != -1) (void) close(s);
1141
1142 return ok;
1143 }