IONetworkInterface.cpp [plain text]
extern "C" {
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/dlil.h>
#include <net/if_dl.h>
#include <net/kpi_interface.h>
#include <sys/kern_event.h>
}
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IONetworkController.h>
#include "IONetworkUserClient.h"
#include "IONetworkStack.h"
#include "IONetworkControllerPrivate.h"
#include <TargetConditionals.h>
#define super IOService
OSDefineMetaClassAndAbstractStructors( IONetworkInterface, IOService )
OSMetaClassDefineReservedUnused( IONetworkInterface, 5);
OSMetaClassDefineReservedUnused( IONetworkInterface, 6);
OSMetaClassDefineReservedUnused( IONetworkInterface, 7);
OSMetaClassDefineReservedUnused( IONetworkInterface, 8);
OSMetaClassDefineReservedUnused( IONetworkInterface, 9);
OSMetaClassDefineReservedUnused( IONetworkInterface, 10);
OSMetaClassDefineReservedUnused( IONetworkInterface, 11);
OSMetaClassDefineReservedUnused( IONetworkInterface, 12);
OSMetaClassDefineReservedUnused( IONetworkInterface, 13);
OSMetaClassDefineReservedUnused( IONetworkInterface, 14);
OSMetaClassDefineReservedUnused( IONetworkInterface, 15);
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define WAITING_FOR_DETACH(n) ((n)->_clientVar[1])
#define _unit _reserved->unit
#define _type _reserved->type
#define _mtu _reserved->mtu
#define _flags _reserved->flags
#define _eflags _reserved->eflags
#define _addrlen _reserved->addrlen
#define _hdrlen _reserved->hdrlen
#define _inputDeltas _reserved->inputDeltas
#define _driverStats _reserved->driverStats
#define _lastDriverStats _reserved->lastDriverStats
#define _detachLock _reserved->detachLock
#define kIONetworkControllerProperties "IONetworkControllerProperties"
void IONetworkInterface::_syncToBackingIfnet()
{
if(_backingIfnet == NULL)
return;
ifnet_set_mtu(_backingIfnet, _mtu);
ifnet_set_flags(_backingIfnet, _flags, 0xffff);
ifnet_set_addrlen(_backingIfnet, _addrlen);
ifnet_set_hdrlen(_backingIfnet, _hdrlen);
}
void IONetworkInterface::_syncFromBackingIfnet() const
{
if(_backingIfnet == NULL)
return;
_mtu = ifnet_mtu(_backingIfnet);
_flags = ifnet_flags(_backingIfnet);
_eflags = ifnet_eflags(_backingIfnet);
_addrlen = ifnet_addrlen(_backingIfnet);
_hdrlen = ifnet_hdrlen(_backingIfnet);
}
bool IONetworkInterface::init(IONetworkController * controller)
{
if ( super::init() == false )
return false;
if(getIfnet())
{
IOLog("\33[00m\33[31m%s: IONetworkingFamily interface KPIs do not support IONetworkInterface subclasses that use ifnets\n\33[00m", getName());
return false;
}
if ( OSDynamicCast(IONetworkController, controller) == 0 )
return false;
_controller = controller;
_reserved = IONew( ExpansionData, 1 );
if ( _reserved == 0 )
return false;
bzero( _reserved, sizeof(ExpansionData) );
_detachLock = IOLockAlloc();
if( _detachLock == 0)
return false;
_ifLock = IORecursiveLockAlloc();
if ( _ifLock == 0 )
return false;
_stateBits = OSNumber::withNumber((UInt64) 0, 32);
if ( _stateBits == 0 )
return false;
setProperty( kIOInterfaceState, _stateBits );
_clientSet = OSSet::withCapacity(2);
if ( _clientSet == 0 )
return false;
if ( (_dataDict = OSDictionary::withCapacity(5)) == 0 )
return false;
IONetworkData * data = IONetworkData::withExternalBuffer(
kIONetworkStatsKey,
sizeof(IONetworkStats),
&_driverStats);
if ( data )
{
addNetworkData(data);
data->release();
}
if ( registerOutputHandler( controller,
controller->getOutputHandler() ) == false )
{
return false;
}
setProperty( kIOInterfaceNamePrefix, getNamePrefix() );
#if TARGET_OS_EMBEDDED
OSString *networkType = OSDynamicCast(OSString, controller->getProperty( "IONetworkRootType" ));
if(networkType)
setProperty( "IONetworkRootType", networkType );
#endif
if (IOService *provider = controller->getProvider())
{
bool gotLocation = false;
setProperty( kIOBuiltin, (bool)( provider->getProperty( "built-in" ) ) );
if(OSData *locationAsData = OSDynamicCast(OSData, provider->getProperty("location")))
{
if(OSData *locationAsCstr = OSData::withData(locationAsData)) {
locationAsCstr->appendByte(0, 1); if(OSString *locationAsString = OSString::withCString((const char *)locationAsCstr->getBytesNoCopy())) {
gotLocation = true;
setProperty( kIOLocation, locationAsString);
locationAsString->release(); }
locationAsCstr->release();
}
}
if(gotLocation == false)
setProperty( kIOLocation, "");
}
return true;
}
void IONetworkInterface::free()
{
DLOG("IONetworkInterface::free()\n");
if ( _clientSet )
{
assert(_clientSet->getCount() == 0);
_clientSet->release();
_clientSet = 0;
}
if ( _dataDict ) { _dataDict->release(); _dataDict = 0; }
if ( _stateBits ) { _stateBits->release(); _stateBits = 0; }
if ( _ifLock )
{
IORecursiveLockFree(_ifLock);
_ifLock = 0;
}
if ( _reserved )
{
clearInputQueue();
if( _detachLock )
IOLockFree( _detachLock);
IODelete( _reserved, ExpansionData, 1 );
_reserved = 0;
}
super::free();
}
bool IONetworkInterface::isPrimaryInterface() const
{
IOService * provider = getController();
bool isPrimary = false;
if ( provider ) provider = provider->getProvider();
if ( provider && provider->getProperty( "built-in" ) && getUnitNumber()==0)
{
isPrimary = true;
}
return isPrimary;
}
IONetworkController * IONetworkInterface::getController() const
{
return _controller;
}
static UInt32 getIfnetHardwareAssistValue( IONetworkController * ctr )
{
UInt32 input;
UInt32 output;
UInt32 hwassist = 0;
do {
if ( ctr->getChecksumSupport(
&input,
IONetworkController::kChecksumFamilyTCPIP,
false ) != kIOReturnSuccess ) break;
if ( ctr->getChecksumSupport(
&output,
IONetworkController::kChecksumFamilyTCPIP,
true ) != kIOReturnSuccess ) break;
if ( input & output & IONetworkController::kChecksumIP )
{
hwassist |= IFNET_CSUM_IP;
}
if ( ( input & ( IONetworkController::kChecksumTCP |
IONetworkController::kChecksumTCPNoPseudoHeader ) )
&& ( output & ( IONetworkController::kChecksumTCP ) ) )
{
hwassist |= IFNET_CSUM_TCP;
}
if ( ( input & ( IONetworkController::kChecksumUDP |
IONetworkController::kChecksumUDPNoPseudoHeader ) )
&& ( output & ( IONetworkController::kChecksumUDP ) ) )
{
hwassist |= IFNET_CSUM_UDP;
}
if ( input & output & IONetworkController::kChecksumTCPSum16 )
{
hwassist |= ( IFNET_CSUM_SUM16 | IFNET_CSUM_TCP | IFNET_CSUM_UDP );
}
}
while ( false );
if( ctr->getFeatures() & kIONetworkFeatureHardwareVlan)
hwassist |= IFNET_VLAN_TAGGING;
if( ctr->getFeatures() & kIONetworkFeatureSoftwareVlan)
hwassist |= IFNET_VLAN_MTU;
if( ctr->getFeatures() & kIONetworkFeatureMultiPages)
hwassist |= IFNET_MULTIPAGES;
if( ctr->getFeatures() & kIONetworkFeatureTSOIPv4)
hwassist |= IFNET_TSO_IPV4;
if( ctr->getFeatures() & kIONetworkFeatureTSOIPv6)
hwassist |= IFNET_TSO_IPV6;
return hwassist;
}
bool IONetworkInterface::initIfnet(struct ifnet * ifp)
{
return false;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 4);
bool IONetworkInterface::initIfnetParams(struct ifnet_init_params *params)
{
params->name = (char *) getNamePrefix();
params->type = _type;
params->unit = _unit;
params->output = output_shim;
params->ioctl = ioctl_shim;
params->set_bpf_tap = set_bpf_tap_shim;
params->detach = detach_shim;
params->softc = this;
return true;
}
bool IONetworkInterface::matchPropertyTable(OSDictionary * table,
SInt32 * score)
{
return super::matchPropertyTable(table, score);
}
void IONetworkInterface::lock()
{
IORecursiveLockLock(_ifLock);
}
void IONetworkInterface::unlock()
{
IORecursiveLockUnlock(_ifLock);
}
bool IONetworkInterface::controllerDidOpen(IONetworkController * controller)
{
return true; }
void IONetworkInterface::controllerWillClose(IONetworkController * controller)
{
}
bool IONetworkInterface::handleOpen(IOService * client,
IOOptionBits options,
void * argument)
{
bool accept = false;
bool controllerOpen = false;
do {
if ( _clientSet->containsObject(client) )
{
DLOG("%s: multiple opens from client %lx\n",
getName(), (UInt32) client);
accept = true;
break;
}
if ( ( getInterfaceState() & kIONetworkInterfaceOpenedState ) == 0 )
{
if ( ( (controllerOpen = _controller->open(this)) == false ) ||
( controllerDidOpen(_controller) == false ) )
break;
}
if ( handleClientOpen(client, options, argument) == false )
break;
if ( _clientSet->setObject(client) == false )
{
handleClientClose(client, 0);
break;
}
accept = true;
}
while (false);
if ( controllerOpen )
{
if (accept)
{
setInterfaceState( kIONetworkInterfaceOpenedState );
_controller->registerInterestedDriver( this );
}
else {
controllerWillClose(_controller);
_controller->close(this);
}
}
return accept;
}
void IONetworkInterface::handleClose(IOService * client, IOOptionBits options)
{
if ( _clientSet->containsObject(client) )
{
handleClientClose( client, options );
if ( _clientSet->getCount() == 1 )
{
_controller->deRegisterInterestedDriver( this );
controllerWillClose( _controller );
_controller->close( this );
setInterfaceState( 0, kIONetworkInterfaceOpenedState );
}
_clientSet->removeObject(client);
}
}
bool IONetworkInterface::handleIsOpen(const IOService * client) const
{
if (client)
return _clientSet->containsObject(client);
else
return (_clientSet->getCount() > 0);
}
bool IONetworkInterface::handleClientOpen(IOService * client,
IOOptionBits options,
void * argument)
{
return true;
}
void IONetworkInterface::handleClientClose(IOService * client,
IOOptionBits options)
{
}
bool IONetworkInterface::registerOutputHandler(OSObject * target,
IOOutputAction action)
{
lock();
if ( (getInterfaceState() & kIONetworkInterfaceOpenedState) ||
!target || !action )
{
unlock();
return false;
}
_outTarget = target;
_outAction = action;
unlock();
return true;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 2);
void IONetworkInterface::feedPacketInputTap(mbuf_t m)
{
bpf_packet_func inFilter = _inputFilterFunc; if(inFilter) inFilter(_backingIfnet, m);
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 3);
void IONetworkInterface::feedPacketOutputTap(mbuf_t m)
{
bpf_packet_func outFilter = _outputFilterFunc; if(outFilter)
outFilter(_backingIfnet, m);
}
#define ABS(a) ((a) < 0 ? -(a) : (a))
#define IN_Q_ENQUEUE(m) \
{ \
if (_inputQHead == 0) { \
_inputQHead = _inputQTail = (m); \
} \
else { \
mbuf_setnextpkt(_inputQTail, (m)) ; \
_inputQTail = (m); \
} \
_inputQCount++; \
}
__inline__ void IONetworkInterface::DLIL_INPUT(mbuf_t m_head)
{
if ( m_head ) {
UInt32 noraceTemp;
int delta;
noraceTemp = _driverStats.inputPackets;
delta = noraceTemp - _lastDriverStats.inputPackets ;
_inputDeltas.packets_in = ABS(delta);
_lastDriverStats.inputPackets = noraceTemp;
noraceTemp = _driverStats.inputErrors;
delta = noraceTemp - _lastDriverStats.inputErrors;
_inputDeltas.errors_in = ABS(delta);
_lastDriverStats.inputErrors = noraceTemp;
noraceTemp = _driverStats.collisions;
delta = noraceTemp - _lastDriverStats.collisions;
_inputDeltas.collisions = ABS(delta);
_lastDriverStats.collisions = noraceTemp;
ifnet_input(_backingIfnet, m_head, &_inputDeltas);
_inputDeltas.bytes_in = 0; }
}
UInt32 IONetworkInterface::flushInputQueue()
{
UInt32 count = _inputQCount;
DLIL_INPUT(_inputQHead);
_inputQHead = _inputQTail = 0;
_inputQCount = 0;
return count;
}
UInt32 IONetworkInterface::clearInputQueue()
{
UInt32 count = _inputQCount;
mbuf_freem_list( _inputQHead );
_inputDeltas.bytes_in = 0;
_inputQHead = _inputQTail = 0;
_inputQCount = 0;
return count;
}
UInt32 IONetworkInterface::inputPacket(mbuf_t pkt,
UInt32 length,
IOOptionBits options,
void * param)
{
UInt32 count;
UInt32 hdrlen = getMediaHeaderLength();
assert(pkt);
if ( length )
{
if ( mbuf_next(pkt) == 0 )
{
mbuf_pkthdr_setlen(pkt, length);
mbuf_setlen(pkt, length);
}
else
{
mbuf_t m = pkt;
mbuf_pkthdr_setlen(pkt, length);
do {
if (length < (UInt32) mbuf_len(m))
mbuf_setlen(m, length);
length -= mbuf_len(m);
} while (( m = mbuf_next(m) ));
assert(length == 0);
}
}
mbuf_pkthdr_setrcvif(pkt, _backingIfnet);
_inputDeltas.bytes_in += mbuf_pkthdr_len(pkt);
if(_inputFilterFunc)
feedPacketInputTap(pkt);
mbuf_pkthdr_setheader(pkt, mbuf_data(pkt));
mbuf_pkthdr_setlen(pkt, mbuf_pkthdr_len(pkt) - hdrlen);
mbuf_setdata(pkt, (char *)mbuf_data(pkt) + hdrlen, mbuf_len(pkt) - hdrlen);
if ( options & kInputOptionQueuePacket )
{
IN_Q_ENQUEUE(pkt);
count = 0;
}
else
{
if ( _inputQHead ) {
IN_Q_ENQUEUE(pkt);
count = _inputQCount;
DLIL_INPUT(_inputQHead);
_inputQHead = _inputQTail = 0;
_inputQCount = 0;
}
else
{
DLIL_INPUT(pkt);
count = 1;
}
}
return count;
}
bool IONetworkInterface::inputEvent(UInt32 type, void * data)
{
bool success = true;
struct {
kern_event_msg header;
uint32_t unit;
char if_name[IFNAMSIZ];
} event;
switch (type)
{
case kIONetworkEventTypeLinkUp:
case kIONetworkEventTypeLinkDown:
if ( _backingIfnet )
{
bzero((void *) &event, sizeof(event));
event.header.total_size = sizeof(event);
event.header.vendor_code = KEV_VENDOR_APPLE;
event.header.kev_class = KEV_NETWORK_CLASS;
event.header.kev_subclass = KEV_DL_SUBCLASS;
event.header.event_code = (type == kIONetworkEventTypeLinkUp) ?
KEV_DL_LINK_ON : KEV_DL_LINK_OFF;
event.header.event_data[0] = ifnet_family(_backingIfnet);
event.unit = ifnet_unit(_backingIfnet);
strncpy(&event.if_name[0], ifnet_name(_backingIfnet), IFNAMSIZ);
ifnet_event(_backingIfnet, &event.header);
}
break;
case kIONetworkEventTypeDLIL:
ifnet_event(_backingIfnet, (struct kern_event_msg *) data);
break;
case kIONetworkEventWakeOnLANSupportChanged:
break;
default:
IOLog("IONetworkInterface: unknown event type %x\n", (uint32_t) type);
success = false;
break;
}
return success;
}
SInt32 IONetworkInterface::syncSIOCSIFMTU(IONetworkController * ctr,
struct ifreq * ifr)
{
SInt32 error;
UInt32 newMTU = ifr->ifr_mtu;
if ( getMaxTransferUnit() == newMTU )
return 0;
error = errnoFromReturn( ctr->setMaxPacketSize(newMTU) );
if ( error == 0 )
{
setMaxTransferUnit(newMTU);
}
return error;
}
SInt32 IONetworkInterface::syncSIOCSIFMEDIA(IONetworkController * ctr,
struct ifreq * ifr)
{
OSDictionary * mediumDict;
IONetworkMedium * medium;
SInt32 error;
mediumDict = ctr->copyMediumDictionary(); if ( mediumDict == 0 )
{
return EOPNOTSUPP;
}
do {
medium = IONetworkMedium::getMediumWithType(
mediumDict,
ifr->ifr_media );
if ( medium ) break;
OSSymbol * selMediumName = (OSSymbol *)
ctr->copyProperty( kIOSelectedMedium );
if ( selMediumName )
{
UInt32 modMediumBits;
IONetworkMedium * selMedium = (IONetworkMedium *)
mediumDict->getObject( selMediumName );
if ( selMedium &&
(modMediumBits = selMedium->getType() ^ ifr->ifr_media) )
{
medium = IONetworkMedium::getMediumWithType(
mediumDict,
ifr->ifr_media,
~( IFM_TMASK | IFM_NMASK | modMediumBits));
}
selMediumName->release();
}
if ( medium ) break;
medium = IONetworkMedium::getMediumWithType(
mediumDict,
ifr->ifr_media,
~( IFM_TMASK | IFM_NMASK ));
} while ( false );
if ( medium )
error = errnoFromReturn(
ctr->selectMediumWithName(medium->getName()) );
else
error = EINVAL;
mediumDict->release();
return error;
}
SInt32 IONetworkInterface::syncSIOCGIFMEDIA(IONetworkController * ctr,
struct ifreq * ifr,
unsigned long cmd)
{
OSDictionary * mediumDict = 0;
UInt mediumCount = 0;
UInt maxCount;
OSCollectionIterator * iter = 0;
UInt32 * typeList;
UInt typeListSize;
OSSymbol * keyObject;
SInt32 error = 0;
struct ifmediareq * ifmr = (struct ifmediareq *) ifr;
maxCount = ifmr->ifm_count;
do {
mediumDict = ctr->copyMediumDictionary(); if (mediumDict == 0)
{
break; }
if ((mediumCount = mediumDict->getCount()) == 0)
break;
if (maxCount == 0)
break;
if (maxCount < mediumCount)
{
error = E2BIG;
}
iter = OSCollectionIterator::withCollection(mediumDict);
if (!iter)
{
error = ENOMEM;
break;
}
typeListSize = maxCount * sizeof(UInt32);
typeList = (UInt32 *) IOMalloc(typeListSize);
if (!typeList)
{
error = ENOMEM;
break;
}
bzero(typeList, typeListSize);
mediumCount = 0;
while ( (keyObject = (OSSymbol *) iter->getNextObject()) &&
(mediumCount < maxCount) )
{
IONetworkMedium * medium = (IONetworkMedium *)
mediumDict->getObject(keyObject);
if (!medium)
continue;
typeList[mediumCount++] = medium->getType();
}
if (mediumCount)
{
user_addr_t srcaddr;
srcaddr = (cmd == SIOCGIFMEDIA64) ?
((struct ifmediareq64 *)ifmr)->ifmu_ulist :
CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
if (srcaddr != USER_ADDR_NULL) {
error = copyout((caddr_t) typeList, srcaddr, typeListSize);
}
}
IOFree(typeList, typeListSize);
}
while (0);
ifmr->ifm_active = ifmr->ifm_current = IFM_NONE;
ifmr->ifm_status = 0;
ifmr->ifm_count = mediumCount;
OSDictionary * pTable = ctr->dictionaryWithProperties();
if (pTable)
{
OSNumber * linkStatus = (OSNumber *)
pTable->getObject(kIOLinkStatus);
if (linkStatus)
ifmr->ifm_status = linkStatus->unsigned32BitValue();
if (mediumDict)
{
IONetworkMedium * medium;
OSSymbol * mediumName;
if ((mediumName = (OSSymbol *) pTable->getObject(kIOSelectedMedium))
&& (medium = (IONetworkMedium *)
mediumDict->getObject(mediumName)))
{
ifmr->ifm_current = medium->getType();
}
if ((mediumName = (OSSymbol *) pTable->getObject(kIOActiveMedium))
&& (medium = (IONetworkMedium *)
mediumDict->getObject(mediumName)))
{
ifmr->ifm_active = medium->getType();
}
}
pTable->release();
}
if (iter)
iter->release();
if (mediumDict)
mediumDict->release();
return error;
}
SInt32 IONetworkInterface::performCommand(IONetworkController * ctr,
unsigned long cmd,
void * arg0,
void * arg1)
{
struct ifreq * ifr = (struct ifreq *) arg1;
SInt32 ret = EOPNOTSUPP;
if ( (ifr == 0) || (ctr == 0) )
return EINVAL;
switch ( cmd )
{
case SIOCGIFMTU:
ifr->ifr_mtu = getMaxTransferUnit();
ret = 0; break;
case SIOCGIFMEDIA32:
case SIOCGIFMEDIA64:
ret = syncSIOCGIFMEDIA(ctr, ifr, cmd);
break;
case SIOCSIFMTU:
case SIOCSIFMEDIA:
case SIOCSIFLLADDR:
ret = (int) ctr->executeCommand(
this,
(IONetworkController::Action)
&IONetworkInterface::performGatedCommand,
this,
ctr,
(void *) cmd,
arg0,
arg1 );
break;
default:
break;
}
return ret;
}
int IONetworkInterface::performGatedCommand(void * target,
void * arg1_ctr,
void * arg2_cmd,
void * arg3_0,
void * arg4_1)
{
IONetworkInterface * self = (IONetworkInterface *) target;
IONetworkController * ctr = (IONetworkController *) arg1_ctr;
struct ifreq * ifr = (struct ifreq *) arg4_1;
SInt32 ret = EOPNOTSUPP;
if ( self->getInterfaceState() & kIONetworkInterfaceDisabledState )
return EPWROFF;
switch ( (uintptr_t) arg2_cmd )
{
case SIOCSIFMTU:
ret = self->syncSIOCSIFMTU(ctr, ifr);
break;
case SIOCSIFMEDIA:
ret = self->syncSIOCSIFMEDIA(ctr, ifr);
break;
case SIOCSIFLLADDR:
ret = ctr->errnoFromReturn(
ctr->setHardwareAddress( ifr->ifr_addr.sa_data,
ifr->ifr_addr.sa_len ) );
break;
}
return ret;
}
errno_t
IONetworkInterface::ioctl_shim(ifnet_t ifn, unsigned long cmd, void * data)
{
IONetworkInterface * self = (IONetworkInterface *) ifnet_softc(ifn);
return self->performCommand( self->_controller,
cmd,
(void *) ifn,
data );
}
int IONetworkInterface::output_shim(ifnet_t ifn, mbuf_t m)
{
UInt32 noraceTemp;
int delta;
u_int32_t outPackets, outErrors;
IONetworkInterface * self = (IONetworkInterface *) ifnet_softc(ifn);
if ( m == 0 )
{
DLOG("IONetworkInterface: NULL output mbuf\n");
return EINVAL;
}
if ( (mbuf_flags(m) & M_PKTHDR) == 0 )
{
DLOG("IONetworkInterface: M_PKTHDR bit not set\n");
mbuf_freem(m);
return EINVAL;
}
noraceTemp = self->_driverStats.outputErrors;
delta = noraceTemp - self->_lastDriverStats.outputErrors;
outErrors = ABS(delta);
self->_lastDriverStats.outputErrors = noraceTemp;
noraceTemp = self->_driverStats.outputPackets;
delta = noraceTemp - self->_lastDriverStats.outputPackets;
outPackets = ABS(delta);
self->_lastDriverStats.outputPackets = noraceTemp;
ifnet_stat_increment_out(self->getIfnet(), outPackets, mbuf_pkthdr_len(m), outErrors);
if(self->_outputFilterFunc)
self->feedPacketOutputTap(m);
return ((self->_outTarget)->*(self->_outAction))(m, 0);
}
errno_t IONetworkInterface::set_bpf_tap_shim(ifnet_t ifn,
bpf_tap_mode mode,
bpf_packet_func func)
{
IONetworkInterface * self = (IONetworkInterface *) ifnet_softc(ifn);
switch ( mode )
{
case BPF_TAP_DISABLE:
self->_inputFilterFunc = self->_outputFilterFunc = 0;
break;
case BPF_TAP_INPUT:
assert(func);
self->_inputFilterFunc = func;
break;
case BPF_TAP_OUTPUT:
assert(func);
self->_outputFilterFunc = func;
break;
case BPF_TAP_INPUT_OUTPUT:
assert(func);
self->_inputFilterFunc = self->_outputFilterFunc = func;
break;
default:
DLOG("IONetworkInterface: Unknown BPF tap mode %d\n", mode);
break;
}
return 0;
}
void IONetworkInterface::detach_shim(ifnet_t ifn)
{
IONetworkInterface * self = (IONetworkInterface *) ifnet_softc(ifn);
IOLockLock( self->_detachLock);
WAITING_FOR_DETACH(self) = 0;
thread_wakeup( (void *) self->getIfnet() );
IOLockUnlock( self->_detachLock);
}
bool IONetworkInterface::_setInterfaceProperty(UInt32 value,
UInt32 mask,
UInt32 bytes,
void * addr,
char * key)
{
bool updateOk = true;
UInt32 newValue;
switch (bytes)
{
case 1:
newValue = (*((UInt8 *) addr) & mask) | value;
*((UInt8 *) addr) = (UInt8) newValue;
break;
case 2:
newValue = (*((UInt16 *) addr) & mask) | value;
*((UInt16 *) addr) = (UInt16) newValue;
break;
case 4:
newValue = (*((UInt32 *) addr) & mask) | value;
*((UInt32 *) addr) = (UInt32) newValue;
break;
default:
updateOk = false;
break;
}
return updateOk;
}
#define IO_IFNET_GET(func, type, field, kpi) \
type IONetworkInterface::func(void) const \
{ \
type val = (_backingIfnet != NULL) ? \
kpi(_backingIfnet) : _##field; \
return val; \
}
#define IO_IFNET_SET(func, type, field, kpi) \
bool IONetworkInterface::func(type value) \
{ \
if (_backingIfnet) \
kpi(_backingIfnet, value); \
else \
_##field = value; \
return true; \
}
#define IO_IFNET_RMW(func, type, field, kpi) \
bool IONetworkInterface::func(type set, type clear) \
{ \
if (_backingIfnet) \
kpi(_backingIfnet, set, set | clear); \
else \
_##field = (_##field & ~clear) | set; \
return true; \
}
bool IONetworkInterface::setInterfaceType(UInt8 type)
{
if(_backingIfnet)
return false;
else
_type = type;
return true;
}
UInt8 IONetworkInterface::getInterfaceType(void) const
{
return _type;
}
IO_IFNET_SET(setMaxTransferUnit, UInt32, mtu, ifnet_set_mtu)
IO_IFNET_GET(getMaxTransferUnit, UInt32, mtu, ifnet_mtu)
IO_IFNET_RMW(setFlags, UInt16, flags, ifnet_set_flags)
IO_IFNET_GET(getFlags, UInt16, flags, ifnet_flags)
bool IONetworkInterface::setExtraFlags( UInt32 set, UInt32 clear )
{
_eflags = (_eflags & ~clear) | set;
return true;
}
IO_IFNET_GET(getExtraFlags, UInt32, eflags, ifnet_eflags)
IO_IFNET_SET(setMediaAddressLength, UInt8, addrlen, ifnet_set_addrlen)
IO_IFNET_GET(getMediaAddressLength, UInt8, addrlen, ifnet_addrlen)
IO_IFNET_SET(setMediaHeaderLength, UInt8, hdrlen, ifnet_set_hdrlen)
IO_IFNET_GET(getMediaHeaderLength, UInt8, hdrlen, ifnet_hdrlen)
bool IONetworkInterface::setUnitNumber( UInt16 value )
{
if(_backingIfnet) return false;
if ( setProperty( kIOInterfaceUnit, value, 16 ) )
{
_unit = value;
setProperty( kIOPrimaryInterface, isPrimaryInterface() );
return true;
}
else
return false;
}
IO_IFNET_GET(getUnitNumber, UInt16, unit, ifnet_unit)
bool IONetworkInterface::isRegistered() const
{
return (bool)(getInterfaceState() & kIONetworkInterfaceRegisteredState);
}
bool IONetworkInterface::serializeProperties( OSSerialize * s ) const
{
IONetworkInterface * self = (IONetworkInterface *) this;
self->setProperty( kIOInterfaceType, getInterfaceType(), 8 );
self->setProperty( kIOMaxTransferUnit, getMaxTransferUnit(), 32 );
self->setProperty( kIOInterfaceFlags, getFlags(), 16 );
self->setProperty( kIOInterfaceExtraFlags, getExtraFlags(), 32 );
self->setProperty( kIOMediaAddressLength, getMediaAddressLength(), 8 );
self->setProperty( kIOMediaHeaderLength, getMediaHeaderLength(), 8 );
return super::serializeProperties( s );
}
UInt32 IONetworkInterface::getInterfaceState() const
{
return _stateBits->unsigned32BitValue();
}
UInt32 IONetworkInterface::setInterfaceState( UInt32 set,
UInt32 clear )
{
UInt32 val;
assert( _stateBits );
lock();
val = ( _stateBits->unsigned32BitValue() | set ) & ~clear;
_stateBits->setValue( val );
unlock();
return val;
}
IONetworkData * IONetworkInterface::getNetworkData(const OSSymbol * key) const
{
return OSDynamicCast(IONetworkData, _dataDict->getObject(key));
}
IONetworkData * IONetworkInterface::getNetworkData(const char * key) const
{
return OSDynamicCast(IONetworkData, _dataDict->getObject(key));
}
bool IONetworkInterface::_syncNetworkDataDict()
{
OSDictionary * aCopy = OSDictionary::withDictionary(_dataDict);
bool ret = false;
if (aCopy) {
ret = setProperty(kIONetworkData, aCopy);
aCopy->release();
}
return ret;
}
bool IONetworkInterface::removeNetworkData(const OSSymbol * aKey)
{
bool ret = false;
lock();
do {
if ( getInterfaceState() & kIONetworkInterfaceOpenedState )
break;
_dataDict->removeObject(aKey);
ret = _syncNetworkDataDict();
}
while (0);
unlock();
return ret;
}
bool IONetworkInterface::removeNetworkData(const char * aKey)
{
bool ret = false;
lock();
do {
if ( getInterfaceState() & kIONetworkInterfaceOpenedState )
break;
_dataDict->removeObject(aKey);
ret = _syncNetworkDataDict();
}
while (0);
unlock();
return ret;
}
bool IONetworkInterface::addNetworkData(IONetworkData * aData)
{
bool ret = false;
if (OSDynamicCast(IONetworkData, aData) == 0)
return false;
lock();
if (( getInterfaceState() & kIONetworkInterfaceOpenedState ) == 0)
{
if ((ret = _dataDict->setObject(aData->getKey(), aData)))
ret = _syncNetworkDataDict();
}
unlock();
return ret;
}
IOReturn IONetworkInterface::newUserClient(task_t owningTask,
void * ,
UInt32 type,
IOUserClient ** handler)
{
IOReturn err = kIOReturnSuccess;
IONetworkUserClient * client;
if (type != kIONetworkUserClientTypeID)
return kIOReturnBadArgument;
client = IONetworkUserClient::withTask(owningTask);
if (!client || !client->attach(this) || !client->start(this))
{
if (client)
{
client->detach(this);
client->release();
client = 0;
}
err = kIOReturnNoMemory;
}
*handler = client;
return err;
}
struct IONetworkPowerChangeNotice {
IOService * policyMaker;
IOPMPowerFlags powerFlags;
uint32_t stateNumber;
uint32_t phase;
};
enum {
kPhasePowerStateWillChange = 0x01,
kPhasePowerStateDidChange = 0x02
};
IOReturn
IONetworkInterface::controllerWillChangePowerState(
IONetworkController * controller,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
if ( ( flags & IOPMDeviceUsable ) == 0 )
{
setInterfaceState( kIONetworkInterfaceDisabledState );
}
return kIOReturnSuccess;
}
IOReturn
IONetworkInterface::controllerDidChangePowerState(
IONetworkController * controller,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
if ( flags & IOPMDeviceUsable )
{
setInterfaceState( 0, kIONetworkInterfaceDisabledState );
}
return kIOReturnSuccess;
}
void
IONetworkInterface::powerChangeHandler( void * target,
void * param0,
void * param1 )
{
IONetworkInterface * self = (IONetworkInterface *) target;
IONetworkPowerChangeNotice * notice;
assert( self );
if ( param1 == 0 )
{
self->getController()->executeCommand(
self,
(IONetworkController::Action) &powerChangeHandler,
self,
param0,
(void *) true );
return;
}
notice = (IONetworkPowerChangeNotice *) param0;
assert( notice );
DLOG("%s: power change flags:%08x, state:%d, from:%p, phase:%d\n",
self->getName(), (uint32_t) notice->powerFlags,
notice->stateNumber, notice->policyMaker, notice->phase );
if ( notice->phase == kPhasePowerStateWillChange )
{
self->controllerWillChangePowerState(
self->getController(),
notice->powerFlags,
notice->stateNumber,
notice->policyMaker );
}
else if ( notice->phase == kPhasePowerStateDidChange )
{
self->controllerDidChangePowerState(
self->getController(),
notice->powerFlags,
notice->stateNumber,
notice->policyMaker );
}
}
IOReturn
IONetworkInterface::powerStateWillChangeTo( IOPMPowerFlags powerFlags,
unsigned long stateNumber,
IOService * policyMaker )
{
IONetworkPowerChangeNotice notice;
notice.policyMaker = policyMaker;
notice.powerFlags = powerFlags;
notice.stateNumber = stateNumber;
notice.phase = kPhasePowerStateWillChange;
powerChangeHandler( (void *) this, (void *) ¬ice,
(void *) false );
return kIOPMAckImplied;
}
IOReturn
IONetworkInterface::powerStateDidChangeTo( IOPMPowerFlags powerFlags,
unsigned long stateNumber,
IOService * policyMaker )
{
IONetworkPowerChangeNotice notice;
notice.policyMaker = policyMaker;
notice.powerFlags = powerFlags;
notice.stateNumber = stateNumber;
notice.phase = kPhasePowerStateDidChange;
powerChangeHandler( (void *) this, (void *) ¬ice,
(void *) false );
return kIOPMAckImplied;
}
IOReturn IONetworkInterface::message( UInt32 type, IOService * provider,
void * argument )
{
if (kMessageControllerWillShutdown == type)
{
powerStateWillChangeTo( 0, 0, NULL );
return kIOReturnSuccess;
}
return super::message( type, provider, argument );
}
IOReturn IONetworkInterface::setProperties( OSObject * properties )
{
IOReturn ret;
OSDictionary * dict;
OSObject * obj;
if (( dict = OSDynamicCast( OSDictionary, properties ) ) &&
( obj = dict->getObject( kIONetworkControllerProperties ) ))
{
ret = _controller->setProperties( obj );
}
else
{
ret = super::setProperties(properties);
}
return ret;
}
bool IONetworkInterface::willTerminate( IOService * provider,
IOOptionBits options )
{
DLOG("%s::%s\n", getName(), __FUNCTION__);
setInterfaceState( kIONetworkInterfaceDisabledState );
return super::willTerminate( provider, options );
}
IONetworkData * IONetworkInterface::getParameter(const char * aKey) const
{ return getNetworkData(aKey); }
bool IONetworkInterface::setExtendedFlags(UInt32 flags, UInt32 clear)
{ return true; }
OSMetaClassDefineReservedUsed(IONetworkInterface, 0);
IOReturn IONetworkInterface::attachToDataLinkLayer( IOOptionBits options,
void * parameter )
{
ifnet_init_params iparams;
struct sockaddr_dl *ll_addr;
char buffer[2*sizeof(struct sockaddr_dl)];
UInt32 asize;
IOReturn ret = kIOReturnInternalError;
memset(&iparams, 0, sizeof(iparams));
initIfnetParams(&iparams);
if(ifnet_allocate( &iparams, &_backingIfnet))
return kIOReturnNoMemory;
ifnet_set_offload (_backingIfnet, getIfnetHardwareAssistValue( getController() ));
memset(buffer, 0, sizeof(buffer));
ll_addr = (struct sockaddr_dl *)buffer;
asize = sizeof(buffer) - offsetof(struct sockaddr_dl, sdl_data);
do{
OSData *hardAddr;
hardAddr = OSDynamicCast(OSData, getController()->getProperty(kIOMACAddress));
if(hardAddr == NULL || hardAddr->getLength() > asize)
continue;
asize = hardAddr->getLength();
bcopy(hardAddr->getBytesNoCopy(), ll_addr->sdl_data, asize);
ll_addr->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + asize;
ll_addr->sdl_family = AF_LINK;
ll_addr->sdl_alen = asize;
_syncToBackingIfnet();
if(ifnet_attach(_backingIfnet, ll_addr) == 0)
return kIOReturnSuccess;
}while(false);
ifnet_release(_backingIfnet);
_backingIfnet = NULL;
return ret;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 1);
void IONetworkInterface::detachFromDataLinkLayer( IOOptionBits options,
void * parameter )
{
WAITING_FOR_DETACH(this) = 1;
ifnet_detach( _backingIfnet );
IOLockLock(_detachLock); if ( WAITING_FOR_DETACH( this ) ) {
assert_wait( (event_t) _backingIfnet, THREAD_UNINT );
IOLockUnlock( _detachLock );
thread_block( THREAD_CONTINUE_NULL );
IOLockLock(_detachLock);
}
IOLockUnlock(_detachLock);
ifnet_release(_backingIfnet);
_backingIfnet = NULL;
}
ifnet_t IONetworkInterface::getIfnet() const
{
return _backingIfnet;
}