IOUserEthernetController.c [plain text]
#include <AssertMacros.h>
#include <pthread.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFBase.h>
#include <IOKit/network/IOUserEthernetResourceUserClient.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOCFSerialize.h>
#include "IOUserEthernetController.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>
#include <fcntl.h>
#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;
sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (sock == -1) return sock;
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;
}
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;
}
result = setsockopt(sock, SYSPROTO_CONTROL, EN_SOCKOPT_CONNECT, cookie, cookielen);
if (result == -1) {
close(sock);
return -1;
}
result = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsize, sizeof(rcvsize));
if (result != 0) {
close(sock);
return -1;
}
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;
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, "IOEthernetController", NULL, NULL, __IOEthernetControllerRelease, NULL, NULL, NULL, NULL,
NULL
};
static pthread_once_t __controllerTypeInit = PTHREAD_ONCE_INIT;
static CFTypeID __kIOEthernetControllerTypeID = _kCFRuntimeNotATypeID;
static mach_port_t __masterPort = MACH_PORT_NULL;
void __IOEthernetControllerRegister(void)
{
__kIOEthernetControllerTypeID = _CFRuntimeRegisterClass(&__IOEthernetControllerClass);
IOMasterPort(bootstrap_port, &__masterPort);
}
IOEthernetControllerRef __IOEthernetControllerCreate(
CFAllocatorRef allocator,
CFAllocatorContext * context __unused)
{
IOEthernetControllerRef controller = NULL;
void * offset = NULL;
uint32_t size;
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;
}
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;
}
}
CFTypeID IOEthernetControllerGetTypeID(void)
{
if ( _kCFRuntimeNotATypeID == __kIOEthernetControllerTypeID )
pthread_once(&__controllerTypeInit, __IOEthernetControllerRegister);
return __kIOEthernetControllerTypeID;
}
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;
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;
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;
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;
}
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;
} while (FALSE);
if ( matching )
CFRelease(matching);
}
return controller->interface;
}
IOReturn IOEthernetControllerSetLinkStatus(
IOEthernetControllerRef controller,
Boolean state)
{
uint64_t param = state;
return IOConnectCallScalarMethod(controller->connect, kIOUserEthernetResourceUserClientMethodSetLinkStatus, ¶m, 1, NULL, NULL);
}
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);
}
CFIndex IOEthernetControllerReadPacket(
IOEthernetControllerRef controller,
uint8_t * buffer,
CFIndex bufferLength)
{
return recv(CFSocketGetNative(controller->socket), (void *)buffer, bufferLength, 0);
}
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;
}
void IOEthernetControllerScheduleWithRunLoop(
IOEthernetControllerRef controller,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
CFRunLoopAddSource(runLoop, controller->portSource, runLoopMode);
CFRunLoopAddSource(runLoop, controller->socketSource, runLoopMode);
controller->runLoop = runLoop;
controller->runLoopMode = runLoopMode;
}
void IOEthernetControllerUnscheduleFromRunLoop(
IOEthernetControllerRef controller,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
CFRunLoopRemoveSource(runLoop, controller->socketSource, runLoopMode);
CFRunLoopRemoveSource(runLoop, controller->portSource, runLoopMode);
controller->runLoop = NULL;
controller->runLoopMode = NULL;
}
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);
}
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);
}
}
void IOEthernetControllerRegisterEnableCallback(
IOEthernetControllerRef controller,
IOEthernetControllerCallback callback,
void * refcon)
{
controller->callbacks.enable.callback = callback;
controller->callbacks.enable.refcon = refcon;
}
void IOEthernetControllerRegisterDisableCallback(
IOEthernetControllerRef controller,
IOEthernetControllerCallback callback,
void * refcon)
{
controller->callbacks.disable.callback = callback;
controller->callbacks.disable.refcon = refcon;
}
void IOEthernetControllerRegisterPacketAvailableCallback(
IOEthernetControllerRef controller,
IOEthernetControllerCallback callback,
void * refcon)
{
controller->callbacks.packet.callback = callback;
controller->callbacks.packet.refcon = refcon;
}