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 *  BLInterpretEFIXMLRepresentationAsNetworkPath.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 12/2/05.
28 *  Copyright 2005-2007 Apple Inc. All Rights Reserved.
29 *
30 */
31
32#include <IOKit/IOKitLib.h>
33#include <IOKit/IOKitKeys.h>
34#include <IOKit/IOBSD.h>
35#include <IOKit/IOCFUnserialize.h>
36#include <IOKit/network/IONetworkInterface.h>
37#include <IOKit/network/IONetworkController.h>
38
39#include <sys/socket.h>
40#include <net/if.h>
41#include <arpa/nameser.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44
45#include "bless.h"
46#include "bless_private.h"
47
48int BLInterpretEFIXMLRepresentationAsNetworkPath(BLContextPtr context,
49                                                 CFStringRef xmlString,
50                                                 BLNetBootProtocolType *protocol,
51                                                 char *interface,
52                                                 char *host,
53                                                 char *path)
54{
55    CFArrayRef  efiArray = NULL;
56    CFIndex     count, i, foundinterfaceindex, foundserverindex;
57    int         foundmac = 0, foundinterface = 0, foundserver = 0, foundPXE = 0;
58    CFDataRef   macAddress = 0;
59    char        buffer[1024];
60
61    io_iterator_t   iter;
62    io_service_t    service;
63    kern_return_t   kret;
64
65
66    if(!CFStringGetCString(xmlString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
67        return 1;
68    }
69
70    efiArray = IOCFUnserialize(buffer,
71                               kCFAllocatorDefault,
72                               0,
73                               NULL);
74    if(efiArray == NULL) {
75        contextprintf(context, kBLLogLevelError, "Could not unserialize string\n");
76        return 2;
77    }
78
79    if(CFGetTypeID(efiArray) != CFArrayGetTypeID()) {
80        CFRelease(efiArray);
81        contextprintf(context, kBLLogLevelError, "Bad type in XML string\n");
82        return 2;
83    }
84
85    // we do a first pass to validate types, and check for the BLMacAddress hint
86    count = CFArrayGetCount(efiArray);
87    foundinterfaceindex = -1;
88    for(i=0; i < count; i++) {
89        CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i);
90
91        if(CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
92            CFRelease(efiArray);
93            contextprintf(context, kBLLogLevelError, "Bad type in XML string\n");
94            return 2;
95        }
96
97        if(!foundmac) {
98            macAddress = CFDictionaryGetValue(dict, CFSTR("BLMACAddress"));
99            if(macAddress) {
100                if(CFGetTypeID(macAddress) == CFDataGetTypeID()) {
101                    // we found it at this index
102                    foundmac = 1;
103                    foundinterfaceindex = i;
104                }
105            }
106        }
107
108        if(!foundPXE) {
109            CFStringRef compType;
110            compType = CFDictionaryGetValue(dict, CFSTR("IOEFIDevicePathType"));
111            if(compType && CFEqual(compType, CFSTR("MessagingNetbootProtocol"))) {
112                CFStringRef	guid;
113
114                guid = CFDictionaryGetValue(dict, CFSTR("Protocol"));
115                if(guid && CFEqual(guid, CFSTR("FE3913DB-9AEE-4E40-A294-ABBE93A1A4B7"))) {
116                    foundPXE = 1;
117                }
118            }
119
120        }
121
122    }
123
124    if(foundmac) {
125        const unsigned char *macbytes = CFDataGetBytePtr(macAddress);
126        CFIndex        maclength = CFDataGetLength(macAddress);
127
128        contextprintf(context, kBLLogLevelVerbose, "Found MAC address: ");
129        for(i=0; i < maclength; i++) {
130               contextprintf(context, kBLLogLevelVerbose, "%02x%s",
131                             macbytes[i],
132                             i < maclength-1 ? ":" : "");
133        }
134        contextprintf(context, kBLLogLevelVerbose, "\n");
135
136        // search for all network interfaces that have this mac address
137
138        kret = IOServiceGetMatchingServices(kIOMasterPortDefault,
139                                            IOServiceMatching(kIONetworkInterfaceClass),
140                                            &iter);
141        if(kret == 0) {
142            while ((service = IOIteratorNext(iter))) {
143                CFDataRef checkMac;
144
145                checkMac = IORegistryEntrySearchCFProperty(service, kIOServicePlane,
146                                                           CFSTR(kIOMACAddress),
147                                                           kCFAllocatorDefault,
148                                                           kIORegistryIterateRecursively|kIORegistryIterateParents);
149                if(checkMac) {
150
151                    // see if it matches ours
152                    if(CFGetTypeID(checkMac) == CFDataGetTypeID()
153                       && CFEqual(checkMac, macAddress)) {
154                        CFStringRef name = IORegistryEntryCreateCFProperty(service,
155                                                                           CFSTR(kIOBSDNameKey),
156                                                                           kCFAllocatorDefault,
157                                                                           0);
158                        if(name && CFGetTypeID(name) == CFStringGetTypeID()) {
159                            CFStringGetCString(name, interface, IF_NAMESIZE,kCFStringEncodingUTF8);
160                            contextprintf(context, kBLLogLevelVerbose, "Found interface: %s\n", interface);
161                            foundinterface = 1;
162                        }
163
164                        if(name) CFRelease(name);
165                    }
166                    CFRelease(checkMac);
167                }
168
169
170                IOObjectRelease(service);
171            }
172            IOObjectRelease(iter);
173
174        }
175    }
176
177    if(!foundinterface) {
178        foundinterfaceindex = -1; // reset, in case BLMacAddress was a false positive
179
180        // try to match against any IOMatch
181        for(i=0; i < count; i++) {
182            CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i);
183            CFDictionaryRef iomatch = CFDictionaryGetValue(dict, CFSTR("IOMatch"));
184
185            if(iomatch && CFGetTypeID(iomatch) == CFDictionaryGetTypeID()) {
186
187				CFRetain(iomatch);
188                service = IOServiceGetMatchingService(kIOMasterPortDefault,iomatch);
189                if(service != IO_OBJECT_NULL) {
190
191                    CFStringRef name = NULL;
192
193                    if(!IOObjectConformsTo(service,kIONetworkInterfaceClass)) {
194                        contextprintf(context, kBLLogLevelVerbose, "found service but it is not a network interface\n");
195                    } else {
196
197                        name = IORegistryEntryCreateCFProperty(service,
198                                                                           CFSTR(kIOBSDNameKey),
199                                                                           kCFAllocatorDefault,
200                                                                           0);
201                        if(name && CFGetTypeID(name) == CFStringGetTypeID()) {
202                            CFStringGetCString(name, interface, IF_NAMESIZE,kCFStringEncodingUTF8);
203                            contextprintf(context, kBLLogLevelVerbose, "Found interface: %s\n", interface);
204                            foundinterface = 1;
205                            foundinterfaceindex = i;
206                        }
207
208                        if(name) CFRelease(name);
209                    }
210
211                    IOObjectRelease(service);
212                }
213            }
214
215        }
216
217    }
218
219    if(!foundinterface) {
220        contextprintf(context, kBLLogLevelVerbose, "Could not find network interface.\n");
221        return 3;
222    }
223
224    // now that we know which entry had the interface, start searching from then on for
225    // a potential remove server. If not, this is broadcast mode
226    foundserverindex = -1;
227    for(i=foundinterfaceindex; i < count; i++) {
228        CFDictionaryRef dict = CFArrayGetValueAtIndex(efiArray, i);
229        CFStringRef devpathtype = CFDictionaryGetValue(dict, CFSTR("IOEFIDevicePathType"));
230
231        if(devpathtype && CFEqual(devpathtype, CFSTR("MessagingIPv4"))) {
232            CFStringRef remoteIP = CFDictionaryGetValue(dict, CFSTR("RemoteIpAddress"));
233
234            if(remoteIP && CFGetTypeID(remoteIP) == CFStringGetTypeID()) {
235                CFStringGetCString(remoteIP, host, NS_MAXDNAME,kCFStringEncodingUTF8);
236                contextprintf(context, kBLLogLevelVerbose, "Found server: %s\n", host);
237                foundserver = 1;
238                foundserverindex = i;
239                break;
240            } else {
241                contextprintf(context, kBLLogLevelVerbose, "Malformed MessagingIPv4 entry. Ignoring...\n");
242            }
243
244        }
245    }
246
247    if(foundserver) {
248
249        path[0] = '\0';
250    } else {
251        // if no server, there can't be a TFTP path
252
253        strlcpy(host, "255.255.255.255", NS_MAXDNAME);
254        path[0] = '\0';
255    }
256
257    if(foundPXE) {
258        *protocol = kBLNetBootProtocol_PXE;
259    } else {
260        *protocol = kBLNetBootProtocol_BSDP;
261    }
262
263    return 0;
264}
265