IOEthernetInterface.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSData.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IOEthernetController.h>
#include "IONetworkUserClient.h"
extern "C" {
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/etherdefs.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <sys/sockio.h>
#include <netinet/in_var.h>
#include <sys/malloc.h>
void arpwhohas(struct arpcom * ac, struct in_addr * addr);
}
#define super IONetworkInterface
OSDefineMetaClassAndStructors( IOEthernetInterface, IONetworkInterface )
OSMetaClassDefineReservedUnused( IOEthernetInterface, 0);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 1);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 2);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 3);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 4);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 5);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 6);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 7);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 8);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 9);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 10);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 11);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 12);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 13);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 14);
OSMetaClassDefineReservedUnused( IOEthernetInterface, 15);
#define kIOEthernetInterfaceNamePrefix "en"
enum {
kFilterOptionDeferIO = 0x0001,
kFilterOptionNotInsideGate = 0x0002,
kFilterOptionNoStateChange = 0x0004,
kFilterOptionDisableZeroBits = 0x0008,
kFilterOptionSyncPendingIO = 0x0010
};
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
UInt32 IOEthernetInterface::getFilters(const OSDictionary * dict,
const OSSymbol * group)
{
OSNumber * num;
UInt32 filters = 0;
assert( dict && group );
if (( num = (OSNumber *) dict->getObject(group) ))
{
filters = num->unsigned32BitValue();
}
return filters;
}
bool IOEthernetInterface::setFilters(OSDictionary * dict,
const OSSymbol * group,
UInt32 filters)
{
OSNumber * num;
bool ret = false;
assert( dict && group );
num = (OSNumber *) dict->getObject(group);
if ( num == 0 )
{
if (( num = OSNumber::withNumber(filters, 32) ))
{
ret = dict->setObject(group, num);
num->release();
}
}
else
{
num->setValue(filters);
ret = true;
}
return ret;
}
#define GET_REQUIRED_FILTERS(g) getFilters(_requiredFilters, (g))
#define GET_ACTIVE_FILTERS(g) getFilters(_activeFilters, (g))
#define GET_SUPPORTED_FILTERS(g) getFilters(_supportedFilters, (g))
#define SET_REQUIRED_FILTERS(g, v) setFilters(_requiredFilters, (g), (v))
#define SET_ACTIVE_FILTERS(g, v) setFilters(_activeFilters, (g), (v))
bool IOEthernetInterface::init(IONetworkController * controller)
{
if ( (_arpcom = (struct arpcom *) IOMalloc(sizeof(*_arpcom))) == 0 )
{
DLOG("IOEthernetInterface: arpcom allocation failed\n");
return false;
}
if ( super::init(controller) == false )
return false;
IONetworkData * data = IONetworkData::withInternalBuffer(
kIOEthernetStatsKey,
sizeof(IOEthernetStats));
if (data)
{
addNetworkData(data);
data->release();
}
_requiredFilters = OSDictionary::withCapacity(4);
_activeFilters = OSDictionary::withCapacity(4);
if ( (_requiredFilters == 0) || (_activeFilters == 0) )
return false;
_supportedFilters = OSDynamicCast(OSDictionary,
controller->getProperty(kIOPacketFilters));
if ( _supportedFilters == 0 ) return false;
_supportedFilters->retain();
if ( !SET_REQUIRED_FILTERS( gIONetworkFilterGroup,
kIOPacketFilterUnicast |
kIOPacketFilterBroadcast )
|| !SET_REQUIRED_FILTERS( gIOEthernetWakeOnLANFilterGroup, 0 )
|| !SET_ACTIVE_FILTERS( gIONetworkFilterGroup, 0 )
|| !SET_ACTIVE_FILTERS( gIOEthernetWakeOnLANFilterGroup, 0 ) )
{
return false;
}
setProperty( kIORequiredPacketFilters, _requiredFilters );
setProperty( kIOActivePacketFilters, _activeFilters );
return true;
}
bool IOEthernetInterface::initIfnet(struct ifnet * ifp)
{
struct arpcom * ac = (struct arpcom *) ifp;
assert(ac);
lock();
bzero(ac, sizeof(*ac));
setInterfaceType( IFT_ETHER );
setMaxTransferUnit( ETHERMTU );
setMediaAddressLength( NUM_EN_ADDR_BYTES );
setMediaHeaderLength( ETHERHDRSIZE );
setFlags( IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS );
unlock();
return super::initIfnet(ifp);
}
void IOEthernetInterface::free()
{
if ( _arpcom )
{
IOFree(_arpcom, sizeof(*_arpcom));
_arpcom = 0;
}
if ( _requiredFilters )
{
_requiredFilters->release();
_requiredFilters = 0;
}
if ( _activeFilters )
{
_activeFilters->release();
_activeFilters = 0;
}
if ( _supportedFilters )
{
_supportedFilters->release();
_supportedFilters = 0;
}
super::free();
}
struct ifnet * IOEthernetInterface::getIfnet() const
{
return (&(_arpcom->ac_if));
}
const char * IOEthernetInterface::getNamePrefix() const
{
return kIOEthernetInterfaceNamePrefix;
}
bool IOEthernetInterface::controllerDidOpen(IONetworkController * ctr)
{
bool ret = false;
OSData * addrData;
IOEthernetAddress * addr;
do {
if ( (ctr == 0) || (super::controllerDidOpen(ctr) == false) )
break;
if ( GET_SUPPORTED_FILTERS(gIONetworkFilterGroup) &
(kIOPacketFilterMulticast | kIOPacketFilterMulticastAll) )
{
setFlags(IFF_MULTICAST);
}
addrData = OSDynamicCast(OSData, ctr->getProperty(kIOMACAddress));
if ( (addrData == 0) || (addrData->getLength() != NUM_EN_ADDR_BYTES) )
{
DLOG("%s: kIOMACAddress property access error (len %d)\n",
getName(), addrData ? addrData->getLength() : 0);
break;
}
addr = (IOEthernetAddress *) addrData->getBytesNoCopy();
#if 1 // Print the address
IOLog("%s: Ethernet address %02x:%02x:%02x:%02x:%02x:%02x\n",
ctr->getName(),
addr->bytes[0],
addr->bytes[1],
addr->bytes[2],
addr->bytes[3],
addr->bytes[4],
addr->bytes[5]);
#endif
bcopy(addr, _arpcom->ac_enaddr, NUM_EN_ADDR_BYTES);
ret = true;
}
while (0);
return ret;
}
void IOEthernetInterface::controllerWillClose(IONetworkController * ctr)
{
super::controllerWillClose(ctr);
}
SInt32 IOEthernetInterface::performCommand( IONetworkController * ctr,
UInt32 cmd,
void * arg0,
void * arg1 )
{
SInt32 ret;
assert( arg0 == _arpcom );
if ( ctr == 0 ) return EINVAL;
switch ( cmd )
{
case SIOCSIFFLAGS:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFADDR:
ret = (int) ctr->executeCommand(
this,
(IONetworkController::Action)
&IOEthernetInterface::performGatedCommand,
this,
ctr,
(void *) cmd,
arg0,
arg1 );
break;
default:
ret = super::performCommand(ctr, cmd, arg0, arg1);
break;
}
return ret;
}
int IOEthernetInterface::performGatedCommand(void * target,
void * arg1_ctr,
void * arg2_cmd,
void * arg3_0,
void * arg4_1)
{
IOEthernetInterface * self = (IOEthernetInterface *) target;
IONetworkController * ctr = (IONetworkController *) arg1_ctr;
SInt ret = EOPNOTSUPP;
if ( self->_controllerLostPower ) return EPWROFF;
self->lock();
switch ( (UInt32) arg2_cmd )
{
case SIOCSIFADDR:
ret = self->syncSIOCSIFADDR(ctr);
break;
case SIOCSIFFLAGS:
ret = self->syncSIOCSIFFLAGS(ctr);
break;
case SIOCADDMULTI:
ret = self->syncSIOCADDMULTI(ctr);
break;
case SIOCDELMULTI:
ret = self->syncSIOCDELMULTI(ctr);
break;
}
self->unlock();
return ret;
}
IOReturn IOEthernetInterface::enableController(IONetworkController * ctr)
{
IOReturn ret = kIOReturnSuccess;
bool enabled = false;
assert(ctr);
do {
if ( _ctrEnabled )
break;
if ( (ret = ctr->enable((IOService *) this)) != kIOReturnSuccess )
break;
enabled = true;
disableFilter(ctr, gIOEthernetWakeOnLANFilterGroup, ~0,
kFilterOptionNoStateChange);
SET_ACTIVE_FILTERS(gIONetworkFilterGroup, 0);
ret = enableFilter(ctr, gIONetworkFilterGroup, 0,
kFilterOptionSyncPendingIO);
if ( ret != kIOReturnSuccess ) break;
syncSIOCADDMULTI(ctr);
_ctrEnabled = true;
} while (false);
if ( enabled && (ret != kIOReturnSuccess) )
{
ctr->disable((IOService *) this);
}
return ret;
}
int IOEthernetInterface::syncSIOCSIFFLAGS(IONetworkController * ctr)
{
UInt16 flags = getFlags();
IOReturn ret = kIOReturnSuccess;
if ( ( ((flags & IFF_UP) == 0) || _controllerLostPower ) &&
( flags & IFF_RUNNING ) )
{
ctr->disable((IOService *) this);
flags &= ~IFF_RUNNING;
_ctrEnabled = false;
}
else if ( ( flags & IFF_UP ) &&
( _controllerLostPower == false ) &&
((flags & IFF_RUNNING) == 0) )
{
if ( (ret = enableController(ctr)) == kIOReturnSuccess )
flags |= IFF_RUNNING;
}
if ( flags & IFF_RUNNING )
{
IOReturn rc;
rc = (flags & IFF_PROMISC) ?
enableFilter(ctr, gIONetworkFilterGroup,
kIOPacketFilterPromiscuous) :
disableFilter(ctr, gIONetworkFilterGroup,
kIOPacketFilterPromiscuous);
if (ret == kIOReturnSuccess) ret = rc;
rc = (flags & IFF_ALLMULTI) ?
enableFilter(ctr, gIONetworkFilterGroup,
kIOPacketFilterMulticastAll) :
disableFilter(ctr, gIONetworkFilterGroup,
kIOPacketFilterMulticastAll);
if (ret == kIOReturnSuccess) ret = rc;
}
setFlags(flags, ~flags);
return errnoFromReturn(ret);
}
SInt IOEthernetInterface::syncSIOCSIFADDR(IONetworkController * ctr)
{
IOReturn ret = kIOReturnSuccess;
setFlags(IFF_UP);
if ( (getFlags() & IFF_RUNNING) == 0 )
{
if ( (ret = enableController(ctr)) == kIOReturnSuccess )
setFlags(IFF_RUNNING);
}
return errnoFromReturn(ret);
}
SInt IOEthernetInterface::syncSIOCADDMULTI(IONetworkController * ctr)
{
IOReturn ret;
ret = enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticast);
if ( ret == kIOReturnSuccess )
{
ret = setupMulticastFilter(ctr);
if ( _mcAddrCount == 0 )
{
IOReturn dret = disableFilter(ctr, gIONetworkFilterGroup,
kIOPacketFilterMulticast);
if (ret == kIOReturnSuccess) ret = dret;
}
}
return errnoFromReturn(ret);
}
SInt IOEthernetInterface::syncSIOCDELMULTI(IONetworkController * ctr)
{
return syncSIOCADDMULTI(ctr);
}
#define getOneFilter(x) ((x) & (~((x) - 1)))
IOReturn
IOEthernetInterface::enableFilter(IONetworkController * ctr,
const OSSymbol * group,
UInt32 filters,
IOOptionBits options = 0)
{
IOReturn ret = kIOReturnSuccess;
if ( options & kFilterOptionNotInsideGate )
{
options &= ~kFilterOptionNotInsideGate;
return ctr->executeCommand(
this,
(IONetworkController::Action)
&IOEthernetInterface::enableFilter,
this,
(void *) ctr,
(void *) group,
(void *) filters,
(void *) options );
}
if ( options & kFilterOptionDisableZeroBits )
{
ret = disableFilter(ctr, group, ~filters, options);
if ( ret != kIOReturnSuccess) return ret;
}
if (( GET_SUPPORTED_FILTERS(group) & filters ) != filters)
return kIOReturnUnsupported;
do {
UInt32 reqFilters = GET_REQUIRED_FILTERS(group) | filters;
UInt32 actFilters = GET_ACTIVE_FILTERS(group);
UInt32 resFilters = ( actFilters ^ reqFilters );
if ( (options & kFilterOptionSyncPendingIO) == 0 )
{
resFilters &= filters;
}
while ( resFilters && ((options & kFilterOptionDeferIO) == 0) )
{
UInt32 oneFilter = getOneFilter(resFilters);
ret = ctr->enablePacketFilter(group, oneFilter,
actFilters, options);
if ( ret != kIOReturnSuccess ) break;
resFilters &= ~oneFilter;
actFilters |= oneFilter;
}
if ( (options & kFilterOptionNoStateChange) == 0 )
SET_REQUIRED_FILTERS(group, reqFilters);
SET_ACTIVE_FILTERS(group, actFilters);
}
while (false);
return ret;
}
IOReturn
IOEthernetInterface::disableFilter(IONetworkController * ctr,
const OSSymbol * group,
UInt32 filters,
IOOptionBits options = 0)
{
IOReturn ret = kIOReturnSuccess;
#if 0
if ( options & kFilterOptionNotInsideGate )
{
options &= ~kFilterOptionNotInsideGate;
return ctr->executeCommand(
this,
(IONetworkController::Action)
&IOEthernetInterface::disableFilter,
this,
(void *) ctr,
(void *) group,
(void *) filters,
(void *) options );
}
#endif
do {
UInt32 reqFilters = GET_REQUIRED_FILTERS(group) & ~filters;
UInt32 actFilters = GET_ACTIVE_FILTERS(group);
UInt32 resFilters = ( actFilters ^ reqFilters ) & filters;
while ( resFilters && ((options & kFilterOptionDeferIO) == 0) )
{
UInt32 oneFilter = getOneFilter(resFilters);
ret = ctr->disablePacketFilter(group, oneFilter,
actFilters, options);
if ( ret != kIOReturnSuccess ) break;
resFilters &= ~oneFilter;
actFilters &= ~oneFilter;
}
if ( (options & kFilterOptionNoStateChange) == 0 )
SET_REQUIRED_FILTERS(group, reqFilters);
SET_ACTIVE_FILTERS(group, actFilters);
}
while (false);
return ret;
}
IOReturn
IOEthernetInterface::setupMulticastFilter(IONetworkController * ctr)
{
void * multiAddrs = 0;
UInt mcount;
OSData * mcData = 0;
struct ifnet * ifp = (struct ifnet *) _arpcom;
struct ifmultiaddr * ifma;
IOReturn ret = kIOReturnSuccess;
bool ok;
assert(ifp);
mcount = 0;
for (ifma = ifp->if_multiaddrs.lh_first;
ifma != NULL;
ifma = ifma->ifma_link.le_next)
{
if ((ifma->ifma_addr->sa_family == AF_UNSPEC) ||
(ifma->ifma_addr->sa_family == AF_LINK))
mcount++;
}
_mcAddrCount = mcount;
if ( mcount )
{
char * addrp;
mcData = OSData::withCapacity(mcount * NUM_EN_ADDR_BYTES);
if (!mcData)
{
DLOG("%s: no memory for multicast address list\n", getName());
return kIOReturnNoMemory;
}
for (ifma = ifp->if_multiaddrs.lh_first;
ifma != NULL;
ifma = ifma->ifma_link.le_next)
{
if (ifma->ifma_addr->sa_family == AF_UNSPEC)
addrp = &ifma->ifma_addr->sa_data[0];
else
if (ifma->ifma_addr->sa_family == AF_LINK)
addrp = LLADDR((struct sockaddr_dl *) ifma->ifma_addr);
else
continue;
ok = mcData->appendBytes((const void *) addrp, NUM_EN_ADDR_BYTES);
assert(ok);
}
multiAddrs = (void *) mcData->getBytesNoCopy();
assert(multiAddrs);
}
ret = ((IOEthernetController *)ctr)->setMulticastList(
(IOEthernetAddress *) multiAddrs,
mcount);
if (mcData)
{
if (ret == kIOReturnSuccess)
setProperty(kIOMulticastAddressList, mcData);
mcData->release();
}
else {
removeProperty(kIOMulticastAddressList);
}
return ret;
}
IOReturn
IOEthernetInterface::controllerWillChangePowerState(
IONetworkController * ctr,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
if ( ( (flags & IOPMDeviceUsable ) == 0) &&
( _controllerLostPower == false ) &&
_ctrEnabled )
{
UInt32 filters;
_controllerLostPower = true;
ctr->getAggressiveness( kPMEthernetWakeOnLANSettings, &filters );
filters &= GET_SUPPORTED_FILTERS( gIOEthernetWakeOnLANFilterGroup );
OSNumber * linkStatusNumber = (OSNumber *)
ctr->getProperty( kIOLinkStatus );
if ( ( linkStatusNumber == 0 ) ||
( ( linkStatusNumber->unsigned32BitValue() &
(kIONetworkLinkValid | kIONetworkLinkActive) ) ==
kIONetworkLinkValid ) )
{
filters &= ~( kIOEthernetWakeOnMagicPacket |
kIOEthernetWakeOnPacketAddressMatch );
}
enableFilter( ctr,
gIOEthernetWakeOnLANFilterGroup,
filters,
kFilterOptionNoStateChange |
kFilterOptionSyncPendingIO );
syncSIOCSIFFLAGS(ctr);
}
return super::controllerWillChangePowerState( ctr, flags,
stateNumber,
policyMaker );
}
IOReturn
IOEthernetInterface::controllerDidChangePowerState(
IONetworkController * ctr,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
IOReturn ret = super::controllerDidChangePowerState( ctr, flags,
stateNumber,
policyMaker );
if ( ( flags & IOPMDeviceUsable ) && ( _controllerLostPower == true ) )
{
_controllerLostPower = false;
syncSIOCSIFFLAGS(ctr);
}
return ret;
}
#define kIONetworkInterfaceProperties "IONetworkInterfaceProperties"
IOReturn IOEthernetInterface::setProperties( OSObject * properties )
{
IOReturn ret;
OSDictionary * dict = (OSDictionary *) properties;
OSNumber * num;
ret = super::setProperties(properties);
if ( (ret == kIOReturnSuccess) || (ret == kIOReturnUnsupported) )
{
dict = OSDynamicCast( OSDictionary,
dict->getObject(kIONetworkInterfaceProperties) );
if ( dict )
{
dict = OSDynamicCast( OSDictionary,
dict->getObject(kIORequiredPacketFilters) );
if ( dict )
{
num = OSDynamicCast( OSNumber,
dict->getObject(kIOEthernetWakeOnLANFilterGroup) );
if ( num )
{
ret = enableFilter( getController(),
gIOEthernetWakeOnLANFilterGroup,
num->unsigned32BitValue(),
kFilterOptionDeferIO |
kFilterOptionNotInsideGate |
kFilterOptionDisableZeroBits );
}
}
}
}
return ret;
}