Source to iokit/Families/IONetworking/IONetworkInterface.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.
*
* IONetworkInterface.cpp
*
* HISTORY
* 8-Jan-1999 Joe Liu (jliu) created.
*
*/
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/network/IONetworkUserClient.h>
extern "C" {
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/bpf.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <net/if_media.h>
#include <net/dlil.h>
int copyout(void *kaddr, void *udaddr, size_t len);
}
//---------------------------------------------------------------------------
#define super IOService
OSDefineMetaClass( IONetworkInterface, IOService )
OSDefineAbstractStructors( IONetworkInterface, IOService )
//---------------------------------------------------------------------------
// Macros
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG
#endif
//---------------------------------------------------------------------------
// Initialize an IONetworkInterface instance.
//
// properties: A property dictionary.
//
// Returns true if initialized successfully, false otherwise.
bool IONetworkInterface::init(OSDictionary * properties = 0)
{
_provider = 0; // single IONetworkController provider.
_ifLock = 0; // locks interface state and data accesses.
_clientSet = 0; // A set of all client objects.
_inputFilterFunc = 0; // input filter tap handler.
_outputFilterFunc = 0; // output filter tap handler.
_target = 0; // output target object.
_outAction = 0; // output packet action.
_dataDict = 0; // IONetworkData dict.
_registered = false;
inputQHead = inputQTail = 0;
// Propagate the init() call to our superclass.
//
if (!super::init(properties))
return false;
// Create interface lock.
//
_ifLock = IORecursiveLockAlloc();
if (!_ifLock)
return false;
// Create an OSSet to store client objects. Initial capacity
// (which can grow) is set at 2 clients.
//
_clientSet = OSSet::withCapacity(2);
if (!_clientSet)
return false;
// Get the ifnet structure of the network interface. Subclasses must
// implement getIfnet() and expect this function to be called when
// they call IONetworkInterface::init().
//
_ifp = getIfnet();
if (!_ifp)
{
DLOG("%s: getIfnet() returned NULL\n", getName());
return false;
}
// Intialize the ifnet structure.
//
if (!initIfnet(_ifp))
return false;
// Create a data dictionary.
//
if ((_dataDict = OSDictionary::withCapacity(5)) == 0)
return false;
IONetworkData * data = IONetworkData::withName(
kIONetworkStatsKey,
sizeof(IONetworkStats),
(UInt8 *) &(_ifp->if_data.ifi_ipackets));
if (data) {
addNetworkData(data);
data->release();
}
// Set the default filter tap mode for both directions to
// kIOFilterTapModeInternal. Both taps will automatically receive all
// packets that flow through the interface in their respective
// directions.
//
_inputFilterTapMode = _outputFilterTapMode = kIOFilterTapModeInternal;
return true;
}
//---------------------------------------------------------------------------
// Destroy the interface. Release all allocated resources.
void IONetworkInterface::free()
{
DLOG("IONetworkInterface::free() called\n");
// Must never free a registered interface.
assert(_registered == false);
if (_clientSet)
{
// Should not have any clients.
//
assert(_clientSet->getCount() == 0);
_clientSet->release();
}
if (_ifLock)
{
IORecursiveLockFree(_ifLock);
_ifLock = 0;
}
if (_dataDict)
_dataDict->release();
clearInputQueue();
super::free();
}
//---------------------------------------------------------------------------
// Initialize the ifnet structure. Subclasses must override this
// method and initialize the ifnet structure given in a family specific
// manner. The implementation of this method in the subclass must call
// the version in super before it returns. The argument provided is a
// pointer to an ifnet structure obtained through getIfnet().
// IONetworkInterface uses this method to initialize the function
// pointers in ifnet.
//
// ifp: Pointer to an ifnet structure obtained through an earlier
// getIfnet() call.
//
// Returns true if the initialization was successful.
bool IONetworkInterface::initIfnet(struct ifnet * ifp)
{
lock();
// Register our 'shim' functions. These function pointers
// points to static member functions inside class.
ifp->if_output = output_shim;
ifp->if_ioctl = ioctl_shim;
ifp->if_set_bpf_tap = set_bpf_tap_shim;
ifp->if_private = this;
// Enable driver reentrancy. This allows the network stack
// to call the driver from the same thread that sent a received
// packet up to the stack. The netisr thread is bypassed. The context
// switch cost is eliminated, but how long will the driver have to wait
// before the thread can return to do driver work?
//
// Not enabled by default, do this on a per driver basis by calling,
// netif->setExtraFlags(IFEF_DVR_REENTRY_OK) from the driver's
// configureNetworkInterface() function.
//
// ifp->if_eflags |= IFEF_DVR_REENTRY_OK;
unlock();
return true;
}
//---------------------------------------------------------------------------
// Take the interface lock (recursive lock).
void IONetworkInterface::lock()
{
IORecursiveLockLock(_ifLock);
}
//---------------------------------------------------------------------------
// Release the interface lock (recursive lock).
void IONetworkInterface::unlock()
{
IORecursiveLockUnlock(_ifLock);
}
//---------------------------------------------------------------------------
// Called by handleOpen() to configure our controller after we have just
// called he controller's open() method. We open the controller only when
// we receive the initial open request from a client. Subclasses should
// override this method and setup the controller before allowing the
// client open, and they must call this method and check the return
// value in their implementation.
//
// controller: Our controller object.
//
// Must return true in order for handleOpen() to accept the client open.
// If the return is false, then the controller will be closed and the client
// open will be rejected.
bool IONetworkInterface::controllerDidOpen(IONetworkController * controller)
{
return true; // open approved.
}
//---------------------------------------------------------------------------
// Called by handleClose() after receiving a close from the
// last client, and just before the controller is closed. Subclasses
// can override this method to perform any cleanup action before the
// controller is closed.
//
// controller: The controller that will be closed.
void IONetworkInterface::controllerWillClose(IONetworkController * controller)
{
}
//---------------------------------------------------------------------------
// Handle a client open on the interface. The open() method in IOService
// calls this method with the arbitration lock held. This method must
// return true to accept the client open. Subclasses should not override
// this method.
//
// client: See IOService.
// options: See IOService.
// argument: See IOService.
//
// Returns true to accept the client open.
bool IONetworkInterface::handleOpen(IOService * client,
IOOptionBits options,
void * argument)
{
bool accept = false;
bool controllerOpen = false;
IONetworkController * controller = OSDynamicCast(IONetworkController,
getProvider());
do {
// Was this object already registered as our client?
//
if (_clientSet->containsObject(client))
{
DLOG("%s: multiple opens from client %lx\n",
getName(), (UInt32) client);
accept = true;
break;
}
// We can only provide services to a single client.
// IONetworkUserClient are excluded from this restriction.
//
bool isClient = (OSDynamicCast(IONetworkUserClient, client) == 0);
if (isClient && _client)
{
DLOG("%s: extra client %lx\n", getName(), (UInt32) client);
break;
}
// If our provider has not yet been opened, then open it.
// However if the controller open fails, then we will not allow
// the new client to open us.
//
if (_provider == 0) {
if ((controller == 0) ||
((controllerOpen = controller->open(this)) == false) ||
(controllerDidOpen(controller) == false))
break;
}
// Qualify the client.
//
if (!handleClientOpen(controller, client))
break;
// Add the new client object to our client set.
//
if (!_clientSet->setObject(client))
{
handleClientClose(controller, client);
break;
}
// Remember our client.
//
if (isClient)
_client = client;
accept = true;
}
while (0);
// If provider was opened above, but an error has caused us to refuse
// the client open, then close our provider. Otherwise, cache our
// provider.
//
if (controllerOpen)
{
if (accept)
{
_provider = controller; // cache our provider.
}
else {
controllerWillClose(controller);
controller->close(this);
}
}
return accept;
}
//---------------------------------------------------------------------------
// Handle a close by one of our clients. We close the controller when
// we receive a close from our last client. The close() method in
// IOService calls this method with the arbitration lock held.
// Subclasses should not override this method.
//
// client: See IOService.
// options: See IOService.
void IONetworkInterface::handleClose(IOService * client, IOOptionBits options)
{
// Remove the object from the client OSSet.
//
if (_clientSet->containsObject(client))
{
bool isClient = (OSDynamicCast(IONetworkUserClient, client) == 0);
// Call handleClientClose() to handle the client close.
//
assert(_provider);
handleClientClose(_provider, client);
if (isClient)
_client = 0;
// If this is the last client, then close our provider.
//
if (_clientSet->getCount() == 1)
{
controllerWillClose(_provider);
_provider->close(this);
_provider = 0;
}
// Remove the client from our OSSet.
//
_clientSet->removeObject(client);
}
}
//---------------------------------------------------------------------------
// Returns true if the specified client, or any client if none if
// specified, presently has an open on this object. This function
// is called by IOService with the arbitration lock held.
// Subclasses should not override this method.
bool IONetworkInterface::handleIsOpen(const IOService * client) const
{
if (client)
return _clientSet->containsObject(client);
else
return (_clientSet->getCount() > 0);
}
//---------------------------------------------------------------------------
// This method is called by handleOpen() to qualify a client
// object is trying to open us. This method must return true
// true to accept the client open. The provider of the
// interface object, an IONetworkController, is also provided.
bool IONetworkInterface::handleClientOpen(IONetworkController * /*ctlr*/,
IOService * client)
{
bool allowOpen = true;
// Always allow opens from IOUserClients.
//
if (OSDynamicCast(IONetworkUserClient, client))
return true;
lock();
// Packet output handler must be valid.
//
if (!_target || !_outAction)
allowOpen = false;
// Transition state to registered.
//
if (allowOpen)
_registered = true;
unlock();
return allowOpen;
}
//---------------------------------------------------------------------------
// Called by handleClose() to handle a client close. Both the provider
// and the client that performed the close are provided.
void IONetworkInterface::handleClientClose(IONetworkController * /*ctlr*/,
IOService * client)
{
if (OSDynamicCast(IONetworkUserClient, client))
return;
lock();
// Transition state to unregistered.
//
_registered = false;
unlock();
}
//---------------------------------------------------------------------------
// Register the output handler. The interface will forward all output packets,
// sent from the network layer, to the output handler registered through
// this method. Until a handler is registered, handleClientOpen()
// will refuse all client opens. The output handler cannot be changed
// when the interface state is kIONetworkInterfaceStateRegistered.
//
// target: Target object that implements the output action.
// action: The action which handles output packets.
bool IONetworkInterface::registerOutputHandler(OSObject * target,
IOOutputAction action)
{
target = OSDynamicCast(OSObject, target);
lock();
// Sanity check on the arguments.
//
if (_registered || !target || !action)
{
unlock();
return false;
}
_target = target;
_outAction = action;
unlock();
return true;
}
//---------------------------------------------------------------------------
// Functions to set and inspect the filter tap settings. These settings
// cannot change once the interface becomes registered.
bool IONetworkInterface::_setFilterTapMode(IOFilterTapMode * modePtr,
IOFilterTapMode mode)
{
lock();
if (_registered)
{
unlock();
return false;
}
*modePtr = mode;
unlock();
return true;
}
bool IONetworkInterface::setInputFilterTapMode(IOFilterTapMode mode)
{
return _setFilterTapMode(&_inputFilterTapMode, mode);
}
bool IONetworkInterface::setOutputFilterTapMode(IOFilterTapMode mode)
{
return _setFilterTapMode(&_outputFilterTapMode, mode);
}
IOFilterTapMode IONetworkInterface::getInputFilterTapMode() const
{
return _inputFilterTapMode;
}
IOFilterTapMode IONetworkInterface::getOutputFilterTapMode() const
{
return _outputFilterTapMode;
}
//---------------------------------------------------------------------------
// Feed packets to the input/output BPF packet filter taps.
static inline void _feedFilterTap(struct ifnet * ifp,
struct mbuf * m,
BPF_FUNC func,
int mode)
{
if (func)
func(ifp, m);
}
//---------------------------------------------------------------------------
// Feed a packet to the output filter tap. This method should not be called if
// the output filter tap mode is set to kIOFilterTapModeInternal, since the tap
// would already receive every output packet that flows through the interface.
// A further call to feedOutputFilterTap() would cause the tap to receive
// multiple copies of the same output packet.
//
// pkt: The packet mbuf to pass to the output tap.
void IONetworkInterface::feedOutputFilterTap(struct mbuf * m)
{
assert(m);
_feedFilterTap(_ifp, m, _outputFilterFunc, BPF_TAP_OUTPUT);
}
//---------------------------------------------------------------------------
// Feed a packet to the input filter tap. This method should not be called if
// the input filter tap mode is set to kIOFilterTapModeInternal, since the tap
// would already receive every input packet that flows through the interface.
// A further call to feedInputFilterTap() would cause the tap to receive
// multiple copies of the same input packet
//
// pkt: The packet mbuf to pass to the input tap. The rcvif
// field in the mbuf is modified by this method.
void IONetworkInterface::feedInputFilterTap(struct mbuf * m)
{
assert(m);
m->m_pkthdr.rcvif = _ifp;
_feedFilterTap(_ifp, m, _inputFilterFunc, BPF_TAP_INPUT);
}
//---------------------------------------------------------------------------
// Called by a controller to pass a received packet
// to the network layer. Packets received by this method can
// also be placed on a queue local to the interface, that the controller
// can use to delay the packet handoff to the network layer, until all
// received packets have been transferred to the queue. A subsequent call
// of flushInputQueue(), or inputPacket() with the queue argument set to
// false, will cause all queued packets (may be a single packet) to be
// delivered to the network layer, by making a single dlil_input() call.
// Additional methods that manipulate the input queue are flushInputQueue()
// and clearInputQueue(). This queue, which is nothing more than a chain of
// mbufs, is not protected by a lock since the controller is expected to
// manipulate the input queue from a single thread. If the input filter
// tap mode is set to kIOFilterTapModeInternal, then packets sent to
// this method are also fed to the input filter tap.
//
// m: The packet mbuf containing the received frame.
//
// length: If non zero, the mbuf will be truncated to the
// given length. If zero, then no truncation will take place.
//
// queue: If true, the only action performed is to queue the
// input packet. Otherwise, the dlil_input() function is
// called to handoff all queued packets (including the packet
// passed in).
//
// Returns the number of packets submitted to the network layer.
// Returns 0 if the packet was queued.
//
// FIXME - The current implementation does not use the new DLIL API's.
// Thus it will only work for Ethernet devices.
#define IN_Q_RESET {inputQHead = 0;}
#define IN_Q_ENQUEUE(m) \
{ \
if (inputQHead == 0) \
inputQHead = inputQTail = (m); \
else { \
inputQTail->m_nextpkt = (m); \
inputQTail = (m); \
} \
}
#define IN_Q_FLUSH(count) \
{ \
struct mbuf * _m; \
struct ether_header * _eh; \
\
count = 0; \
\
while ((_m = inputQHead)) { \
inputQHead = inputQHead->m_nextpkt; \
_m->m_nextpkt = 0; \
_m->m_pkthdr.rcvif = _ifp; \
\
if (_inputFilterTapMode == kIOFilterTapModeInternal) \
_feedFilterTap(_ifp, _m, _inputFilterFunc, BPF_TAP_INPUT); \
\
_eh = (struct ether_header *) _m->m_data; \
_m->m_len -= sizeof(struct ether_header); \
_m->m_data += sizeof(struct ether_header); \
_m->m_pkthdr.len -= sizeof(struct ether_header); \
\
dlil_input(_ifp, _m, (char *) _eh); \
count++; \
} \
}
UInt32 IONetworkInterface::flushInputQueue()
{
UInt32 count;
IN_Q_FLUSH(count);
// IN_Q_RESET;
return count;
}
UInt32 IONetworkInterface::clearInputQueue()
{
UInt32 count = 0;
struct mbuf * m;
while ((m = inputQHead))
{
inputQHead = inputQHead->m_nextpkt;
m_freem(m);
count++;
}
// IN_Q_RESET;
return count;
}
UInt32 IONetworkInterface::inputPacket(struct mbuf * m,
UInt32 length = 0,
bool queue = false)
{
assert(m);
// Truncate the tail of the mbuf chain to the specified length.
// The mbuf chain passed in will usually have the length in
// each mbuf set to its capacity.
//
if (length)
{
struct mbuf * mb = m;
mb->m_pkthdr.len = length; // set total length
do {
if (length < (UInt) mb->m_len)
{
mb->m_len = length; // truncate mbuf to remaining length
}
length -= mb->m_len; // decrement remaining length
} while ((mb = mb->m_next));
assert(length == 0); // why is capacity smaller than length?
}
IN_Q_ENQUEUE(m);
if (queue == false)
{
UInt32 count;
IN_Q_FLUSH(count);
// IN_Q_RESET;
return count;
}
else
return 0;
}
//---------------------------------------------------------------------------
// Called by the controller driver to send a network event to the network
// layer. Possible applications include: media changed events,
// power management events, controller state change events.
//
// eventType: The event type.
// arg: An argument associated with the event.
void IONetworkInterface::inputEvent(UInt32 eventType, void * arg)
{
switch (eventType)
{
case kIONetworkEventMediumChange:
case kIONetworkEventLinkChange:
_handleMediumAndLinkChangeEvent();
break;
default:
IOLog("IONetworkInterface: event (%x, %x) not handled\n",
(UInt) eventType, (UInt) arg);
break;
}
}
//---------------------------------------------------------------------------
// Handles media and link change events. Deliver a notification to
// all IONetworkUserClients.
void IONetworkInterface::_handleMediumAndLinkChangeEvent()
{
OSCollectionIterator * iter;
IONetworkUserClient * uc;
lockForArbitration();
iter = OSCollectionIterator::withCollection(_clientSet);
if (iter) {
while ((uc = (IONetworkUserClient *) iter->getNextObject())) {
if ((uc = OSDynamicCast(IONetworkUserClient, uc)) == 0)
continue;
uc->deliverNotification(kIONUCNotificationTypeLinkChange);
}
iter->release();
}
unlockForArbitration();
}
//---------------------------------------------------------------------------
// SIOCSIFMTU (set interface MTU) ioctl handler.
SInt IONetworkInterface::syncSIOCSIFMTU(IONetworkController * ctlr,
struct ifreq * ifr)
{
SInt error;
UInt32 newMtu = ifr->ifr_mtu;
// If change is not necessary, return success without getting the
// controller involved.
if (getMaxTransferUnit() == newMtu)
return 0;
// Request the controller to switch MTU size.
//
error = errnoFromReturn(ctlr->doSetMaxTransferUnit(this, newMtu));
if (error == 0)
{
// Controller reports success. Update the interface MTU size
// property.
//
setMaxTransferUnitInt(newMtu);
}
return error;
}
//---------------------------------------------------------------------------
// SIOCSIFMEDIA (SET interface media) ioctl handler.
SInt IONetworkInterface::syncSIOCSIFMEDIA(IONetworkController * ctlr,
struct ifreq * ifr)
{
OSDictionary * mediumDict;
IONetworkMedium * medium;
SInt error;
mediumDict = ctlr->copyMediumDictionary(); // creates a copy
if (!mediumDict)
{
// unable to allocate memory, or no medium dictionary.
return EOPNOTSUPP;
}
medium = IONetworkMedium::getMediumWithType(mediumDict, ifr->ifr_media);
if (!medium)
{
// Exact type was not found. Try a partial match.
// ifconfig program sets the media type and media
// options separately. The client should not send
// an incomplete type!!!
medium = IONetworkMedium::getMediumWithType(mediumDict,
ifr->ifr_media,
~kIOMediumTypeMask);
if (!medium)
{
mediumDict->release();
return EINVAL; // requested medium not found.
}
}
// It may be possible for the controller to update the medium
// dictionary and perhaps delete the medium entry that we have
// selected from our copy of the stale dictionary. This should be
// harmless since IONetworkController's doSelectMedium() should
// filter invalid selections before calling the driver.
error = errnoFromReturn(ctlr->doSelectMedium(this, medium->getName()));
if (error == 0)
{
// Remember the last media type sent by BSD which the driver
// accepted. Note that _bsdMediaType may not be equal to
// medium->getType() since we may have done a partial match.
// The only reason for this is to be able to return the
// _bsdMediaType as the current media type in SICGIFMEDIA.
_bsdMediaType = ifr->ifr_media;
}
mediumDict->release();
return error;
}
//---------------------------------------------------------------------------
// SIOCGIFMEDIA (GET interface media) ioctl handler.
SInt IONetworkInterface::syncSIOCGIFMEDIA(IONetworkController * ctlr,
struct ifreq * ifr)
{
OSDictionary * mediumDict = 0;
UInt mediumCount = 0;
UInt maxCount;
OSCollectionIterator * iter = 0;
UInt32 * typeList;
UInt typeListSize;
OSSymbol * keyObject;
SInt error = 0;
struct ifmediareq * ifmr = (struct ifmediareq *) ifr;
// Maximum number of medium types that the ioctl caller will accept.
//
maxCount = ifmr->ifm_count;
do {
mediumDict = ctlr->copyMediumDictionary(); // creates a copy
if (!mediumDict)
{
error = EOPNOTSUPP;
break; // unable to allocate memory, or no medium dictionary.
}
if ((mediumCount = mediumDict->getCount()) == 0)
break; // no medium in the medium dictionary
if (maxCount == 0)
break; // caller is only probing for support and media count.
if (maxCount < mediumCount)
{
// user buffer is too small to hold all medium entries.
error = E2BIG;
// Proceed with partial copy on E2BIG. This follows the
// SIOCGIFMEDIA handling practice in bsd/net/if_media.c.
//
// break;
}
// Create an iterator to loop through the medium entries in the
// dictionary.
//
iter = OSCollectionIterator::withCollection(mediumDict);
if (!iter)
{
error = ENOMEM;
break;
}
// Allocate memory for the copyout buffer.
//
typeListSize = maxCount * sizeof(UInt32);
typeList = (UInt32 *) IOMalloc(typeListSize);
if (!typeList)
{
error = ENOMEM;
break;
}
bzero(typeList, typeListSize);
// Iterate through the medium dictionary and copy the type of
// each medium entry to typeList[].
//
mediumCount = 0;
while ( (keyObject = (OSSymbol *) iter->getNextObject()) &&
(mediumCount < maxCount) )
{
IONetworkMedium * medium = OSDynamicCast(IONetworkMedium,
mediumDict->getObject(keyObject));
if (!medium)
continue; // should not happen!
typeList[mediumCount++] = medium->getType();
}
if (mediumCount)
{
error = copyout((caddr_t) typeList,
(caddr_t) ifmr->ifm_ulist,
typeListSize);
}
IOFree(typeList, typeListSize);
}
while (0);
ifmr->ifm_active = ifmr->ifm_current = IFM_NONE;
ifmr->ifm_status = 0;
ifmr->ifm_count = mediumCount;
// Get a copy of the controller's property table and read the
// link status, current, and active medium.
OSDictionary * pTable = ctlr->dictionaryWithProperties();
if (pTable)
{
OSNumber * linkStatus = OSDynamicCast(OSNumber,
pTable->getObject(kIOLinkStatus));
if (linkStatus)
ifmr->ifm_status = linkStatus->unsigned32BitValue();
if (mediumDict)
{
IONetworkMedium * medium;
OSSymbol * mediumName;
if ((mediumName = OSDynamicCast(OSSymbol,
pTable->getObject(kIOCurrentMedium))) &&
(medium = OSDynamicCast(IONetworkMedium,
mediumDict->getObject(mediumName))))
{
ifmr->ifm_current = medium->getType();
if (IOMediumGetType(_bsdMediaType) ==
IOMediumGetType((UInt32) ifmr->ifm_current))
{
ifmr->ifm_current = _bsdMediaType;
}
}
if ((mediumName = OSDynamicCast(OSSymbol,
pTable->getObject(kIOActiveMedium))) &&
(medium = OSDynamicCast(IONetworkMedium,
mediumDict->getObject(mediumName))))
{
ifmr->ifm_active = medium->getType();
}
}
pTable->release();
}
if (iter)
iter->release();
if (mediumDict)
mediumDict->release();
return error;
}
//---------------------------------------------------------------------------
// Handles generic socket ioctl commands sent to the interface.
// IONetworkInterface handles commands that are common to all network
// families. A subclass of IONetworkInterface may override this method
// in order to handle the same command that is handled here, but in a
// different manner, or (the more like case) to augment the command
// handling to include additional commands, and call super for any
// commands not handled in the subclass.
//
// The commands handled by IONetworkInterface are:
// SIOCGIFMTU - Get interface MTU size.
// SIOCSIFMTU - Set interface MTU size.
// SIOCSIFMEDIA - Set media.
// SIOCGIFMEDIA - Get media and link status.
//
// Returns a BSD return code defined in bsd/sys/errno.h.
SInt IONetworkInterface::performCommand(IONetworkController * ctlr,
UInt32 cmd,
void * arg0,
void * arg1)
{
SInt ret = EBUSY;
ctlr->syncRequest(this, /* sender */
this, /* target */
(IONetworkAction) &IONetworkInterface::syncPerformCommand,
(UInt32 *) &ret, /* return */
(void *) ctlr, /* arg0 - arg3 */
(void *) cmd,
(void *) arg0,
(void *) arg1);
return ret;
}
SInt IONetworkInterface::syncPerformCommand(IONetworkController * ctlr,
UInt32 cmd,
void * arg0,
void * arg1)
{
struct ifreq * ifr = (struct ifreq *) arg1;
SInt ret = EINVAL;
if (ifr == 0)
return EINVAL;
switch (cmd)
{
// Get interface MTU.
//
case SIOCGIFMTU:
ifr->ifr_mtu = getMaxTransferUnit();
ret = 0; // no error
break;
// Set interface MTU.
//
case SIOCSIFMTU:
ret = syncSIOCSIFMTU(ctlr, ifr);
break;
// Set interface media type.
//
case SIOCSIFMEDIA:
ret = syncSIOCSIFMEDIA(ctlr, ifr);
break;
// Get interface media type and status.
//
case SIOCGIFMEDIA:
ret = syncSIOCGIFMEDIA(ctlr, ifr);
break;
default:
// DLOG(%s: command not handled (%08lx), getName(), cmd);
break;
}
return ret;
}
//---------------------------------------------------------------------------
// if_ioctl() handler - Calls performCommand() when we receive an ioctl
// command from the network stack.
int
IONetworkInterface::ioctl_shim(struct ifnet * ifp, u_long cmd, caddr_t data)
{
assert(ifp && ifp->if_private);
IONetworkInterface * self = (IONetworkInterface *) ifp->if_private;
assert((ifp == self->_ifp) && self->_provider);
return self->performCommand(self->_provider,
cmd,
(void *) ifp,
(void *) data);
}
//---------------------------------------------------------------------------
// if_output() handler.
//
// Handle a call from the network stack to transmit all packets in
// the ifnet's output queue. This queue is likely to be removed in
// the future when DLIL gets rolled out.
int IONetworkInterface::output_shim(struct ifnet * ifp, struct mbuf * m)
{
assert(ifp && ifp->if_private);
IONetworkInterface * self = (IONetworkInterface *) ifp->if_private;
assert(ifp == self->_ifp);
if (m == 0)
{
DLOG("IONetworkInterface: NULL output mbuf\n");
return EINVAL;
}
if ((m->m_flags & M_PKTHDR) == 0)
{
DLOG("IONetworkInterface: M_PKTHDR bit not set\n");
m_freem(m);
return EINVAL;
}
// Feed the output filter tap.
//
if (self->_outputFilterTapMode == kIOFilterTapModeInternal)
_feedFilterTap(ifp, m, self->_outputFilterFunc, BPF_TAP_OUTPUT);
// Forward the packet to the registered output packet handler.
//
return ((self->_target)->*(self->_outAction))(m);
}
//---------------------------------------------------------------------------
// if_set_bpf_tap() handler. Handles request from the DLIL to enable or
// disable the input/output filter taps.
//
// FIXME - locking may be needed.
int IONetworkInterface::set_bpf_tap_shim(struct ifnet * ifp,
int mode,
BPF_FUNC func)
{
assert(ifp && ifp->if_private);
IONetworkInterface * self = (IONetworkInterface *) ifp->if_private;
assert(ifp == self->_ifp);
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;
}
//---------------------------------------------------------------------------
// As the name implies, this function does nothing. This will get called
// if the network stack tries to call the if_watchdog function pointer
// in ifnet. This should not happen. IOKit does not use this watchdog
// timer facility.
void IONetworkInterface::null_shim(struct ifnet * /*ifp*/)
{
IOLog("IONetworkInterface::null_shim called!\n");
}
//---------------------------------------------------------------------------
// ifnet field (and property table) getter/setter.
//
// For the setter, if restrict flag is set, that means that the property is
// restricted from being modified once the interface's has been registered.
bool IONetworkInterface::_setInterfaceProperty(
UInt32 value,
UInt32 mask,
UInt32 bytes,
void * addr,
char * key,
bool restrict)
{
bool updateOk = false;
UInt32 newValue;
lock();
if (restrict && (_registered == true))
goto abort;
// Update the property in ifnet.
//
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:
goto abort;
}
// Update the OSNumber in the property table.
//
updateOk = key ? setProperty(key, newValue, bytes * 8) : true;
abort:
unlock();
return updateOk;
}
#define IO_IFNET_GET(func, type, field) \
type IONetworkInterface:: ## func() const \
{ \
type ret; \
((IONetworkInterface *) this)->lock(); \
ret = _ifp ? _ifp-> ## field : 0; \
((IONetworkInterface *) this)->unlock(); \
return ret; \
}
#define IO_IFNET_SET(func, type, field, propName) \
bool IONetworkInterface:: ## func ## Int(type value) \
{ \
return _setInterfaceProperty( \
(UInt32) value, \
0, \
sizeof(type), \
(void *) &_ifp-> ## field, \
propName, \
false); \
} \
\
bool IONetworkInterface:: ## func(type value) \
{ \
return _setInterfaceProperty( \
(UInt32) value, \
0, \
sizeof(type), \
(void *) &_ifp-> ## field, \
propName, \
true); \
}
#define IO_IFNET_RMW(func, type, field, propName) \
bool IONetworkInterface:: ## func ## Int(type value, type clear = 0) \
{ \
return _setInterfaceProperty( \
(UInt32) value, \
(UInt32) ~clear, \
sizeof(type), \
(void *) &_ifp-> ## field, \
propName, \
false); \
} \
\
bool IONetworkInterface:: ## func(type value, type clear = 0) \
{ \
return _setInterfaceProperty( \
(UInt32) value, \
(UInt32) ~clear, \
sizeof(type), \
(void *) &_ifp-> ## field, \
propName, \
true); \
}
//---------------------------------------------------------------------------
// Interface type accessors (ifp->if_type). The list of interface types is
// defined in <bsd/net/if_types.h>.
IO_IFNET_SET(setInterfaceType, UInt8, if_type, kIOInterfaceType)
IO_IFNET_GET(getInterfaceType, UInt8, if_type)
//---------------------------------------------------------------------------
// Mtu (MaxTransferUnit) accessors (ifp->if_mtu).
IO_IFNET_SET(setMaxTransferUnit, UInt32, if_mtu, kIOMaxTransferUnit)
IO_IFNET_GET(getMaxTransferUnit, UInt32, if_mtu)
//---------------------------------------------------------------------------
// Flags accessors (ifp->if_flags). This is a read-modify-write operation.
IO_IFNET_RMW(setFlags, UInt16, if_flags, kIOInterfaceFlags)
IO_IFNET_GET(getFlags, UInt16, if_flags)
//---------------------------------------------------------------------------
// EFlags accessors (ifp->if_eflags). This is a read-modify-write operation.
IO_IFNET_RMW(setExtraFlags, UInt32, if_eflags, kIOInterfaceExtraFlags)
IO_IFNET_GET(getExtraFlags, UInt32, if_eflags)
//---------------------------------------------------------------------------
// MediaAddressLength accessors (ifp->if_addrlen)
IO_IFNET_SET(setMediaAddressLength, UInt8, if_addrlen, kIOMediaAddressLength)
IO_IFNET_GET(getMediaAddressLength, UInt8, if_addrlen)
//---------------------------------------------------------------------------
// MediaHeaderLength accessors (ifp->if_hdrlen)
IO_IFNET_SET(setMediaHeaderLength, UInt8, if_hdrlen, kIOMediaHeaderLength)
IO_IFNET_GET(getMediaHeaderLength, UInt8, if_hdrlen)
//---------------------------------------------------------------------------
// Interface unit number. The unit number for the interface is assigned
// by our client.
IO_IFNET_SET(setUnitNumber, UInt16, if_unit, 0)
IO_IFNET_GET(getUnitNumber, UInt16, if_unit)
//---------------------------------------------------------------------------
// Interface name.
IO_IFNET_SET(setInterfaceName, const char *, if_name, 0)
IO_IFNET_GET(getInterfaceName, const char *, if_name)
//---------------------------------------------------------------------------
// Return true if the interface has been registered with the network layer,
// false otherwise.
bool IONetworkInterface::isRegistered() const
{
return _registered;
}
//---------------------------------------------------------------------------
// Perform a lookup of the dictionary kept by the interface,
// and return an entry that matches the specified string key.
//
// key: Search for an IONetworkData entry with this key.
//
// Returns the matching entry, or 0 if no match was found.
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));
}
//---------------------------------------------------------------------------
// A private function to copy the data dictionary to the property table.
bool IONetworkInterface::_copyNetworkDataDictToPropertyTable()
{
OSDictionary * aCopy = OSDictionary::withDictionary(_dataDict);
bool ret = false;
if (aCopy) {
ret = setProperty(kIONetworkData, _dataDict);
aCopy->release();
}
return ret;
}
//---------------------------------------------------------------------------
// Remove an entry from the dictionary of IONetworkData objects.
//
// aKey: A key for an IONetworkData entry in the dictionary.
//
// Returns true if completed without errors,
// false if the operation was aborted.
bool IONetworkInterface::removeNetworkData(const OSSymbol * aKey)
{
bool ret = false;
lockForArbitration();
do {
if (_clientSet->getCount())
break;
_dataDict->removeObject(aKey);
ret = _copyNetworkDataDictToPropertyTable();
}
while (0);
unlockForArbitration();
return ret;
}
bool IONetworkInterface::removeNetworkData(const char * aKey)
{
bool ret = false;
lockForArbitration();
do {
if (_clientSet->getCount())
break;
_dataDict->removeObject(aKey);
ret = _copyNetworkDataDictToPropertyTable();
}
while (0);
unlockForArbitration();
return ret;
}
//---------------------------------------------------------------------------
// Add an IONetworkData object to a dictionary kept by
// the interface.
//
// aData: An IONetworkData object.
//
// Returns true if the data object was added successfully, false otherwise.
bool IONetworkInterface::addNetworkData(IONetworkData * aData)
{
bool ret = false;
if (OSDynamicCast(IONetworkData, aData) == 0)
return false;
lockForArbitration();
if (_clientSet->getCount() == 0)
{
if ((ret = _dataDict->setObject(aData->getKey(), aData)))
ret = _copyNetworkDataDictToPropertyTable();
}
unlockForArbitration();
return ret;
}
//---------------------------------------------------------------------------
// Create a new IOUserClient to handle client requests. The default
// implementation will create an IONetworkUserClient instance if
// the type is kIONUCType.
IOReturn IONetworkInterface::newUserClient(task_t owningTask,
void * /*security_id*/,
UInt32 type,
IOUserClient ** handler)
{
IOReturn err = kIOReturnSuccess;
IONetworkUserClient * client;
if (type != kIONUCType)
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;
}