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