Source to iokit/Families/IONetworking/IOEthernetInterface.cpp
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* IOEthernetInterface.cpp
*
* HISTORY
* 8-Jan-1999 Joe Liu (jliu) created.
*
*/
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSData.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IOEthernetController.h>
#include <IOKit/network/IONetworkUserClient.h>
extern "C" {
#include <sys/param.h>
#include <sys/errno.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 )
// The name prefix for all BSD Ethernet interfaces.
//
#define kIOEthernetInterfaceNamePrefix "en"
//---------------------------------------------------------------------------
// Macros
#define CTLR_SYNC_REQ(ctlr, action, ret, args...) \
ctlr->syncRequest(this, this, (IONetworkAction) action, \
(UInt32 *) ret, ## args)
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG
#endif
//---------------------------------------------------------------------------
// Initialize an IOEthernetInterface instance. Instance variables are
// initialized, and an arpcom structure is allocated.
bool IOEthernetInterface::init(OSDictionary * properties = 0)
{
// Initialize instance variables.
//
_arpcom = 0; // arpcom structure.
_mcAddrCount = 0; // number of multicast addresses.
_features = 0; // controller's family specific features.
_activeFilters = 0; // active packet filters.
_availableFilters = 0; // available packet filters.
_controllerEnabled = false;
// Allocate an arpcom structure, clear it, then call super::init().
// We expect our superclass to call getIfnet() during its init()
// method. So we have to create arpcom before calling super::init().
if ((_arpcom = (struct arpcom *) IOMalloc(sizeof(*_arpcom))) == 0)
{
DLOG("IOEthernetInterface: arpcom allocation failed\n");
return false;
}
bzero(_arpcom, sizeof(*_arpcom));
// Pass the init() call to our superclass.
//
if (!super::init(properties))
return false;
// Add an IONetworkData with room to hold an IOEthernetStats structure.
// This class does not reference the data object created, and no harm
// is done if the data object is released.
IONetworkData * data = IONetworkData::withName(kIOEthernetStatsKey,
sizeof(IOEthernetStats));
if (data) {
addNetworkData(data);
data->release();
}
return true;
}
//---------------------------------------------------------------------------
// Initialize the ifnet structure. The argument specified is
// a pointer to an ifnet structure obtained through getIfnet().
// IOEthernetInterface will initialize this structure in a manner that
// is appropriate for Ethernet interfaces.
//
// ifp: Pointer to the ifnet structure to be initialized.
//
// Returns true if successful, false otherwise.
bool IOEthernetInterface::initIfnet(struct ifnet * ifp)
{
struct arpcom * ac = (struct arpcom *) ifp;
assert(ac);
lock(); // lock interface state
bzero(ac, sizeof(*ac));
// Set defaults suitable for Ethernet interfaces.
setInterfaceType(IFT_ETHER);
setMaxTransferUnit(ETHERMTU);
setMediaAddressLength(NUM_EN_ADDR_BYTES);
setMediaHeaderLength(ETHERHDRSIZE);
setFlags(IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS);
unlock(); // unlock interface state
return super::initIfnet(ifp);
}
//---------------------------------------------------------------------------
// Free the IOEthernetInterface instance. The memory allocated
// for the arpcom structure is released.
void IOEthernetInterface::free()
{
if (_arpcom) IOFree(_arpcom, sizeof(*_arpcom));
super::free();
}
//---------------------------------------------------------------------------
// This method returns a pointer to an ifnet structure
// maintained by the family specific interface. IOEthernetInterface
// allocates an arpcom structure during initialization, and returns
// a pointer to this structure when this method is called.
//
// Returns a pointer to an ifnet structure.
struct ifnet * IOEthernetInterface::getIfnet() const
{
return (&(_arpcom->ac_if));
}
//---------------------------------------------------------------------------
// The name of the interface advertised to the network layer
// is generated by concatenating the string returned by this method,
// and an unit number.
//
// Returns a pointer to a constant string "en". Thus Ethernet interfaces
// will be registered as en0, en1, etc.
const char * IOEthernetInterface::getNamePrefix() const
{
return kIOEthernetInterfaceNamePrefix;
}
//---------------------------------------------------------------------------
// Update the packet filter property. This property contains the set of
// packet filters needed by the interface. It may not represent the set
// of packet filters that are actually in use and enabled.
//
// Returns true if the newFilter provided is different from the previously
// cached value.
bool IOEthernetInterface::_setActiveFilters(UInt32 newFilters)
{
if (newFilters == _activeFilters)
return false;
_activeFilters = newFilters;
setProperty(kIOPacketFilters, _activeFilters, 32);
return true;
}
//---------------------------------------------------------------------------
// Prepare the controller after it has been opened.
// This method will be called by our superclass after a
// network controller has accepted an open from this interface.
// IOEthernetInterface uses this method to inspect the controller
// and to cache certain controller properties, such as its hardware
// address, and its set of supported packet filters.
//
// controller: The controller object that was opened.
//
// Returns true if the controller was accepted, false otherwise
// (which will cause the controller to be closed).
bool IOEthernetInterface::controllerDidOpen(IONetworkController * inController)
{
bool ret = false;
IOEthernetController * controller = OSDynamicCast(IOEthernetController,
inController);
do {
IOReturn r;
if (!controller)
break;
// Call the superclass' controllerDidOpen().
//
if (!super::controllerDidOpen(controller))
break;
// Cache the controller's family feature set.
//
_features = controller->getFamilyFeatureSet();
// Get the controller's (supported) packet filters.
//
r = controller->doGetPacketFilters(this, &_availableFilters);
if (r != kIOReturnSuccess)
{
DLOG("%s: doGetPacketFilters error %x\n", getName(), r);
break;
}
// Controller must support Unicast and Broadcast filtering.
//
if ((_availableFilters &
(kIOPacketFilterUnicast | kIOPacketFilterBroadcast)) !=
(kIOPacketFilterUnicast | kIOPacketFilterBroadcast))
{
DLOG("%s: No Unicast/Broadcast packet filters %lx\n",
getName(), _availableFilters);
break;
}
// If controller reports multicast or multicast-all capability,
// then update if_flags to include multicast support.
//
if (_availableFilters &
(kIOPacketFilterMulticast | kIOPacketFilterMulticastAll))
{
setFlagsInt(IFF_MULTICAST);
}
// Even though the network stack will not explicitly request the
// interface to enable Unicast and Broadcast filtering. It is
// implicit. Therefore, we will always activate both of those
// filter types.
_setActiveFilters(kIOPacketFilterUnicast | kIOPacketFilterBroadcast);
r = controller->doEnablePacketFilters(this, _activeFilters);
if (r != kIOReturnSuccess)
{
DLOG("%s: Failed to enable Unicast/Broadcast filters %x\n",
getName(), r);
break;
}
// Read and save the controller's MAC address.
//
r = ((IOEthernetController *)
controller)->doGetHardwareAddress(this, &_macAddr);
if (r != kIOReturnSuccess)
{
DLOG("%s: kRequestGetHardwareAddress error %x\n", getName(), r);
break;
}
#if 1 // Print the MAC address
IOLog("%s: Ethernet address %02x:%02x:%02x:%02x:%02x:%02x\n",
getName(),
_macAddr.ether_addr_octet[0],
_macAddr.ether_addr_octet[1],
_macAddr.ether_addr_octet[2],
_macAddr.ether_addr_octet[3],
_macAddr.ether_addr_octet[4],
_macAddr.ether_addr_octet[5]);
#endif
// Copy the hardware address we obtained from the controller
// to the arpcom structure.
//
bcopy(&_macAddr, _arpcom->ac_enaddr, NUM_EN_ADDR_BYTES);
// Store MAC address in interface's propertyTable.
//
setProperty(kIOMACAddress, (void *) &_macAddr, NUM_EN_ADDR_BYTES);
ret = true;
}
while (0);
return ret;
}
//---------------------------------------------------------------------------
// When the last close from our client is received, the
// interface object will close its controller. But before the controller
// is closed, this method will be called by our superclass to perform any
// final cleanup. IOEthernetInterface will ensure that the controller
// is disabled before it is closed.
//
// controller: The currently opened controller object.
void IOEthernetInterface::controllerWillClose(IONetworkController * controller)
{
if (_controllerEnabled)
{
// Make sure the controller is disabled when we have lost all
// our clients, and is about to close the controller. It
// should be safe to update the _controllerEnabled variable
// shared with the ioctl handlers, which shouldn't run without
// an open client.
controller->doDisable(this);
_controllerEnabled = false;
}
super::controllerWillClose(controller);
}
//---------------------------------------------------------------------------
// The handler for ioctl commands sent from the network layer.
// Commands not handled by this method are passed to our superclass.
//
// Argument convention is:
//
// arg0 - (struct ifnet *)
// arg1 - (void *)
//
// The commands handled by IOEthernetInterface are:
//
// SIOCSIFADDR
// SIOCSIFFLAGS
// SIOCADDMULTI
// SIOCDELMULTI
//
// Returns an error code defined in errno.h (BSD).
SInt IOEthernetInterface::performCommand(IONetworkController * inController,
UInt32 cmd,
void * arg0,
void * arg1)
{
struct arpcom * ac = (struct arpcom *) _arpcom;
struct ifaddr * ifa = (struct ifaddr *) arg1;
SInt ret = EBUSY;
IOEthernetController * controller = (IOEthernetController *) inController;
assert(arg0 == _arpcom);
if (!controller) return EINVAL;
switch (cmd)
{
case SIOCSIFADDR:
CTLR_SYNC_REQ(controller, &IOEthernetInterface::syncSIOCSIFADDR,
&ret, controller);
if (ret)
{
IOLog("IOEthernetInterface: SIOCSIFADDR returned %d\n", ret);
break;
}
switch (ifa->ifa_addr->sa_family)
{
case AF_INET:
//
// See if another station has *our* IP address.
// i.e.: There is an address conflict! If a
// conflict exists, a message is sent to the
// console.
//
if (IA_SIN(ifa)->sin_addr.s_addr != 0)
{
/* don't bother for 0.0.0.0 */
ac->ac_ipaddr = IA_SIN(ifa)->sin_addr;
arpwhohas(ac, &IA_SIN(ifa)->sin_addr);
}
break;
default:
break;
}
break;
case SIOCSIFFLAGS:
CTLR_SYNC_REQ(controller, &IOEthernetInterface::syncSIOCSIFFLAGS,
&ret, controller);
break;
case SIOCADDMULTI:
CTLR_SYNC_REQ(controller, &IOEthernetInterface::syncSIOCADDMULTI,
&ret, controller);
break;
case SIOCDELMULTI:
CTLR_SYNC_REQ(controller, &IOEthernetInterface::syncSIOCDELMULTI,
&ret, controller);
break;
default:
// Don't know what to do with this ioctl command, forward it
// to our superclass.
//
ret = super::performCommand(controller, cmd, arg0, arg1);
break;
}
return ret;
}
//---------------------------------------------------------------------------
// _enableController() is reponsible for calling the controller's enable()
// method and restoring the state of the controller. We assume that
// controllers can completely reset its state upon receiving a disable()
// method call. And when it is brought back up, the interface should
// assist in restoring the previous state of the Ethernet controller.
IOReturn IOEthernetInterface::_enableController(IONetworkController * ctlr)
{
IOReturn ret;
assert(ctlr);
// Send the controller an enable request.
//
ret = ctlr->doEnable(this);
if (ret != kIOReturnSuccess)
return ret; // unable to bring up the controller.
// Restore current filter settings.
//
ret = ctlr->doEnablePacketFilters(this, _activeFilters);
if (ret != kIOReturnSuccess)
goto error;
// Update multicast address list.
//
if (_availableFilters & kIOPacketFilterMulticast)
{
ret = _loadMulticastList((IOEthernetController *) ctlr);
if (ret != kIOReturnSuccess)
goto error;
}
_controllerEnabled = true;
return kIOReturnSuccess;
error:
// If the controller was enabled, make sure we disable it if an
// error occurred.
//
DLOG("%s: _enableController error %x\n", getName(), ret);
ret = ctlr->doDisable(this);
return ret;
}
//---------------------------------------------------------------------------
// Handles SIOCSIFFLAGS ioctl command for Ethernet interfaces. The network
// stack has changed the if_flags field in ifnet. Our job is to go
// through if_flags and see what has changed, and act accordingly.
//
// The fact that if_flags contains both generic and Ethernet specific bits
// means that we cannot move some of the default flag processing to the
// superclass. Sigh...
int IOEthernetInterface::syncSIOCSIFFLAGS(IOEthernetController * ctlr)
{
SInt r = 0;
UInt16 flags = getFlags();
IOReturn ret;
UInt32 newFilters = _activeFilters;
if ( !(flags & IFF_UP) && (flags & IFF_RUNNING) )
{
// If interface is marked down and it is running,
// then stop it.
ctlr->doDisable(this);
flags &= ~IFF_RUNNING;
_controllerEnabled = false;
}
else if ( (flags & IFF_UP) && !(flags & IFF_RUNNING) )
{
// If interface is marked up and it is stopped, then
// start it.
if ((ret = _enableController(ctlr)) == kIOReturnSuccess)
flags |= IFF_RUNNING;
else
r = errnoFromReturn(ret);
}
if (flags & IFF_RUNNING)
{
// Set/Clear promiscuous mode.
//
if (_availableFilters & kIOPacketFilterPromiscuous)
{
if (flags & IFF_PROMISC)
newFilters |= kIOPacketFilterPromiscuous;
else
newFilters &= ~kIOPacketFilterPromiscuous;
}
// Set/Clear Multicast-All mode.
//
if (_availableFilters & kIOPacketFilterMulticastAll)
{
if (flags & IFF_ALLMULTI)
newFilters |= kIOPacketFilterMulticastAll;
else
newFilters &= ~kIOPacketFilterMulticastAll;
}
}
if (_setActiveFilters(newFilters))
{
ret = ctlr->doEnablePacketFilters(this, newFilters);
if (ret != kIOReturnSuccess)
r = errnoFromReturn(ret);
}
// Update the flags field to pick up any modifications. Also update the
// property table to reflect any flag changes.
//
setFlagsInt(flags, ~flags);
return r;
}
//---------------------------------------------------------------------------
// Handles SIOCSIFADDR ioctl command for Ethernet interfaces.
SInt IOEthernetInterface::syncSIOCSIFADDR(IOEthernetController * ctlr)
{
SInt r = 0;
UInt16 flags = getFlags();
flags |= IFF_UP;
if (!(flags & IFF_RUNNING))
{
r = errnoFromReturn(_enableController(ctlr));
if (r == 0)
flags |= IFF_RUNNING;
}
setFlagsInt(flags, ~flags);
return r;
}
//---------------------------------------------------------------------------
// This method is called by syncSIOCADDMULTI() and syncSIOCDELMULTI() to
// reload the hardware's multicast filter whenever the multicast address
// list is changed. A OSData is published in the property table containing
// the multicast addresses.
IOReturn
IOEthernetInterface::_loadMulticastList(IOEthernetController * ctlr)
{
enet_addr_t * multiAddrs = 0;
UInt mcount;
OSData * mcData = 0;
struct ifnet * ifp = (struct ifnet *) _arpcom;
struct ifmultiaddr * ifma;
IOReturn ret;
bool ok;
assert(ifp);
// Update the multicast addresses count ivar.
//
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: multicast memory allocation failed\n", getName());
return kIOReturnNoMemory;
}
// Loop through the linked multicast structures and write the
// address to the OSData.
//
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 = (enet_addr_t *) mcData->getBytesNoCopy();
assert(multiAddrs);
}
// Call the controller's setMulticastList() method.
//
ret = ctlr->doSetMulticastList(this, multiAddrs, mcount);
if (mcData)
{
if (ret == kIOReturnSuccess)
setProperty(kIOMulticastAddresses, mcData);
mcData->release();
}
else {
removeProperty(kIOMulticastAddresses);
}
return ret;
}
//---------------------------------------------------------------------------
// Handles SIOCADDMULTI ioctl command.
SInt IOEthernetInterface::syncSIOCADDMULTI(IOEthernetController * ctlr)
{
IOReturn ret;
UInt32 activatedFilters;
SInt r = 0;
if (_availableFilters & kIOPacketFilterMulticast)
{
_setActiveFilters(_activeFilters | kIOPacketFilterMulticast);
// Make sure the multicast filter is active.
//
ret = ctlr->doEnablePacketFilters(this, _activeFilters,
&activatedFilters);
// If the controller did not activate the multicast filter,
// report an error.
//
if ((activatedFilters & kIOPacketFilterMulticast) == 0)
{
assert(ret != kIOReturnSuccess);
r = errnoFromReturn(ret);
}
// Do not load multicast list if the multicast filter
// did not become active.
//
if (r == 0)
r = errnoFromReturn(_loadMulticastList(ctlr));
}
else
r = EOPNOTSUPP; // no multicast support.
return r;
}
//---------------------------------------------------------------------------
// Handles SIOCDELMULTI ioctl command.
SInt IOEthernetInterface::syncSIOCDELMULTI(IOEthernetController * ctlr)
{
IOReturn ret;
UInt32 activatedFilters;
SInt r = 0;
if (_availableFilters & kIOPacketFilterMulticast)
{
r = errnoFromReturn(_loadMulticastList(ctlr));
if (r == 0)
{
// If the multicast list is now empty, instruct the controller
// to turn off its multicast filter.
if (_mcAddrCount == 0)
{
_setActiveFilters(_activeFilters & ~kIOPacketFilterMulticast);
ret = ctlr->doEnablePacketFilters(this, _activeFilters,
&activatedFilters);
if (activatedFilters & kIOPacketFilterMulticast)
{
assert(ret != kIOReturnSuccess);
r = errnoFromReturn(ret);
}
}
}
}
else
r = EOPNOTSUPP; // no multicast support.
return r;
}