1/*
2 * Copyright (c) 2005-2007 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 *  BLGetIOServiceForPreferredNetworkInterface.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 11/14/05.
28 *  Copyright 2005-2007 Apple Inc. All Rights Reserved.
29 *
30 */
31
32#import <mach/mach_error.h>
33
34#import <IOKit/IOKitLib.h>
35#import <IOKit/IOKitKeys.h>
36#import <IOKit/network/IONetworkInterface.h>
37#import <IOKit/network/IONetworkController.h>
38#import <IOKit/network/IONetworkMedium.h>
39#import <IOKit/IOBSD.h>
40
41#include <CoreFoundation/CoreFoundation.h>
42
43#include <sys/socket.h>
44#include <net/if.h>
45
46#include "bless.h"
47#include "bless_private.h"
48
49extern bool isInterfaceLinkUp(BLContextPtr context,
50                              io_service_t serv);
51
52static io_service_t getLinkUpInterface(BLContextPtr context,
53                                       io_iterator_t iterator);
54
55/* Algorithm:
56    1) Search for IONetworkInterface that are built-in and have
57        an active link
58    2) Rank those according to order seen, or IOPrimaryInterface
59*/
60int BLGetPreferredNetworkInterface(BLContextPtr context,
61                                char *ifname)
62{
63
64    io_service_t    interface = IO_OBJECT_NULL;
65    kern_return_t   kret;
66    CFMutableDictionaryRef  matchingDict = NULL, propDict = NULL;
67    io_iterator_t   iterator = IO_OBJECT_NULL;
68
69    ifname[0] = '\0';
70
71    matchingDict = IOServiceMatching(kIONetworkInterfaceClass);
72    propDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
73                                         &kCFTypeDictionaryKeyCallBacks,
74                                         &kCFTypeDictionaryValueCallBacks);
75
76    CFDictionaryAddValue(propDict, CFSTR(kIOBuiltin), kCFBooleanTrue);
77    CFDictionaryAddValue(propDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
78    CFDictionaryAddValue(matchingDict, CFSTR(kIOPropertyMatchKey), propDict);
79    CFRelease(propDict);
80
81    kret = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict,
82                                            &iterator);
83    if(kret) {
84        contextprintf(context, kBLLogLevelError, "Could not get interface iterator\n");
85        return 1;
86    }
87
88    interface = getLinkUpInterface(context, iterator);
89    IOObjectRelease(iterator);
90
91    if(interface == IO_OBJECT_NULL) {
92
93        matchingDict = IOServiceMatching(kIONetworkInterfaceClass);
94        propDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
95                                             &kCFTypeDictionaryKeyCallBacks,
96                                             &kCFTypeDictionaryValueCallBacks);
97
98        CFDictionaryAddValue(propDict, CFSTR(kIOBuiltin), kCFBooleanTrue);
99        CFDictionaryAddValue(matchingDict, CFSTR(kIOPropertyMatchKey), propDict);
100        CFRelease(propDict);
101
102        kret = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict,
103                                            &iterator);
104        if(kret) {
105            contextprintf(context, kBLLogLevelError, "Could not get interface iterator\n");
106            return 1;
107        }
108
109        interface = getLinkUpInterface(context, iterator);
110        IOObjectRelease(iterator);
111    }
112
113	if(interface == IO_OBJECT_NULL) {
114
115        matchingDict = IOServiceMatching(kIONetworkInterfaceClass);
116        propDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
117                                             &kCFTypeDictionaryKeyCallBacks,
118                                             &kCFTypeDictionaryValueCallBacks);
119
120        CFDictionaryAddValue(matchingDict, CFSTR(kIOPropertyMatchKey), propDict);
121        CFRelease(propDict);
122
123        kret = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict,
124                                            &iterator);
125        if(kret) {
126            contextprintf(context, kBLLogLevelError, "Could not get interface iterator\n");
127            return 1;
128        }
129
130        interface = getLinkUpInterface(context, iterator);
131        IOObjectRelease(iterator);
132    }
133
134    if(interface != IO_OBJECT_NULL) {
135        CFStringRef name;
136
137        name = IORegistryEntryCreateCFProperty(interface, CFSTR(kIOBSDNameKey),
138                                               kCFAllocatorDefault,
139                                               0);
140
141        if(name == NULL || CFGetTypeID(name) != CFStringGetTypeID()) {
142            if(name) CFRelease(name);
143            IOObjectRelease(interface);
144
145            contextprintf(context, kBLLogLevelError, "Preferred interface does not have a BSD name\n");
146            return 2;
147        }
148
149        if(!CFStringGetCString(name, ifname, IF_NAMESIZE, kCFStringEncodingUTF8)) {
150            CFRelease(name);
151            IOObjectRelease(interface);
152
153            contextprintf(context, kBLLogLevelError, "Could not get BSD name\n");
154            return 3;
155        }
156
157        CFRelease(name);
158        IOObjectRelease(interface);
159
160        contextprintf(context, kBLLogLevelVerbose, "Found primary interface: %s\n", ifname);
161
162        return 0;
163    }
164
165    return 2;
166}
167
168static io_service_t getLinkUpInterface(BLContextPtr context,
169                                       io_iterator_t iterator)
170{
171    io_service_t    serv;
172    kern_return_t   kret;
173
174    if(!IOIteratorIsValid(iterator))
175        IOIteratorReset(iterator);
176
177    while((serv = IOIteratorNext(iterator))) {
178        io_string_t   path;
179        bool        hasLink;
180
181        hasLink = isInterfaceLinkUp(context, serv);
182
183        kret = IORegistryEntryGetPath(serv, kIOServicePlane, path);
184        if(kret) {
185            strlcpy(path, "<unknown>", sizeof path);
186        }
187
188        contextprintf(context, kBLLogLevelVerbose, "Interface at %s %s an active link\n",
189                      path,
190                      hasLink ? "has" : "does not have");
191
192        if(hasLink) {
193            return serv;
194        } else {
195            IOObjectRelease(serv);
196        }
197    }
198
199    return IO_OBJECT_NULL;
200}
201
202