IOEthernetInterface.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSData.h>
#include <IOEthernetInterface.h>
#include <IOEthernetController.h>
#include "IONetworkUserClient.h"
#include "IONetworkDebug.h"
#include "IONetworkControllerPrivate.h"
#include <IOKit/pwr_mgt/RootDomain.h> // publishFeature()
extern "C" {
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_ether.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/dlil.h>
#include <net/bpf.h>
#include <netinet/if_ether.h>
#include <sys/sockio.h>
#include <sys/malloc.h>
}
#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 {
kFilterOptionNoStateChange = 0x0004,
kFilterOptionSyncPendingIO = 0x0010
};
#define _altMTU _reserved->altMTU
#define _publishedFeatureID _reserved->publishedFeatureID
#define _supportedWakeFilters _reserved->supportedWakeFilters
#define _disabledWakeFilters _reserved->disabledWakeFilters
#define _wompEnabledAssertionID _reserved->wompEnabledAssertionID
#define kWOMPFeatureKey "WakeOnMagicPacket"
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)
{
OSObject * obj;
_reserved = IONew( ExpansionData, 1 );
if( _reserved == 0 )
return false;
memset(_reserved, 0, sizeof(ExpansionData));
if ( super::init(controller) == false )
return false;
setInterfaceType(IFT_ETHER);
setMaxTransferUnit( ETHERMTU );
setMediaAddressLength( ETHER_ADDR_LEN );
setMediaHeaderLength( ETHER_HDR_LEN );
setFlags( IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS,
IFF_RUNNING | IFF_MULTICAST );
IONetworkData * data = IONetworkData::withInternalBuffer(
kIOEthernetStatsKey,
sizeof(IOEthernetStats));
if (data)
{
addNetworkData(data);
data->release();
}
_inputEventThreadCall = thread_call_allocate(
handleEthernetInputEvent, this );
if (!_inputEventThreadCall)
return false;
_requiredFilters = OSDictionary::withCapacity(2);
_activeFilters = OSDictionary::withCapacity(2);
if ( (_requiredFilters == 0) || (_activeFilters == 0) )
return false;
obj = controller->copyProperty(kIOPacketFilters);
if (obj && ((_supportedFilters = OSDynamicCast(OSDictionary, obj)) == 0))
obj->release();
if (!_supportedFilters)
return false;
_supportedWakeFilters = GET_SUPPORTED_FILTERS(
gIOEthernetWakeOnLANFilterGroup );
obj = _supportedFilters->getObject(
gIOEthernetDisabledWakeOnLANFilterGroup );
_disabledWakeFilters = OSDynamicCast(OSNumber, obj);
if (_disabledWakeFilters)
_disabledWakeFilters->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;
}
_publishedFeatureID = 0;
setProperty( kIORequiredPacketFilters, _requiredFilters );
setProperty( kIOActivePacketFilters, _activeFilters );
return true;
}
static const u_char ether_broadcast_addr[ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
bool IOEthernetInterface::initIfnetParams(struct ifnet_init_params *params)
{
OSData *uniqueID;
super::initIfnetParams( params );
uniqueID = OSDynamicCast(OSData, getProvider()->getProperty(kIOMACAddress));
if ( (uniqueID == 0) || (uniqueID->getLength() != ETHER_ADDR_LEN) )
{
DLOG("%s: kIOMACAddress property access error (len %d)\n",
getName(), uniqueID ? uniqueID->getLength() : 0);
return false;
}
params->uniqueid = uniqueID->getBytesNoCopy();
params->uniqueid_len = uniqueID->getLength();
params->family = APPLE_IF_FAM_ETHERNET;
params->demux = ether_demux;
params->add_proto = ether_add_proto;
params->del_proto = ether_del_proto;
params->framer = ether_frameout;
params->check_multi = ether_check_multi;
params->broadcast_addr = ether_broadcast_addr;
params->broadcast_len = sizeof(ether_broadcast_addr);
return true;
}
void IOEthernetInterface::free()
{
if ( _requiredFilters )
{
_requiredFilters->release();
_requiredFilters = 0;
}
if ( _activeFilters )
{
_activeFilters->release();
_activeFilters = 0;
}
if ( _supportedFilters )
{
_supportedFilters->release();
_supportedFilters = 0;
}
if ( _inputEventThreadCall )
{
thread_call_free( _inputEventThreadCall );
_inputEventThreadCall = 0;
}
if ( _reserved )
{
if (kIOPMUndefinedDriverAssertionID != _wompEnabledAssertionID)
{
getPMRootDomain()->releasePMAssertion(_wompEnabledAssertionID);
_wompEnabledAssertionID = kIOPMUndefinedDriverAssertionID;
}
if (_disabledWakeFilters)
{
_disabledWakeFilters->release();
_disabledWakeFilters = 0;
}
IODelete( _reserved, ExpansionData, 1 );
_reserved = 0;
}
super::free();
}
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);
}
if ( _supportedWakeFilters & kIOEthernetWakeOnMagicPacket )
{
IOPMrootDomain * root = getPMRootDomain();
if ( root ) root->publishFeature( kWOMPFeatureKey,
kIOPMSupportedOnAC | kIOPMSupportedOnUPS,
(uint32_t *)&_publishedFeatureID);
}
addrData = OSDynamicCast(OSData, ctr->getProperty(kIOMACAddress));
if ( (addrData == 0) || (addrData->getLength() != ETHER_ADDR_LEN) )
{
DLOG("%s: kIOMACAddress property access error (len %d)\n",
getName(), addrData ? addrData->getLength() : 0);
break;
}
addr = (IOEthernetAddress *) addrData->getBytesNoCopy();
DLOG("%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]);
ret = true;
}
while (0);
return ret;
}
void IOEthernetInterface::controllerWillClose(IONetworkController * ctr)
{
if ( _supportedWakeFilters & kIOEthernetWakeOnMagicPacket )
{
IOPMrootDomain * root = getPMRootDomain();
if ( root ) root->removePublishedFeature( _publishedFeatureID );
_publishedFeatureID = 0;
}
super::controllerWillClose(ctr);
}
SInt32 IOEthernetInterface::performCommand( IONetworkController * ctr,
unsigned long cmd,
void * arg0,
void * arg1 )
{
SInt32 ret;
if ( ctr == 0 ) return EINVAL;
switch ( cmd )
{
case SIOCSIFFLAGS:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFADDR:
case SIOCSIFMTU:
case SIOCSIFDEVMTU:
case SIOCGIFDEVMTU:
case SIOCSIFLLADDR:
case SIOCSIFCAP:
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;
struct ifreq * ifr = (struct ifreq *) arg4_1;
SInt ret = EOPNOTSUPP;
if ( self->_controllerLostPower ||
( self->getInterfaceState() & kIONetworkInterfaceDisabledState ) )
return EPWROFF;
switch ( (uintptr_t) 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;
case SIOCSIFMTU:
ret = self->syncSIOCSIFMTU( ctr, ifr, 0 );
break;
case SIOCSIFDEVMTU:
ret = self->syncSIOCSIFMTU(ctr, ifr, 1);
break;
case SIOCGIFDEVMTU:
ret = self->syncSIOCGIFDEVMTU(ctr, ifr);
break;
case SIOCSIFLLADDR:
ret = self->syncSIOCSIFLLADDR( ctr, ifr->ifr_addr.sa_data,
ifr->ifr_addr.sa_len );
break;
case SIOCSIFCAP:
ret = self->syncSIOCSIFCAP( ctr, ifr );
break;
}
return ret;
}
IOReturn IOEthernetInterface::enableController(IONetworkController * ctr)
{
IOReturn ret = kIOReturnSuccess;
bool enabled = false;
assert(ctr);
do {
if ( _ctrEnabled )
break;
if ( (ret = ctr->doEnable(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);
OSData * lladdr = OSDynamicCast(OSData, getProperty(kIOMACAddress));
if ( lladdr && lladdr->getLength() == ETHER_ADDR_LEN )
{
ctr->setHardwareAddress( lladdr->getBytesNoCopy(),
lladdr->getLength() );
}
_ctrEnabled = true;
reportInterfaceWakeFlags(ctr);
} while (false);
if ( enabled && (ret != kIOReturnSuccess) )
{
ctr->doDisable(this);
}
return ret;
}
int IOEthernetInterface::syncSIOCSIFFLAGS(IONetworkController * ctr)
{
UInt16 flags = getFlags();
IOReturn ret = kIOReturnSuccess;
if ( ( ((flags & IFF_UP) == 0) || _controllerLostPower ) &&
( flags & IFF_RUNNING ) )
{
ctr->doDisable(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 MTU_TO_FRAMESIZE(x) \
((x) + kIOEthernetCRCSize + sizeof(struct ether_header))
#define FRAMESIZE_TO_MTU(x) \
((x) - kIOEthernetCRCSize - sizeof(struct ether_header))
int IOEthernetInterface::syncSIOCGIFDEVMTU( IONetworkController * ctr,
struct ifreq * ifr )
{
IOReturn ret;
UInt32 size;
ifr->ifr_devmtu.ifdm_current = max(_altMTU, getMaxTransferUnit());
ret = ctr->getMaxPacketSize( &size );
if(ret == kIOReturnSuccess )
{
ifr->ifr_devmtu.ifdm_max = FRAMESIZE_TO_MTU(size);
ret = ctr->getMinPacketSize( &size );
if(ret == kIOReturnSuccess)
ifr->ifr_devmtu.ifdm_min = FRAMESIZE_TO_MTU( size );
}
return errnoFromReturn( ret );
}
int IOEthernetInterface::syncSIOCSIFMTU( IONetworkController * ctr,
struct ifreq * ifr,
bool setAltMTU)
{
IOReturn ret = kIOReturnSuccess;
UInt32 newControllerMTU, oldControllerMTU;
UInt32 softMTU = getMaxTransferUnit();
UInt32 requestedMTU = ifr->ifr_mtu;
UInt32 maxFrameSize = kIOEthernetMaxPacketSize; UInt32 minFrameSize = kIOEthernetMinPacketSize;
UInt32 tempSize;
if ( ctr->getMaxPacketSize( &tempSize ) == kIOReturnSuccess )
maxFrameSize = max( tempSize, kIOEthernetMaxPacketSize );
if ( MTU_TO_FRAMESIZE( requestedMTU ) > maxFrameSize )
return EINVAL;
ctr->getMinPacketSize( &minFrameSize );
if ( MTU_TO_FRAMESIZE( requestedMTU ) < minFrameSize && !(setAltMTU && requestedMTU==0)) return EINVAL;
newControllerMTU = max(requestedMTU, setAltMTU ? softMTU : _altMTU);
oldControllerMTU = max(softMTU, _altMTU);
if(newControllerMTU != oldControllerMTU &&
( (MTU_TO_FRAMESIZE(newControllerMTU) > kIOEthernetMaxPacketSize) ||
(MTU_TO_FRAMESIZE(oldControllerMTU) > kIOEthernetMaxPacketSize) )
)
{
ret = ctr->setMaxPacketSize( max(MTU_TO_FRAMESIZE(newControllerMTU), kIOEthernetMaxPacketSize)); }
if(ret == kIOReturnSuccess) {
if(setAltMTU)
_altMTU = requestedMTU;
else
setMaxTransferUnit( requestedMTU );
}
return errnoFromReturn( ret );
}
int IOEthernetInterface::syncSIOCSIFLLADDR( IONetworkController * ctr,
const char * lladdr, int len )
{
unsigned char tempaddr[kIOEthernetAddressSize];
OSData *hardAddr;
if(len != kIOEthernetAddressSize)
return EINVAL;
if (_ctrEnabled != true)
return (ENETDOWN);
hardAddr = OSDynamicCast(OSData, getProperty(kIOMACAddress));
if(hardAddr && hardAddr->getLength() == kIOEthernetAddressSize)
bcopy(hardAddr->getBytesNoCopy(), tempaddr, kIOEthernetAddressSize);
if ( ctr->setHardwareAddress( lladdr, len ) == kIOReturnSuccess )
{
if( ifnet_set_lladdr(getIfnet(), lladdr, len) ) {
if(hardAddr)
ctr->setHardwareAddress(tempaddr, sizeof(tempaddr));
return EINVAL;
}
setProperty(kIOMACAddress, (void *)lladdr, len);
DLOG("%s: SIOCSIFLLADDR %02x:%02x:%02x:%02x:%02x:%02x\n",
ctr->getName(),
lladdr[0], lladdr[1], lladdr[2],
lladdr[3], lladdr[4], lladdr[5]);
}
return 0;
}
int IOEthernetInterface::syncSIOCSIFCAP( IONetworkController * ctr,
struct ifreq * ifr )
{
int result = EINVAL;
if(ifr)
{
u_int32_t curAV;
u_int32_t reqAV;
curAV = ifr->ifr_curcap & IFCAP_AV; reqAV = ifr->ifr_reqcap & IFCAP_AV;
if(curAV != reqAV)
{
IOEthernetController *controller = OSDynamicCast(IOEthernetController, ctr);
if(controller)
{
IOEthernetControllerAVBStateEvent event = reqAV ? kIOEthernetControllerAVBStateEventEnable : kIOEthernetControllerAVBStateEventDisable;
result = errnoFromReturn(controller->changeAVBControllerState(event));
if(!result)
{
result = ifnet_set_capabilities_enabled(getIfnet(), reqAV, IFCAP_AV);
}
}
}
else
{
result = 0;
}
}
return result;
}
#define getOneFilter(x) ((x) & (~((x) - 1)))
IOReturn
IOEthernetInterface::enableFilter_Wrapper(
IOEthernetInterface * self,
IONetworkController * ctr,
const OSSymbol * group,
UInt32 filters,
IOOptionBits options)
{
return self->enableFilter(ctr, group, filters, options);
}
IOReturn
IOEthernetInterface::enableFilter(IONetworkController * ctr,
const OSSymbol * group,
UInt32 filters,
IOOptionBits options)
{
IOReturn ret = kIOReturnSuccess;
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 )
{
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)
{
IOReturn ret = kIOReturnSuccess;
do {
UInt32 reqFilters = GET_REQUIRED_FILTERS(group) & ~filters;
UInt32 actFilters = GET_ACTIVE_FILTERS(group);
UInt32 resFilters = ( actFilters ^ reqFilters ) & filters;
while ( resFilters )
{
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;
ifnet_t interface;
struct sockaddr dlAddress;
IOReturn ret = kIOReturnSuccess;
bool ok;
ifmultiaddr_t *addressList;
interface = getIfnet();
assert(interface);
if(ifnet_get_multicast_list(interface, &addressList))
return kIOReturnNoMemory;
mcount = 0;
for(int i=0; addressList[i]; i++)
{
ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress));
if (dlAddress.sa_family == AF_UNSPEC || dlAddress.sa_family == AF_LINK)
mcount++;
}
_mcAddrCount = mcount;
if ( mcount )
{
char * addrp;
mcData = OSData::withCapacity(mcount * ETHER_ADDR_LEN);
if (!mcData)
{
DLOG("%s: no memory for multicast address list\n", getName());
ifnet_free_multicast_list(addressList);
return kIOReturnNoMemory;
}
for(int i = 0; addressList[i]; i++)
{
ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress));
if (dlAddress.sa_family == AF_UNSPEC)
addrp = &dlAddress.sa_data[0];
else if (dlAddress.sa_family == AF_LINK)
addrp = LLADDR((struct sockaddr_dl *)&dlAddress);
else
continue;
ok = mcData->appendBytes((const void *) addrp, ETHER_ADDR_LEN);
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);
}
ifnet_free_multicast_list(addressList);
return ret;
}
IOReturn
IOEthernetInterface::controllerWillChangePowerState(
IONetworkController * ctr,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
if ( ( (flags & IOPMDeviceUsable ) == 0) &&
( _controllerLostPower == false ) )
{
_controllerLostPower = true;
if (_ctrEnabled && !ctr->isInactive())
{
if (policyMaker)
{
unsigned long filters;
ctr->getAggressiveness( kPMEthernetWakeOnLANSettings, &filters );
filters &= _supportedWakeFilters;
OSNumber * linkStatusNumber = (OSNumber *)
ctr->getProperty( kIOLinkStatus );
if ( ( linkStatusNumber == 0 ) ||
( ( linkStatusNumber->unsigned32BitValue() &
(kIONetworkLinkValid | kIONetworkLinkActive) ) ==
kIONetworkLinkValid ) )
{
filters &= ~( kIOEthernetWakeOnMagicPacket |
kIOEthernetWakeOnPacketAddressMatch );
}
enableFilter( ctr,
gIOEthernetWakeOnLANFilterGroup,
(UInt32)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;
if (!ctr->isInactive())
syncSIOCSIFFLAGS(ctr);
}
return ret;
}
bool IOEthernetInterface::willTerminate( IOService * provider,
IOOptionBits options )
{
bool ret = super::willTerminate( provider, options );
if (_ctrEnabled && (getController() == provider))
{
DLOG("IOEthernetInterface::willTerminate disabling controller\n");
getController()->doDisable( this );
_ctrEnabled = false;
}
return ret;
}
IOReturn IOEthernetInterface::attachToDataLinkLayer( IOOptionBits options,
void * parameter )
{
IOReturn ret = super::attachToDataLinkLayer( options, parameter );
if ( ret == kIOReturnSuccess )
{
ifnet_set_baudrate(getIfnet(), 0);
bpfattach( getIfnet(), DLT_EN10MB, sizeof(struct ether_header) );
IOEthernetController *ctrl = OSDynamicCast(IOEthernetController, getController());
if(ctrl && ctrl->getAVBSupport(NULL))
{
ifnet_set_capabilities_supported(getIfnet(), IFCAP_AV, IFCAP_AV);
if(kIOEthernetControllerAVBStateDisabled != ctrl->getControllerAVBState())
{
ifnet_set_capabilities_enabled(getIfnet(), IFCAP_AV, IFCAP_AV);
}
}
}
return ret;
}
#define VLAN_HEADER_LEN 4
void IOEthernetInterface::_fixupVlanPacket(mbuf_t mt, u_int16_t vlan_tag, int inputPacket)
{
mbuf_t newmb;
mbuf_t chain;
size_t remainingBytes;
size_t copyBytes = 0; char * destptr;
if( mbuf_gethdr(MBUF_DONTWAIT, MT_DATA, &newmb) )
return;
mbuf_setlen(newmb, ETHER_ADDR_LEN*2 + VLAN_HEADER_LEN);
mbuf_pkthdr_setlen(newmb, mbuf_pkthdr_len( mt ) + VLAN_HEADER_LEN);
mbuf_pkthdr_setrcvif(newmb, mbuf_pkthdr_rcvif( mt ) );
chain = mt;
remainingBytes = ETHER_ADDR_LEN*2;
destptr = (char *)mbuf_data( newmb );
while(chain && remainingBytes)
{
copyBytes = remainingBytes > mbuf_len( chain ) ? mbuf_len( chain ): remainingBytes;
remainingBytes -= copyBytes;
bcopy( mbuf_data( chain ), destptr, copyBytes);
destptr += copyBytes;
if (mbuf_len( chain ) == copyBytes) {
chain = mbuf_next( chain ); copyBytes = 0; }
}
if(chain==0 || remainingBytes)
{
mbuf_freem( newmb );
return; }
mbuf_setdata(chain, (char *)mbuf_data( chain ) + copyBytes, mbuf_len( chain ) - copyBytes );
*(short *)(destptr) = htons(ETHERTYPE_VLAN); *(short *)(destptr + 2) = htons( vlan_tag ); mbuf_setnext( newmb, chain );
if(inputPacket)
super::feedPacketInputTap( newmb );
else
super::feedPacketOutputTap( newmb );
mbuf_setnext( newmb, NULL );
mbuf_freem( newmb );
mbuf_setdata( chain, (char *)mbuf_data( chain ) - copyBytes, mbuf_len( chain ) + copyBytes );
}
void IOEthernetInterface::feedPacketInputTap(mbuf_t mt)
{
u_int16_t vlan;
if( mbuf_get_vlan_tag(mt, &vlan) == 0) _fixupVlanPacket(mt, vlan, 1);
else
super::feedPacketInputTap(mt);
}
void IOEthernetInterface::feedPacketOutputTap(mbuf_t mt)
{
u_int16_t vlan;
if( mbuf_get_vlan_tag(mt, &vlan) == 0) _fixupVlanPacket(mt, vlan, 0);
else
super::feedPacketOutputTap(mt);
}
bool IOEthernetInterface::inputEvent( UInt32 type, void * data )
{
if ((type == kIONetworkEventTypeLinkUp) ||
(type == kIONetworkEventTypeLinkDown) ||
(type == kIONetworkEventWakeOnLANSupportChanged))
{
IONetworkController * ctr = getController();
if (ctr && !ctr->isInactive())
{
retain();
ctr->retain();
if (thread_call_enter( _inputEventThreadCall ) == TRUE)
{
release();
ctr->release();
}
}
}
if ((type == kIONetworkEventTypeLinkUp) ||
(type == kIONetworkEventTypeLinkDown))
{
ifnet_t ifp = getIfnet();
const IONetworkLinkEventData * linkData =
(const IONetworkLinkEventData *) data;
if (ifp && linkData)
{
if ((IOMediumGetNetworkType(linkData->linkType) == kIOMediumEthernet))
{
if (type == kIONetworkEventTypeLinkUp)
{
ifnet_set_link_quality(ifp, IFNET_LQM_THRESH_GOOD);
}
else
{
ifnet_set_link_quality(ifp, IFNET_LQM_THRESH_OFF);
}
}
}
}
return super::inputEvent(type, data);
}
void IOEthernetInterface::handleEthernetInputEvent(
thread_call_param_t param0,
thread_call_param_t )
{
IOEthernetInterface * me = (IOEthernetInterface *) param0;
IONetworkController * ctr;
if (me)
{
ctr = me->getController();
if (ctr)
{
ctr->executeCommand(
me,
OSMemberFunctionCast(
IONetworkController::Action, me,
&IOEthernetInterface::reportInterfaceWakeFlags),
me,
ctr );
ctr->release();
}
me->release();
}
}
void IOEthernetInterface::reportInterfaceWakeFlags( IONetworkController * ctr )
{
ifnet_t ifnet;
OSNumber * number;
unsigned long wakeSetting = 0;
uint32_t disabled = 0;
uint32_t filters = 0;
uint32_t linkStatus = 0;
ifnet = getIfnet();
if (!ifnet || !ctr)
return;
if (_controllerLostPower)
{
DLOG("en%u: controllerLostPower\n", getUnitNumber());
return;
}
do {
if (!_ctrEnabled ||
((_supportedWakeFilters & kIOEthernetWakeOnMagicPacket) == 0))
{
DLOG("en%u: ctrEnabled = %x, WakeFilters = %x\n",
getUnitNumber(), _ctrEnabled, _supportedWakeFilters);
break;
}
if (_disabledWakeFilters)
{
if ( ctr->getPacketFilters(
gIOEthernetDisabledWakeOnLANFilterGroup,
(UInt32 *) &disabled ) != kIOReturnSuccess )
{
disabled = 0;
}
_disabledWakeFilters->setValue( disabled );
}
getAggressiveness(kPMEthernetWakeOnLANSettings, &wakeSetting);
filters = wakeSetting & _supportedWakeFilters & ~disabled;
DLOG("en%u: WakeSetting = %lx, WakeFilters = %x, disabled = %x\n",
getUnitNumber(), wakeSetting, _supportedWakeFilters, disabled);
if ((kIOEthernetWakeOnMagicPacket & filters) == 0)
break;
number = OSDynamicCast(OSNumber, ctr->getProperty(kIOLinkStatus));
if (!number)
{
filters = 0;
break;
}
linkStatus = number->unsigned32BitValue();
if ((linkStatus & (kIONetworkLinkValid | kIONetworkLinkActive)) ==
kIONetworkLinkValid)
{
filters = 0;
}
}
while (false);
filters &= IFNET_WAKE_ON_MAGIC_PACKET;
if (filters != (ifnet_get_wake_flags(ifnet) & IFNET_WAKE_ON_MAGIC_PACKET))
{
ifnet_set_wake_flags(ifnet, filters, IFNET_WAKE_ON_MAGIC_PACKET);
DLOG("en%u: ifnet_set_wake_flags = %x\n", getUnitNumber(), filters);
if (kIOPMUndefinedDriverAssertionID == _wompEnabledAssertionID)
{
_wompEnabledAssertionID = getPMRootDomain()->createPMAssertion(
kIOPMDriverAssertionMagicPacketWakeEnabledBit,
(filters & IFNET_WAKE_ON_MAGIC_PACKET) ?
kIOPMDriverAssertionLevelOn : kIOPMDriverAssertionLevelOff,
this, getName());
}
else
{
getPMRootDomain()->setPMAssertionLevel(_wompEnabledAssertionID,
(filters & IFNET_WAKE_ON_MAGIC_PACKET) ?
kIOPMDriverAssertionLevelOn : kIOPMDriverAssertionLevelOff);
}
}
}