IONetworkStack.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h> // for setAggressiveness()
#include "IONetworkStack.h"
#include <libkern/c++/OSDictionary.h>
extern "C" {
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <net/dlil.h>
#include <sys/sockio.h>
void ether_ifattach(struct ifnet * ifp); }
#define super IOService
OSDefineMetaClassAndStructors( IONetworkStack, IOService )
OSMetaClassDefineReservedUnused( IONetworkStack, 0);
OSMetaClassDefineReservedUnused( IONetworkStack, 1);
OSMetaClassDefineReservedUnused( IONetworkStack, 2);
OSMetaClassDefineReservedUnused( IONetworkStack, 3);
#ifdef DEBUG_XXX
#define __LOG(class, fn, fmt, args...) IOLog(class "::%s " fmt, fn, ## args)
#define DLOG(fmt, args...) __LOG("IONetworkStack", __FUNCTION__, fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define NETIF_FLAGS(n) ((n)->_clientVar[0])
#define SET_NETIF_FLAGS(n, x) (NETIF_FLAGS(n) |= (x))
#define CLR_NETIF_FLAGS(n, x) (NETIF_FLAGS(n) &= ~(x))
static IONetworkStack * gIONetworkStack = 0;
enum {
kInterfaceFlagActive = 0x01, kInterfaceFlagRegistered = 0x02, kInterfaceFlagRegistering = 0x04 };
#include <IOKit/IOUserClient.h>
class IONetworkStackUserClient : public IOUserClient
{
OSDeclareDefaultStructors( IONetworkStackUserClient )
protected:
IONetworkStack * _provider;
public:
static IONetworkStackUserClient * withTask( task_t owningTask );
virtual bool start( IOService * provider );
virtual IOReturn clientClose();
virtual IOReturn clientDied();
virtual IOReturn setProperties( OSObject * properties );
};
bool IONetworkStack::init( OSDictionary * properties )
{
if ( super::init(properties) == false )
return false;
return true;
}
bool IONetworkStack::start( IOService * provider )
{
DLOG("%p\n", provider);
if ( super::start(provider) == false )
return false;
if ( gIONetworkStack != 0 )
return false;
gIONetworkStack = this;
_ifSet = OSOrderedSet::withCapacity(10);
if ( _ifSet == 0 )
return false;
_ifDict = OSDictionary::withCapacity(4);
if ( _ifDict == 0 )
return false;
IOService * root = (IOService *) getPMRootDomain();
if ( root )
{
UInt32 filters = kIOEthernetWakeOnMagicPacket |
kIOEthernetWakeOnPacketAddressMatch;
root->IOService::setAggressiveness( kPMEthernetWakeOnLANSettings,
filters );
}
_interfaceNotifier = addNotification(
gIOFirstPublishNotification,
serviceMatching("IONetworkInterface"),
interfacePublished,
this );
if ( _interfaceNotifier == 0 ) return false;
registerService();
DLOG("success\n");
return true;
}
void IONetworkStack::stop( IOService * provider )
{
DLOG("%p\n", provider);
super::stop(provider);
}
void IONetworkStack::free()
{
DLOG("\n");
if ( _interfaceNotifier )
{
_interfaceNotifier->remove();
_interfaceNotifier = 0;
}
if ( _ifDict )
{
_ifDict->release();
_ifDict = 0;
}
if ( _ifSet )
{
_ifSet->release();
_ifSet = 0;
}
gIONetworkStack = 0;
super::free();
}
IONetworkStack * IONetworkStack::getNetworkStack()
{
return (IONetworkStack *) IOService::waitForService(
IOService::serviceMatching("IONetworkStack") );
}
bool IONetworkStack::addInterface( IONetworkInterface * netif )
{
return _ifSet->setObject(netif);
}
void IONetworkStack::removeInterface( IONetworkInterface * netif )
{
_ifSet->removeObject(netif);
DLOG("count = %d\n", _ifSet->getCount());
}
IONetworkInterface * IONetworkStack::getInterface( UInt32 index )
{
return (IONetworkInterface *) _ifSet->getObject(index);
}
bool IONetworkStack::containsInterface( IONetworkInterface * netif )
{
return _ifSet->containsObject(netif);
}
bool IONetworkStack::addRegisteredInterface( IONetworkInterface * netif )
{
bool success = true;
OSOrderedSet * set;
const char * prefix = netif->getNamePrefix();
if (prefix == 0) return false;
set = (OSOrderedSet *) _ifDict->getObject(prefix);
if ( (set == 0) &&
((set = OSOrderedSet::withCapacity(10, orderRegisteredInterfaces))) )
{
success = _ifDict->setObject(prefix, set);
set->release();
}
success = (set && success) ? set->setObject(netif) : false;
return success;
}
void IONetworkStack::removeRegisteredInterface( IONetworkInterface * netif )
{
OSOrderedSet * set;
const char * prefix = netif->getNamePrefix();
if ( prefix )
{
set = (OSOrderedSet *) _ifDict->getObject(prefix);
if ( set )
{
set->removeObject(netif);
DLOG("set:%s count = %d\n", prefix, set->getCount());
if ( set->getCount() == 0 ) _ifDict->removeObject(prefix);
}
}
}
IONetworkInterface *
IONetworkStack::getRegisteredInterface( const char * prefix,
UInt32 unit )
{
OSOrderedSet * set;
IONetworkInterface * netif = 0;
set = (OSOrderedSet *) _ifDict->getObject(prefix);
for ( UInt32 index = 0;
( set && (netif = (IONetworkInterface *) set->getObject(index)) );
index++ )
{
if ( netif->getUnitNumber() == unit )
break;
}
return netif;
}
IONetworkInterface *
IONetworkStack::getLastRegisteredInterface( const char * prefix )
{
OSOrderedSet * set;
set = (OSOrderedSet *) _ifDict->getObject(prefix);
return ( set ) ? (IONetworkInterface *) set->getLastObject() : 0;
}
UInt32
IONetworkStack::getNextAvailableUnitNumber( const char * prefix,
UInt32 startingUnit )
{
IONetworkInterface * netif = getLastRegisteredInterface(prefix);
if ( ( netif == 0 ) || ( netif->getUnitNumber() < startingUnit ) )
{
}
else if ( netif->getUnitNumber() == startingUnit )
{
startingUnit++;
}
else
{
OSOrderedSet * set = (OSOrderedSet *) _ifDict->getObject(prefix);
for ( UInt32 index = 0; set; index++ )
{
netif = (IONetworkInterface *) set->getObject(index);
if ( ( netif == 0 ) ||
( netif->getUnitNumber() > startingUnit ) )
break;
else if ( netif->getUnitNumber() < startingUnit )
continue;
else
startingUnit = netif->getUnitNumber() + 1;
}
}
return startingUnit;
}
bool IONetworkStack::interfacePublished( void * ,
void * ,
IOService * service )
{
IONetworkInterface * netif = OSDynamicCast(IONetworkInterface, service);
bool success = false;
DLOG("%p\n", netif);
if ( gIONetworkStack == 0 )
return false;
gIONetworkStack->lockForArbitration();
do {
if ( netif == 0 ) break;
if ( gIONetworkStack->containsInterface(netif) == true )
{
success = true;
break;
}
if ( gIONetworkStack->addInterface(netif) == false )
break;
if ( gIONetworkStack->attach(netif) == false )
break;
NETIF_FLAGS(netif) = kInterfaceFlagActive;
if ( gIONetworkStack->_registerPrimaryInterface &&
netif->isPrimaryInterface() )
{
const char * prefix = netif->getNamePrefix();
const UInt32 unit = 0;
if ( gIONetworkStack->getRegisteredInterface(prefix, unit) == 0 )
{
OSArray * array = OSArray::withCapacity(1);
if ( array )
{
gIONetworkStack->preRegisterInterface( netif,
prefix,
unit,
array );
completeRegistration( array, false ); }
}
}
success = true;
}
while ( false );
if (success == false) gIONetworkStack->removeInterface(netif);
gIONetworkStack->unlockForArbitration();
return success;
}
IOReturn IONetworkStack::message( UInt32 type,
IOService * provider,
void * )
{
IONetworkInterface * netif = (IONetworkInterface *) provider;
IOReturn ret = kIOReturnBadArgument;
DLOG("%lx %p\n", type, provider);
if ( type == kIOMessageServiceIsTerminated )
{
lockForArbitration();
do {
if ( containsInterface(netif) == false )
break;
ret = kIOReturnSuccess;
CLR_NETIF_FLAGS( netif, kInterfaceFlagActive );
if ( NETIF_FLAGS(netif) & kInterfaceFlagRegistering )
break;
removeInterface(netif);
if ( (NETIF_FLAGS(netif) & kInterfaceFlagRegistered) == 0 )
break;
thread_call_func( (thread_call_func_t) unregisterBSDInterface,
netif,
TRUE );
}
while ( false );
unlockForArbitration();
}
return ret;
}
void IONetworkStack::unregisterBSDInterface( IONetworkInterface * netif )
{
assert( netif );
netif->setInterfaceState( 0, kIONetworkInterfaceRegisteredState );
DLOG("%p\n", netif);
if ( dlil_if_detach(netif->getIfnet()) != DLIL_WAIT_FOR_FREE )
{
bsdInterfaceWasUnregistered( netif->getIfnet() );
}
}
int IONetworkStack::bsdInterfaceWasUnregistered( struct ifnet * ifp )
{
IONetworkInterface * netif;
assert( ifp );
netif = (IONetworkInterface *) ifp->if_private;
DLOG("%p\n", netif);
assert( netif && gIONetworkStack );
gIONetworkStack->lockForArbitration();
assert( NETIF_FLAGS(netif) == kInterfaceFlagRegistered );
CLR_NETIF_FLAGS( netif, kInterfaceFlagRegistered );
gIONetworkStack->removeRegisteredInterface(netif);
gIONetworkStack->unlockForArbitration();
netif->setFlags( 0, IFF_UP ); (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, 0);
netif->close(gIONetworkStack);
return 0;
}
bool IONetworkStack::preRegisterInterface( IONetworkInterface * netif,
const char * prefix,
UInt32 unit,
OSArray * array,
bool fixedUnit )
{
bool success = false;
UInt32 inUnit = unit;
DLOG("%p %s %d\n", netif, prefix ? prefix : "", unit);
assert( netif && array );
do {
if ( prefix == 0 ) break;
if ( containsInterface(netif) == false )
break;
if ( NETIF_FLAGS(netif) != kInterfaceFlagActive )
{
break;
}
unit = getNextAvailableUnitNumber(prefix, unit);
if ( (fixedUnit == true) && (unit != inUnit) ) break;
if ( netif->open(this) == false )
{
break;
}
if ( ( netif->setUnitNumber(unit) == false ) ||
( addRegisteredInterface(netif) == false ) )
{
netif->close(this);
break;
}
success = true;
}
while ( false );
if ( success )
{
SET_NETIF_FLAGS( netif, kInterfaceFlagRegistering );
success = array->setObject( netif );
assert( success );
}
return success;
}
void
IONetworkStack::completeRegistration( OSArray * array, bool isSync )
{
if ( isSync )
{
completeRegistrationUsingArray( array );
}
else
{
thread_call_func( (thread_call_func_t) completeRegistrationUsingArray,
array,
TRUE );
}
}
void
IONetworkStack::completeRegistrationUsingArray( OSArray * array )
{
IONetworkInterface * netif;
assert( array );
for ( UInt32 i = 0; i < array->getCount(); i++ )
{
netif = (IONetworkInterface *) array->getObject(i);
assert( netif );
registerBSDInterface( netif );
}
array->release(); }
void IONetworkStack::registerBSDInterface( IONetworkInterface * netif )
{
char ifname[20];
bool doTermination = false;
assert( netif );
bpfattach( netif->getIfnet(), DLT_EN10MB, sizeof(struct ether_header) );
ether_ifattach( netif->getIfnet() );
netif->setInterfaceState( kIONetworkInterfaceRegisteredState );
sprintf(ifname, "%s%d", netif->getNamePrefix(), netif->getUnitNumber());
netif->setProperty(kIOBSDNameKey, ifname);
gIONetworkStack->lockForArbitration();
assert( ( NETIF_FLAGS(netif) &
( kInterfaceFlagRegistering | kInterfaceFlagRegistered ) ) ==
kInterfaceFlagRegistering );
CLR_NETIF_FLAGS( netif, kInterfaceFlagRegistering );
SET_NETIF_FLAGS( netif, kInterfaceFlagRegistered );
if ( ( NETIF_FLAGS(netif) & kInterfaceFlagActive ) == 0 )
{
doTermination = true;
}
else
{
netif->registerService();
}
gIONetworkStack->unlockForArbitration();
if ( doTermination )
{
gIONetworkStack->message(kIOMessageServiceIsTerminated, netif);
}
}
IOReturn
IONetworkStack::registerAllInterfaces()
{
IONetworkInterface * netif;
const UInt32 unit = 0;
OSArray * array;
lockForArbitration();
array = OSArray::withCapacity( _ifSet->getCount() );
if ( array == 0 )
{
unlockForArbitration();
return kIOReturnNoMemory;
}
for ( UInt32 index = 0; ( netif = getInterface(index) ); index++ )
{
if ( NETIF_FLAGS(netif) != kInterfaceFlagActive )
{
continue;
}
preRegisterInterface( netif,
netif->getNamePrefix(),
unit,
array );
}
unlockForArbitration();
completeRegistration( array, true );
return kIOReturnSuccess;
}
IOReturn IONetworkStack::registerPrimaryInterface( bool enable )
{
IONetworkInterface * netif;
const UInt32 unit = 0;
OSArray * array;
lockForArbitration();
_registerPrimaryInterface = enable;
if ( _registerPrimaryInterface == false )
{
unlockForArbitration();
return kIOReturnSuccess;
}
array = OSArray::withCapacity( _ifSet->getCount() );
if ( array == 0 )
{
unlockForArbitration();
return kIOReturnNoMemory;
}
for ( UInt32 index = 0; ( netif = getInterface(index) ); index++ )
{
const char * prefix = netif->getNamePrefix();
if ( NETIF_FLAGS(netif) != kInterfaceFlagActive )
{
continue;
}
if ( netif->isPrimaryInterface() != true )
{
continue;
}
if ( getRegisteredInterface( prefix, unit ) )
{
continue;
}
preRegisterInterface( netif, prefix, unit, array );
}
unlockForArbitration();
completeRegistration( array, true );
return kIOReturnSuccess;
}
IOReturn IONetworkStack::registerInterface( IONetworkInterface * netif,
const char * prefix,
UInt32 unit,
bool isSync,
bool fixedUnit )
{
bool ret;
OSArray * array;
array = OSArray::withCapacity( 1 );
if ( array == 0 )
{
return kIOReturnNoMemory;
}
lockForArbitration();
ret = preRegisterInterface( netif, prefix, unit, array, fixedUnit );
unlockForArbitration();
completeRegistration( array, isSync );
return ret ? kIOReturnSuccess : kIOReturnError;
}
SInt32 IONetworkStack::
orderRegisteredInterfaces( const OSMetaClassBase * obj1,
const OSMetaClassBase * obj2,
void * ref )
{
const IONetworkInterface * netif1 = (const IONetworkInterface *) obj1;
const IONetworkInterface * netif2 = (const IONetworkInterface *) obj2;
assert( netif1 && netif2 );
return ( netif2->getUnitNumber() - netif1->getUnitNumber() );
}
IOReturn IONetworkStack::newUserClient( task_t owningTask,
void * ,
UInt32 ,
IOUserClient ** handler )
{
IOReturn err = kIOReturnSuccess;
IOUserClient * client;
client = IONetworkStackUserClient::withTask(owningTask);
if (!client || !client->attach(this) || !client->start(this))
{
if (client)
{
client->detach(this);
client->release();
client = 0;
err = kIOReturnExclusiveAccess;
}
else
{
err = kIOReturnNoMemory;
}
}
*handler = client;
return err;
}
IOReturn IONetworkStack::setProperties( OSObject * properties )
{
IONetworkInterface * netif;
OSDictionary * dict = OSDynamicCast(OSDictionary, properties);
IOReturn ret = kIOReturnBadArgument;
OSString * path = 0;
OSNumber * unit;
OSNumber * cmd;
UInt32 cmdType = kRegisterInterface;
bool fixedUnit = false;
do {
if ( dict == 0 ) break;
cmd = OSDynamicCast( OSNumber,
dict->getObject( kIONetworkStackUserCommand ) );
if ( cmd ) cmdType = cmd->unsigned32BitValue();
switch ( cmdType )
{
case kRegisterInterfaceWithFixedUnit:
fixedUnit = true;
case kRegisterInterface:
path = OSDynamicCast( OSString,
dict->getObject( kIOPathMatchKey ));
unit = OSDynamicCast( OSNumber,
dict->getObject( kIOInterfaceUnit ));
if ( (path == 0) || (unit == 0) ) break;
netif = OSDynamicCast( IONetworkInterface,
IORegistryEntry::fromPath( path->getCStringNoCopy()) );
if ( netif == 0 ) break;
ret = registerInterface( netif,
netif->getNamePrefix(),
unit->unsigned32BitValue(),
true,
fixedUnit );
netif->release();
break;
case kRegisterAllInterfaces:
ret = registerAllInterfaces();
break;
}
}
while ( false );
return ret;
}
#undef super
#define super IOUserClient
OSDefineMetaClassAndStructors( IONetworkStackUserClient, IOUserClient )
IONetworkStackUserClient * IONetworkStackUserClient::withTask( task_t task )
{
IONetworkStackUserClient * me = new IONetworkStackUserClient;
if ( me && me->init() == false )
{
me->release();
return 0;
}
return me;
}
bool IONetworkStackUserClient::start( IOService * provider )
{
if ( super::start(provider) == false )
return false;
if ( provider->open(this) == false )
return false;
_provider = (IONetworkStack *) provider;
return true;
}
IOReturn IONetworkStackUserClient::clientClose()
{
if (_provider)
{
_provider->close(this);
detach(_provider);
}
return kIOReturnSuccess;
}
IOReturn IONetworkStackUserClient::clientDied()
{
return clientClose();
}
IOReturn IONetworkStackUserClient::setProperties( OSObject * properties )
{
return ( _provider ) ? _provider->setProperties( properties )
: kIOReturnNotReady;
}