/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include "IOUserEthernetController.h" #include #include #include #include #include #include #include #include #include #include #include #define EN_CONTROL_NAME "com.apple.userspace_ethernet" #define EN_SOCKOPT_CONNECT 12 static IOEthernetControllerRef __IOEthernetControllerCreate( CFAllocatorRef allocator, CFAllocatorContext * context __unused); static void __IOEthernetControllerRelease( CFTypeRef object ); static void __IOEthernetControllerRegister(void); static void __IOEthernetControllerMachPortCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info); static void __IOEthernetControllerSetState(IOEthernetControllerRef controller, Boolean state); static void __IOEthernetControllerSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); static int __connect_to_kernel(void *cookie, size_t cookielen) { int sock = -1; int result = 0; int rcvsize = 64 * 1024; // Create a socket sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); if (sock == -1) return sock; // Lookup the control ID for the given control name struct ctl_info ctlinfo; bzero(&ctlinfo, sizeof(ctlinfo)); strlcpy(ctlinfo.ctl_name, EN_CONTROL_NAME, sizeof(ctlinfo.ctl_name)); result = ioctl(sock, CTLIOCGINFO, &ctlinfo); if (result == -1) { close(sock); return -1; } // Connect to the kernel control struct sockaddr_ctl sac; bzero(&sac, sizeof(sac)); sac.sc_len = sizeof(sac); sac.sc_family = AF_SYSTEM; sac.ss_sysaddr = AF_SYS_CONTROL; sac.sc_id = ctlinfo.ctl_id; sac.sc_unit = 0; result = connect(sock, (struct sockaddr*)&sac, sizeof(sac)); if (result == -1) { close(sock); return -1; } // Bind the kernel control to an ethernet interface using the socket option result = setsockopt(sock, SYSPROTO_CONTROL, EN_SOCKOPT_CONNECT, cookie, cookielen); if (result == -1) { close(sock); return -1; } // increase the receive buffer size result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsize, sizeof(rcvsize)); if (result != 0) { close(sock); return -1; } // make it non blocking result = fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); if (result == -1) { close(sock); return -1; } return sock; } CFTypeRef kIOEthernetHardwareAddress = CFSTR(kIOUserEthernetHardwareAddressKey); typedef struct __IOEthernetController { CFRuntimeBase cfBase; // base CFType information io_service_t service; io_connect_t connect; CFDictionaryRef properties; io_object_t interface; CFRunLoopRef runLoop; CFStringRef runLoopMode; CFMachPortRef port; CFRunLoopSourceRef portSource; CFSocketRef socket; CFRunLoopSourceRef socketSource; Boolean enable; uint64_t cookie; struct { struct { IOEthernetControllerCallback callback; void * refcon; } enable, disable, packet; } callbacks; } __IOEthernetController, *__IOEthernetControllerRef; static const CFRuntimeClass __IOEthernetControllerClass = { 0, // version "IOEthernetController", // className NULL, // init NULL, // copy __IOEthernetControllerRelease, // finalize NULL, // equal NULL, // hash NULL, // copyFormattingDesc NULL, NULL }; static pthread_once_t __controllerTypeInit = PTHREAD_ONCE_INIT; static CFTypeID __kIOEthernetControllerTypeID = _kCFRuntimeNotATypeID; static mach_port_t __masterPort = MACH_PORT_NULL; //------------------------------------------------------------------------------ // __IOEthernetControllerRegister //------------------------------------------------------------------------------ void __IOEthernetControllerRegister(void) { __kIOEthernetControllerTypeID = _CFRuntimeRegisterClass(&__IOEthernetControllerClass); IOMasterPort(bootstrap_port, &__masterPort); } //------------------------------------------------------------------------------ // __IOEthernetControllerCreate //------------------------------------------------------------------------------ IOEthernetControllerRef __IOEthernetControllerCreate( CFAllocatorRef allocator, CFAllocatorContext * context __unused) { IOEthernetControllerRef controller = NULL; void * offset = NULL; uint32_t size; /* allocate service */ size = sizeof(__IOEthernetController) - sizeof(CFRuntimeBase); controller = (IOEthernetControllerRef)_CFRuntimeCreateInstance(allocator, IOEthernetControllerGetTypeID(), size, NULL); if (!controller) return NULL; offset = controller; bzero(offset + sizeof(CFRuntimeBase), size); return controller; } //------------------------------------------------------------------------------ // __IOEthernetControllerRelease //------------------------------------------------------------------------------ void __IOEthernetControllerRelease( CFTypeRef object ) { IOEthernetControllerRef controller = (IOEthernetControllerRef)object; if ( controller->properties ) { CFRelease(controller->properties); controller->properties = NULL; } if ( controller->socket ) { CFSocketInvalidate(controller->socket); CFRelease(controller->socket); controller->socket = NULL; } if ( controller->socketSource ) { CFRelease(controller->socketSource); controller->socketSource = NULL; } if ( controller->connect ) { IOServiceClose(controller->connect); controller->connect = 0; } if ( controller->service ) { IOObjectRelease(controller->service); controller->service = 0; } if ( controller->interface ) { IOObjectRelease(controller->interface); controller->interface = 0; } if ( controller->port ) { CFRelease(controller->port); controller->port = NULL; } if ( controller->portSource ) { CFRelease(controller->portSource); controller->portSource = NULL; } } //------------------------------------------------------------------------------ // IOEthernetControllerGetTypeID //------------------------------------------------------------------------------ CFTypeID IOEthernetControllerGetTypeID(void) { if ( _kCFRuntimeNotATypeID == __kIOEthernetControllerTypeID ) pthread_once(&__controllerTypeInit, __IOEthernetControllerRegister); return __kIOEthernetControllerTypeID; } //------------------------------------------------------------------------------ // IOEthernetControllerCreate //------------------------------------------------------------------------------ IOEthernetControllerRef IOEthernetControllerCreate( CFAllocatorRef allocator, CFDictionaryRef properties) { IOEthernetControllerRef controller; CFDataRef data; kern_return_t kr; do { if ( !properties ) break; controller = __IOEthernetControllerCreate(allocator, NULL); if ( !controller ) break; controller->service = IOServiceGetMatchingService(__masterPort, IOServiceMatching(kIOUserEthernetResourceKey)); if ( controller->service == MACH_PORT_NULL ) return NULL; kr = IOServiceOpen(controller->service, mach_task_self(), kIOUserEthernetResourceUserClientTypeController, &controller->connect); if ( kr != KERN_SUCCESS ) break; // Establish the notification port first CFMachPortContext portContext = {0, controller, NULL, NULL, NULL}; controller->port = CFMachPortCreate(kCFAllocatorDefault, __IOEthernetControllerMachPortCallBack, &portContext, NULL); if (!controller->port) break; kr = IOConnectSetNotificationPort(controller->connect, kIOUserEthernetResourceUserClientPortTypeState, CFMachPortGetPort(controller->port), 0); if ( kr != KERN_SUCCESS ) break; controller->portSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, controller->port, 0); if ( !controller->portSource ) break; // Create and start the little bugger with properties data = IOCFSerialize(properties, 0); if ( !data ) break; uint32_t count = 1; kr = IOConnectCallMethod( controller->connect, kIOUserEthernetResourceUserClientMethodCreate, NULL, 0, CFDataGetBytePtr(data), CFDataGetLength(data), &controller->cookie, &count, NULL, NULL); CFRelease(data); if ( kr != KERN_SUCCESS ) break; // connect and bind to the data socket int sock = __connect_to_kernel(&controller->cookie, sizeof(controller->cookie)); if ( sock == -1 ) break; CFSocketContext sockContext = {0, controller, NULL, NULL, NULL}; controller->socket = CFSocketCreateWithNative(kCFAllocatorDefault, sock, kCFSocketReadCallBack, __IOEthernetControllerSocketCallback, &sockContext); if ( !controller->socket ) break; controller->socketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, controller->socket, 0); if ( !controller->socketSource ) break; return controller; } while ( FALSE ); if ( controller ) CFRelease(controller); return NULL; } //------------------------------------------------------------------------------ // IOEthernetControllerGetIONetworkInterfaceObject //------------------------------------------------------------------------------ io_object_t IOEthernetControllerGetIONetworkInterfaceObject(IOEthernetControllerRef controller) { if ( !controller->interface ) { CFMutableDictionaryRef matching = NULL; do { CFDictionaryRef propertyMatch = NULL; CFStringRef key = NULL; CFNumberRef number = NULL; mach_port_t masterPort = MACH_PORT_NULL; kern_return_t kr; kr = IOMasterPort(MACH_PORT_NULL, &masterPort); if ( KERN_SUCCESS != kr ) break; if ( !masterPort ) break; matching = IOServiceMatching(kIONetworkInterfaceClass); if ( !matching ) break; number = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt64Type,&controller->cookie); if ( !number ) break; key = CFSTR(kIOUserEthernetCookieKey); propertyMatch = CFDictionaryCreate( kCFAllocatorDefault, (const void **) &key, (const void **) &number, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); CFRelease(number); if ( !propertyMatch ) break; CFDictionarySetValue(matching, CFSTR(kIOPropertyMatchKey), propertyMatch); CFRelease(propertyMatch); controller->interface = IOServiceGetMatchingService(masterPort,matching); matching = NULL; // consumed by IOServiceAddMatchingNotification } while (FALSE); if ( matching ) CFRelease(matching); } return controller->interface; } //------------------------------------------------------------------------------ // IOEthernetControllerSetLinkStatus //------------------------------------------------------------------------------ IOReturn IOEthernetControllerSetLinkStatus( IOEthernetControllerRef controller, Boolean state) { uint64_t param = state; return IOConnectCallScalarMethod(controller->connect, kIOUserEthernetResourceUserClientMethodSetLinkStatus, ¶m, 1, NULL, NULL); } //------------------------------------------------------------------------------ // __IOEthernetControllerSocketCallback //------------------------------------------------------------------------------ void __IOEthernetControllerSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address __unused, const void *data __unused, void *info) { IOEthernetControllerRef controller = (IOEthernetControllerRef)info; if ( controller == NULL ) return; if ( controller->socket != s ) return; if ( kCFSocketReadCallBack != type ) return; if ( controller->callbacks.packet.callback ) (*controller->callbacks.packet.callback)(controller, controller->callbacks.packet.refcon); } //------------------------------------------------------------------------------ // IOEthernetControllerReadPacket //------------------------------------------------------------------------------ CFIndex IOEthernetControllerReadPacket( IOEthernetControllerRef controller, uint8_t * buffer, CFIndex bufferLength) { return recv(CFSocketGetNative(controller->socket), (void *)buffer, bufferLength, 0); } //------------------------------------------------------------------------------ // IOEthernetControllerWritePacket //------------------------------------------------------------------------------ IOReturn IOEthernetControllerWritePacket( IOEthernetControllerRef controller, const uint8_t * buffer, CFIndex bufferLength) { CFDataRef data = NULL; CFSocketError error = kCFSocketError; data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, bufferLength, kCFAllocatorNull); if ( data ) { error = CFSocketSendData(controller->socket, NULL, data, 0); CFRelease(data); } return (error == kCFSocketSuccess) ? kIOReturnSuccess : kIOReturnError; } //------------------------------------------------------------------------------ // IOEthernetControllerScheduleWithRunLoop //------------------------------------------------------------------------------ void IOEthernetControllerScheduleWithRunLoop( IOEthernetControllerRef controller, CFRunLoopRef runLoop, CFStringRef runLoopMode) { CFRunLoopAddSource(runLoop, controller->portSource, runLoopMode); CFRunLoopAddSource(runLoop, controller->socketSource, runLoopMode); controller->runLoop = runLoop; controller->runLoopMode = runLoopMode; } //------------------------------------------------------------------------------ // IOEthernetControllerUnscheduleFromRunLoop //------------------------------------------------------------------------------ void IOEthernetControllerUnscheduleFromRunLoop( IOEthernetControllerRef controller, CFRunLoopRef runLoop, CFStringRef runLoopMode) { CFRunLoopRemoveSource(runLoop, controller->socketSource, runLoopMode); CFRunLoopRemoveSource(runLoop, controller->portSource, runLoopMode); controller->runLoop = NULL; controller->runLoopMode = NULL; } //------------------------------------------------------------------------------ // __IOEthernetControllerMachPortCallBack //------------------------------------------------------------------------------ void __IOEthernetControllerMachPortCallBack(CFMachPortRef port __unused, void *msg __unused, CFIndex size __unused, void *info) { IOEthernetControllerRef controller = (IOEthernetControllerRef)info; uint64_t state = 0; uint32_t count = 1; IOConnectCallScalarMethod(controller->connect, kIOUserEthernetResourceUserClientMethodGetState, NULL, 0, &state, &count); __IOEthernetControllerSetState(controller, state); } //------------------------------------------------------------------------------ // __IOEthernetControllerSetState //------------------------------------------------------------------------------ void __IOEthernetControllerSetState( IOEthernetControllerRef controller, Boolean state) { if ( state == controller->enable) return; controller->enable = state; if ( state ) { if ( controller->callbacks.enable.callback ) (*controller->callbacks.enable.callback)(controller, controller->callbacks.enable.refcon); } else { if ( controller->callbacks.disable.callback ) (*controller->callbacks.disable.callback)(controller, controller->callbacks.disable.refcon); } } //------------------------------------------------------------------------------ // IOEthernetControllerRegisterEnableCallback //------------------------------------------------------------------------------ void IOEthernetControllerRegisterEnableCallback( IOEthernetControllerRef controller, IOEthernetControllerCallback callback, void * refcon) { controller->callbacks.enable.callback = callback; controller->callbacks.enable.refcon = refcon; } //------------------------------------------------------------------------------ // IOEthernetControllerRegisterDisableCallback //------------------------------------------------------------------------------ void IOEthernetControllerRegisterDisableCallback( IOEthernetControllerRef controller, IOEthernetControllerCallback callback, void * refcon) { controller->callbacks.disable.callback = callback; controller->callbacks.disable.refcon = refcon; } //------------------------------------------------------------------------------ // IOEthernetControllerRegisterPacketAvailableCallback //------------------------------------------------------------------------------ void IOEthernetControllerRegisterPacketAvailableCallback( IOEthernetControllerRef controller, IOEthernetControllerCallback callback, void * refcon) { controller->callbacks.packet.callback = callback; controller->callbacks.packet.refcon = refcon; }