/* * Copyright (c) 1998-2008 Apple 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@ */ /* * IONetworkInterface.cpp * * HISTORY * 8-Jan-1999 Joe Liu (jliu) created. * */ extern "C" { #include #include #include #include #include #include #include #include #include #include #include #include #define _IP_VHL #include #include } #include #include #include #include #include #include "IONetworkUserClient.h" #include "IONetworkStack.h" #include "IONetworkTypesPrivate.h" #include "IONetworkControllerPrivate.h" #include "IONetworkDebug.h" #include "IOMbufQueue.h" #include //------------------------------------------------------------------------------ #define super IOService OSDefineMetaClassAndAbstractStructors( IONetworkInterface, IOService ) OSMetaClassDefineReservedUsed( IONetworkInterface, 5); OSMetaClassDefineReservedUsed( IONetworkInterface, 6); OSMetaClassDefineReservedUsed( IONetworkInterface, 7); OSMetaClassDefineReservedUsed( IONetworkInterface, 8); OSMetaClassDefineReservedUsed( IONetworkInterface, 9); OSMetaClassDefineReservedUsed( IONetworkInterface, 10); OSMetaClassDefineReservedUnused( IONetworkInterface, 11); OSMetaClassDefineReservedUnused( IONetworkInterface, 12); OSMetaClassDefineReservedUnused( IONetworkInterface, 13); OSMetaClassDefineReservedUnused( IONetworkInterface, 14); OSMetaClassDefineReservedUnused( IONetworkInterface, 15); //------------------------------------------------------------------------------ // Macros #define IFNET_TO_THIS(x) ((IONetworkInterface *) ifnet_softc(ifp)) #define WAITING_FOR_DETACH(n) ((n)->_clientVar[1]) #define _unit _reserved->unit #define _type _reserved->type #define _mtu _reserved->mtu #define _flags _reserved->flags #define _eflags _reserved->eflags #define _addrlen _reserved->addrlen #define _hdrlen _reserved->hdrlen #define _loggingLevel _reserved->loggingLevel #define _outputQueueModel _reserved->outputQueueModel #define _inputDeltas _reserved->inputDeltas #define _driverStats _reserved->driverStats #define _lastDriverStats _reserved->lastDriverStats #define _publicLock _reserved->publicLock #define _remote_NMI_pattern _reserved->remote_NMI_pattern #define _remote_NMI_len _reserved->remote_NMI_len #define _controller _reserved->controller #define _configFlags _reserved->configFlags #define _txRingSize _reserved->txRingSize #define _txPullOptions _reserved->txPullOptions #define _txQueueSize _reserved->txQueueSize #define _txSchedulingModel _reserved->txSchedulingModel #define _txThreadState _reserved->txThreadState #define _txThreadFlags _reserved->txThreadFlags #define _txThreadSignal _reserved->txThreadSignal #define _txThreadSignalLast _reserved->txThreadSignalLast #define _txStartThread _reserved->txStartThread #define _txStartAction _reserved->txStartAction #define _txWorkLoop _reserved->txWorkLoop #define _rxRingSize _reserved->rxRingSize #define _rxPollOptions _reserved->rxPollOptions #define _rxPollModel _reserved->rxPollModel #define _rxPollAction _reserved->rxPollAction #define _rxCtlAction _reserved->rxCtlAction #define _rxPollEmpty _reserved->rxPollEmpty #define _rxPollTotal _reserved->rxPollTotal #define _peqHandler _reserved->peqHandler #define _peqTarget _reserved->peqTarget #define _peqRefcon _reserved->peqRefcon #define _subType _reserved->subType #define kRemoteNMI "remote_nmi" #define REMOTE_NMI_PATTERN_LEN 32 // _txThreadState #define kTxThreadStateInit 0x00000001 // initial state #define kTxThreadStateStop 0x00000100 // temporarily disable #define kTxThreadStateDetach 0x00000200 // permanently disable #define kTxThreadStateHalted 0x00000800 // stop confirmation #define kTxThreadStatePurge 0x00001000 // purge new packets // _txThreadFlags #define kTxThreadWakeupEnable 0x00000001 // enable ifnet_start call #define kTxThreadWakeupSignal 0x00000002 // driver signaled #define kTxThreadWakeupMask 0x00000003 // wakeup bits enum { kConfigFrozen = 0x01, kConfigTxPull = 0x02, kConfigRxPoll = 0x04, kConfigDataRates = 0x08, kConfigPreEnqueue = 0x10 }; //------------------------------------------------------------------------------ // Initialize an IONetworkInterface instance. // // Returns true if initialized successfully, false otherwise. bool IONetworkInterface::init( IONetworkController * controller ) { IONetworkData * nd; #if TARGET_OS_EMBEDDED OSString * networkType; #endif // Propagate the init() call to our superclass. if ( super::init() == false ) goto fail; // A non-null value at this point means the subclass is pre-kpi // and allocated its ifnet/arpcom itself. // We can't work with such a sublcass but at least we can fail gracefully. if (getIfnet()) { IOLog("\33[00m\33[31m%s: IONetworkingFamily interface KPIs do not support IONetworkInterface subclasses that use ifnets\n\33[00m", getName()); goto fail; } // The controller object provided must be valid. if ( OSDynamicCast(IONetworkController, controller) == 0 ) goto fail; _driver = controller; _driver->retain(); // Allocate memory for the ExpansionData structure. _reserved = IONew( ExpansionData, 1 ); if ( _reserved == 0 ) goto fail; // Initialize the fields in the ExpansionData structure. bzero(_reserved, sizeof(ExpansionData)); _privateLock = IOLockAlloc(); if ( _privateLock == 0) goto fail; _publicLock = IORecursiveLockAlloc(); if ( _publicLock == 0 ) goto fail; _controller = controller; _rxPollModel = IFNET_MODEL_INPUT_POLL_OFF; _inputPushQueue = IONew(IOMbufQueue, 1); bzero(_inputPushQueue, sizeof(*_inputPushQueue)); // Set initial queue state before attaching to network stack. _txThreadState = kTxThreadStateInit | kTxThreadStateHalted; // Create an OSNumber to store interface state bits. _stateBits = OSNumber::withNumber((UInt64) 0, 32); if ( _stateBits == 0 ) goto fail; setProperty( kIOInterfaceState, _stateBits ); // Create an OSSet to store client objects. Initial capacity // (which can grow) is set at 2 clients. _clientSet = OSSet::withCapacity(2); if ( _clientSet == 0 ) goto fail; // Dictionary to store network data. if ( (_dataDict = OSDictionary::withCapacity(5)) == 0 ) goto fail; nd = IONetworkData::withExternalBuffer( kIONetworkStatsKey, sizeof(IONetworkStats), &_driverStats); if ( nd ) { addNetworkData(nd); nd->release(); } // Register default output handler (not used for pull transmit model) if (!registerOutputHandler(controller, controller->getOutputHandler())) goto fail; // Set the kIOInterfaceNamePrefix and kIOPrimaryInterface properties. // These may be used by an user space agent as hints when assigning a // BSD name for the interface. setProperty( kIOInterfaceNamePrefix, getNamePrefix() ); #if TARGET_OS_EMBEDDED networkType = OSDynamicCast(OSString, controller->getProperty( "IONetworkRootType" )); if (networkType) setProperty( "IONetworkRootType", networkType ); #endif /* TARGET_OS_EMBEDDED */ if (IOService *provider = controller->getProvider()) { bool gotLocation = false; OSData * locationAsData; OSData * locationAsCstr; OSString * locationAsString; setProperty(kIOBuiltin, (bool)(provider->getProperty("built-in"))); if ((locationAsData = OSDynamicCast(OSData, provider->getProperty("location")))) { // the data may be null terminated...but to be on the safe side, // let's assume it's not, and add a null... // create a copy that we can convert to C string if ((locationAsCstr = OSData::withData(locationAsData))) { locationAsCstr->appendByte(0, 1); // now create the OSString if ((locationAsString = OSString::withCString( (const char *) locationAsCstr->getBytesNoCopy()))) { gotLocation = true; setProperty(kIOLocation, locationAsString); locationAsString->release(); //setProperty took a ref } locationAsCstr->release(); } } if (!gotLocation) setProperty(kIOLocation, ""); } DLOG("IONetworkInterface::init(%p, %s)\n", this, controller->getName()); return true; fail: LOG("IONetworkInterface::init(%p, %s) failed\n", this, controller->getName()); return false; } //------------------------------------------------------------------------------ // Destroy the interface. Release all allocated resources. void IONetworkInterface::free( void ) { DLOG("IONetworkInterface::free(%p)\n", this); if (_driver) { _driver->release(); _driver = 0; } if ( _clientSet ) { // Should not have any clients. assert(_clientSet->getCount() == 0); _clientSet->release(); _clientSet = 0; } if ( _dataDict ) { _dataDict->release(); _dataDict = 0; } if ( _stateBits ) { _stateBits->release(); _stateBits = 0; } if ( _inputPushQueue ) { clearInputQueue(); IODelete(_inputPushQueue, IOMbufQueue, 1); _inputPushQueue = 0; } if ( _privateLock ) { IOLockFree(_privateLock); _privateLock = 0; } if (_backingIfnet) { ifnet_release(_backingIfnet); _backingIfnet = 0; } // Free resources referenced through fields in the ExpansionData // structure, and also the structure itself. if ( _reserved ) { if ( _publicLock ) { IORecursiveLockFree(_publicLock); _publicLock = 0; } if (_remote_NMI_pattern) { IOFree(_remote_NMI_pattern, _remote_NMI_len); } if (_txWorkLoop) _txWorkLoop->release(); memset(_reserved, 0, sizeof(ExpansionData)); IODelete(_reserved, ExpansionData, 1); _reserved = 0; } super::free(); } //------------------------------------------------------------------------------ // Returns true if the receiver of this method is the system's primary // network interface. bool IONetworkInterface::isPrimaryInterface() const { IOService * provider = _driver; bool isPrimary = false; if ( provider ) provider = provider->getProvider(); // Look for the built-in property in the ethernet entry. if ( provider && provider->getProperty("built-in") && getUnitNumber() == 0) { isPrimary = true; } return isPrimary; } //------------------------------------------------------------------------------ IONetworkController * IONetworkInterface::getController( void ) const { return _controller; } //------------------------------------------------------------------------------ // Get the value that should be set in the hwassist field in the ifnet // structure. Currently, this field is solely used for advertising the // hardware checksumming support. static UInt32 getIfnetHardwareAssistValue( IONetworkController * driver ) { UInt32 input; UInt32 output; UInt32 hwassist = 0; UInt32 driverFeatures = driver->getFeatures(); do { if ( driver->getChecksumSupport( &input, IONetworkController::kChecksumFamilyTCPIP, false ) != kIOReturnSuccess ) break; if ( driver->getChecksumSupport( &output, IONetworkController::kChecksumFamilyTCPIP, true ) != kIOReturnSuccess ) break; if ( input & output & IONetworkController::kChecksumIP ) { hwassist |= IFNET_CSUM_IP; } if ( ( input & ( IONetworkController::kChecksumTCP | IONetworkController::kChecksumTCPNoPseudoHeader ) ) && ( output & ( IONetworkController::kChecksumTCP ) ) ) { hwassist |= IFNET_CSUM_TCP; } if ( ( input & ( IONetworkController::kChecksumUDP | IONetworkController::kChecksumUDPNoPseudoHeader ) ) && ( output & ( IONetworkController::kChecksumUDP ) ) ) { hwassist |= IFNET_CSUM_UDP; } if ( input & output & IONetworkController::kChecksumTCPSum16 ) { hwassist |= ( IFNET_CSUM_SUM16 | IFNET_CSUM_TCP | IFNET_CSUM_UDP ); } if ( input & output & IONetworkController::kChecksumTCPIPv6 ) { hwassist |= IFNET_CSUM_TCPIPV6; } if ( input & output & IONetworkController::kChecksumUDPIPv6 ) { hwassist |= IFNET_CSUM_UDPIPV6; } } while ( false ); if( driverFeatures & kIONetworkFeatureHardwareVlan) hwassist |= IFNET_VLAN_TAGGING; if( driverFeatures & kIONetworkFeatureSoftwareVlan) hwassist |= IFNET_VLAN_MTU; if( driverFeatures & kIONetworkFeatureMultiPages) hwassist |= IFNET_MULTIPAGES; if( driverFeatures & kIONetworkFeatureTSOIPv4) hwassist |= IFNET_TSO_IPV4; if( driverFeatures & kIONetworkFeatureTSOIPv6) hwassist |= IFNET_TSO_IPV6; if (driverFeatures & kIONetworkFeatureTransmitCompletionStatus) hwassist |= IFNET_TX_STATUS; return hwassist; } //------------------------------------------------------------------------------ // Initialize the ifnet structure. bool IONetworkInterface::initIfnet( struct ifnet * ifp ) { return false; // deprecated - replaced by initIfnetParams() } OSMetaClassDefineReservedUsed(IONetworkInterface, 4); bool IONetworkInterface::initIfnetParams( struct ifnet_init_params *params ) { // Register our 'shim' functions. These function pointers // points to static member functions inside this class. params->name = (char *) getNamePrefix(); params->type = _type; params->unit = _unit; params->output = if_output; params->ioctl = if_ioctl; params->set_bpf_tap = if_set_bpf_tap; params->detach = if_detach; params->softc = this; return true; } //------------------------------------------------------------------------------ // Take/release the interface lock. void IONetworkInterface::lock() { IORecursiveLockLock(_publicLock); } void IONetworkInterface::unlock() { IORecursiveLockUnlock(_publicLock); } //------------------------------------------------------------------------------ // Inspect the controller after it has been opened. bool IONetworkInterface::controllerDidOpen( IONetworkController * controller ) { return true; // by default, always accept the controller open. } //------------------------------------------------------------------------------ // Perform cleanup before the controller is closed. void IONetworkInterface::controllerWillClose( IONetworkController * controller ) { } //------------------------------------------------------------------------------ // Handle a client open on the interface (IONetworkStack or user client) bool IONetworkInterface::handleOpen( IOService * client, IOOptionBits options, void * argument ) { bool accept = false; bool controllerOpen = false; do { // Was this object already registered as our client? if ( _clientSet->containsObject(client) ) { DLOG("%s: rejected open from existing client %s\n", getName(), client->getName()); accept = true; break; } // If the interface has not received a client open, which also // implies that the interface has not yet opened the controller, // then open the controller upon receiving the first open from // a client. If the controller open fails, the client open will // be rejected. if (( getInterfaceState() & kIONetworkInterfaceOpenedState ) == 0) { if (!_driver || ((controllerOpen = _driver->open(this)) == false) || (controllerDidOpen(_driver) == false)) break; } // Allow subclasses to intercept. if ( handleClientOpen(client, options, argument) == false ) break; // Add the new client object to our client set. if ( _clientSet->setObject(client) == false ) { handleClientClose(client, 0); break; } accept = true; } while (false); // If provider was opened above, but an error has caused us to refuse // the client open, then close our provider. if ( controllerOpen ) { if (accept) { setInterfaceState( kIONetworkInterfaceOpenedState ); _driver->registerInterestedDriver( this ); } else { controllerWillClose(_driver); _driver->close(this); } } return accept; } //------------------------------------------------------------------------------ // Handle a client close on the interface. void IONetworkInterface::handleClose( IOService * client, IOOptionBits options ) { if ( _clientSet->containsObject(client) ) { // Call handleClientClose() for subclass to handle the client close. handleClientClose( client, options ); // Close our provider on last client close. if ( _clientSet->getCount() == 1 ) { _driver->deRegisterInterestedDriver( this ); controllerWillClose( _driver ); _driver->close( this ); setInterfaceState( 0, kIONetworkInterfaceOpenedState ); // Closed by IONetworkStack after detaching interface, // drop the driver retain from init(). if (!isRegistered()) { _driver->release(); _driver = 0; } } _clientSet->removeObject(client); } } //------------------------------------------------------------------------------ // Query whether a client has an open on the interface. bool IONetworkInterface::handleIsOpen( const IOService * client ) const { if (client) return _clientSet->containsObject(client); else return (_clientSet->getCount() > 0); } //------------------------------------------------------------------------------ // Handle a client open on the interface. bool IONetworkInterface::handleClientOpen(IOService * client, IOOptionBits options, void * argument) { return true; } //------------------------------------------------------------------------------ // Handle a client close on the interface. void IONetworkInterface::handleClientClose(IOService * client, IOOptionBits options) { } //------------------------------------------------------------------------------ // Register the output packet handler. bool IONetworkInterface::registerOutputHandler(OSObject * target, IOOutputAction action) { IOLockLock(_privateLock); // Sanity check the arguments. if ( (getInterfaceState() & kIONetworkInterfaceOpenedState) || !target || !action ) { IOLockUnlock(_privateLock); return false; } _outTarget = target; _outAction = action; IOLockUnlock(_privateLock); return true; } //------------------------------------------------------------------------------ // Feed packets to the input/output BPF packet filter taps. OSMetaClassDefineReservedUsed(IONetworkInterface, 2); void IONetworkInterface::feedPacketInputTap(mbuf_t m) { // PR3662433 we're not protected from this getting changed out from under us // so use a local ptr and double check for NULL to avoid the race bpf_packet_func inFilter = _inputFilterFunc; if (inFilter) inFilter(_backingIfnet, m); } OSMetaClassDefineReservedUsed(IONetworkInterface, 3); void IONetworkInterface::feedPacketOutputTap(mbuf_t m) { // see comment in feedPacketInputTap bpf_packet_func outFilter = _outputFilterFunc; if (outFilter) outFilter(_backingIfnet, m); } //------------------------------------------------------------------------------ #define ABS(a) ((a) < 0 ? -(a) : (a)) void IONetworkInterface::pushInputQueue( IOMbufQueue * queue ) { uint32_t temp; int delta; _inputDeltas.packets_in = queue->count; _inputDeltas.bytes_in = queue->bytes; // Report our packet count rather than rely on the driver stats. // ifnet_input_extended() requires the count to be accurate. temp = _driverStats.inputErrors; delta = temp - _lastDriverStats.inputErrors; _inputDeltas.errors_in = ABS(delta); _lastDriverStats.inputErrors = temp; temp = _driverStats.collisions; delta = temp - _lastDriverStats.collisions; _inputDeltas.collisions = ABS(delta); _lastDriverStats.collisions = temp; ifnet_input_extended(_backingIfnet, queue->head, queue->tail, &_inputDeltas); IOMbufQueueInit(queue); } void IONetworkInterface::pushInputPacket( mbuf_t packet, uint32_t length ) { uint32_t temp; int delta; _inputDeltas.packets_in = 1; _inputDeltas.bytes_in = length; // Report our packet count rather than rely on the driver stats. // ifnet_input_extended() requires the count to be accurate. temp = _driverStats.inputErrors; delta = temp - _lastDriverStats.inputErrors; _inputDeltas.errors_in = ABS(delta); _lastDriverStats.inputErrors = temp; temp = _driverStats.collisions; delta = temp - _lastDriverStats.collisions; _inputDeltas.collisions = ABS(delta); _lastDriverStats.collisions = temp; ifnet_input_extended(_backingIfnet, packet, packet, &_inputDeltas); } UInt32 IONetworkInterface::flushInputQueue( void ) { UInt32 count = _inputPushQueue->count; if (count) { //DLOG("push pkt cnt %u\n", count); pushInputQueue(_inputPushQueue); } return count; } UInt32 IONetworkInterface::clearInputQueue( void ) { UInt32 count = _inputPushQueue->count; if (count) mbuf_freem_list(_inputPushQueue->head); IOMbufQueueInit(_inputPushQueue); return count; } inline static const char *get_icmp_data(mbuf_t *pkt, int hdrlen, int datalen) { struct ip *ip; struct icmp *icmp; int hlen, icmplen; icmplen = sizeof(*icmp) + sizeof(struct timeval); /* make sure hdrlen is sane with respect to mbuf */ if (mbuf_len(*pkt) < (sizeof(*ip) + hdrlen)) goto error; /* only work for IPv4 packets */ ip = (struct ip *) ((char *) mbuf_data(*pkt) + hdrlen); if (IP_VHL_V(ip->ip_vhl) != IPVERSION) goto error; hlen = IP_VHL_HL(ip->ip_vhl) << 2; /* make sure header and data is contiguous */ if (mbuf_pullup(pkt, hlen + icmplen) != 0) goto error; /* refresh the pointer and hlen in case buffer was shifted */ ip = (struct ip *) ((char *) mbuf_data(*pkt) + hdrlen); hlen = IP_VHL_HL(ip->ip_vhl) << 2; if (ip->ip_p != IPPROTO_ICMP) goto error; if (ip->ip_len < (icmplen + datalen)) goto error; icmp = (struct icmp *) (((char *) mbuf_data(*pkt) + hdrlen) + hlen); if (icmp->icmp_type != ICMP_ECHO) goto error; return (const char *) (((char *) mbuf_data(*pkt) + hdrlen) + icmplen); error: return NULL; } UInt32 IONetworkInterface::inputPacket( mbuf_t packet, UInt32 length, IOOptionBits options, void * param ) { const UInt32 hdrlen = _hdrlen; UInt32 count; void * mdata; assert(packet); assert(_backingIfnet); if (!packet || !_backingIfnet) return 0; assert((mbuf_flags(packet) & MBUF_PKTHDR)); assert((mbuf_nextpkt(packet) == 0)); mbuf_pkthdr_setrcvif(packet, _backingIfnet); if (length) { // Driver wants interface to set the mbuf length. if (mbuf_next(packet) == 0) { mbuf_pkthdr_setlen(packet, length); mbuf_setlen(packet, length); } else { // rare case of single packet but multiple mbufs. mbuf_t m = packet; uint32_t remain = length; mbuf_pkthdr_setlen(packet, remain); do { if (remain < (UInt32) mbuf_len(m)) mbuf_setlen(m, remain); remain -= mbuf_len(m); } while ((m = mbuf_next(m))); assert(remain == 0); } } else { length = mbuf_pkthdr_len(packet); } // check for special debugger packet if (_remote_NMI_len) { const char *data = get_icmp_data(&packet, hdrlen, _remote_NMI_len); if (data && (memcmp(data, _remote_NMI_pattern, _remote_NMI_len) == 0)) { IOKernelDebugger::signalDebugger(); } } // input BPF tap if (_inputFilterFunc) feedPacketInputTap(packet); // frame header at start of mbuf data mdata = mbuf_data(packet); mbuf_pkthdr_setheader(packet, mdata); // packet length does not include the frame header mbuf_pkthdr_setlen(packet, length - hdrlen); // adjust the mbuf data and length to skip over the frame header mbuf_setdata(packet, (char *)mdata + hdrlen, mbuf_len(packet) - hdrlen); if ( options & kInputOptionQueuePacket ) { IOMbufQueueTailAdd(_inputPushQueue, packet, length); count = 0; } else { if (!IOMbufQueueIsEmpty(_inputPushQueue)) { IOMbufQueueTailAdd(_inputPushQueue, packet, length); count = _inputPushQueue->count; pushInputQueue(_inputPushQueue); } else { pushInputPacket(packet, length); count = 1; } } return count; } //------------------------------------------------------------------------------ // Deliver an event to the network layer. bool IONetworkInterface::inputEvent(UInt32 type, void * data) { bool success = true; struct { kern_event_msg header; uint32_t unit; char if_name[IFNAMSIZ]; } event; const IONetworkLinkEventData * linkData; switch (type) { // Deliver an IOKit defined event. case kIONetworkEventTypeLinkUp: case kIONetworkEventTypeLinkDown: case kIONetworkEventTypeLinkSpeedChange: // Send an event only if DLIL has a reference to this // interface. linkData = (const IONetworkLinkEventData *) data; if (!_backingIfnet || !linkData) break; // Use link speed to report bandwidth for legacy drivers if ((_configFlags & kConfigDataRates) == 0) { if_bandwidths_t bw; bw.max_bw = linkData->linkSpeed; bw.eff_bw = linkData->linkSpeed; ifnet_set_bandwidths(_backingIfnet, &bw, &bw); } if ((type == kIONetworkEventTypeLinkUp) || (type == kIONetworkEventTypeLinkDown)) { bzero(&event, sizeof(event)); event.header.total_size = sizeof(event); event.header.vendor_code = KEV_VENDOR_APPLE; event.header.kev_class = KEV_NETWORK_CLASS; event.header.kev_subclass = KEV_DL_SUBCLASS; event.header.event_code = (type == kIONetworkEventTypeLinkUp) ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF; event.header.event_data[0] = ifnet_family(_backingIfnet); event.unit = ifnet_unit(_backingIfnet); strncpy(&event.if_name[0], ifnet_name(_backingIfnet), IFNAMSIZ); ifnet_event(_backingIfnet, &event.header); } break; // Deliver a raw kernel event to DLIL. // The data argument must point to a kern_event_msg structure. case kIONetworkEventTypeDLIL: ifnet_event(_backingIfnet, (struct kern_event_msg *) data); break; case kIONetworkEventWakeOnLANSupportChanged: break; default: IOLog("IONetworkInterface: unknown event type %x\n", (uint32_t) type); success = false; break; } return success; } //------------------------------------------------------------------------------ // SIOCSIFMTU (set interface MTU) ioctl handler. SInt32 IONetworkInterface::syncSIOCSIFMTU(IONetworkController * ctr, struct ifreq * ifr) { SInt32 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( ctr->setMaxPacketSize(newMTU) ); if ( error == 0 ) { // Controller reports success. Update the interface MTU size // property. setMaxTransferUnit(newMTU); } return error; } //------------------------------------------------------------------------------ // SIOCSIFMEDIA (SET interface media) ioctl handler. SInt32 IONetworkInterface::syncSIOCSIFMEDIA(IONetworkController * ctr, struct ifreq * ifr) { OSDictionary * mediumDict; IONetworkMedium * medium; SInt32 error; mediumDict = ctr->copyMediumDictionary(); // creates a copy if ( mediumDict == 0 ) { // unable to allocate memory, or no medium dictionary. return EOPNOTSUPP; } do { // Look for an exact match on the media type. medium = IONetworkMedium::getMediumWithType( mediumDict, ifr->ifr_media ); if ( medium ) break; // Try a partial match. ifconfig tool sets the media type and media // options separately. When media options are changed, the options // bits are set or cleared based on the current media selection. OSSymbol * selMediumName = (OSSymbol *) ctr->copyProperty( kIOSelectedMedium ); if ( selMediumName ) { UInt32 modMediumBits; IONetworkMedium * selMedium = (IONetworkMedium *) mediumDict->getObject( selMediumName ); if ( selMedium && (modMediumBits = selMedium->getType() ^ ifr->ifr_media) ) { medium = IONetworkMedium::getMediumWithType( mediumDict, ifr->ifr_media, ~( IFM_TMASK | IFM_NMASK | modMediumBits)); } selMediumName->release(); } if ( medium ) break; // Still no match, look for a medium that matches the network type // and sub-type. medium = IONetworkMedium::getMediumWithType( mediumDict, ifr->ifr_media, ~( IFM_TMASK | IFM_NMASK )); } while ( false ); // 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 is // harmless since IONetworkController will filter out invalid // selections before calling the driver. if ( medium ) error = errnoFromReturn( ctr->selectMediumWithName(medium->getName()) ); else error = EINVAL; mediumDict->release(); return error; } //------------------------------------------------------------------------------ // SIOCGIFMEDIA (GET interface media) ioctl handler. SInt32 IONetworkInterface::syncSIOCGIFMEDIA(IONetworkController * ctr, struct ifreq * ifr, unsigned long cmd) { OSDictionary * mediumDict = 0; UInt mediumCount = 0; UInt maxCount; OSCollectionIterator * iter = 0; UInt32 * typeList; UInt typeListSize; OSSymbol * keyObject; SInt32 error = 0; struct ifmediareq * ifmr = (struct ifmediareq *) ifr; if (ifmr->ifm_count < 0) return EINVAL; do { mediumDict = ctr->copyMediumDictionary(); // creates a copy if (mediumDict == 0) { error = EOPNOTSUPP; break; // unable to allocate memory, or no medium dictionary. } if ((mediumCount = mediumDict->getCount()) == 0) { error = EOPNOTSUPP; break; // no medium in the medium dictionary } // Maximum number of medium types that the caller will accept. // maxCount = ifmr->ifm_count; if (maxCount == 0) break; // caller is only probing for support and media count. if (maxCount > mediumCount) { maxCount = mediumCount; } else 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 = (IONetworkMedium *) mediumDict->getObject(keyObject); if (!medium) continue; // should not happen! typeList[mediumCount++] = medium->getType(); } if (mediumCount) { user_addr_t srcaddr; // here's where the difference in ioctls needs to be accounted for. srcaddr = (cmd == SIOCGIFMEDIA64) ? ((struct ifmediareq64 *)ifmr)->ifmu_ulist : CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist); if (srcaddr != USER_ADDR_NULL) { error = copyout((caddr_t) typeList, srcaddr, typeListSize); } } IOFree(typeList, typeListSize); } while (0); ifmr->ifm_active = ifmr->ifm_current = IFM_NONE; ifmr->ifm_status = 0; ifmr->ifm_count = mediumCount; // Get a copy of the controller's property table and read the // link status, current, and active medium. OSDictionary * pTable = ctr->dictionaryWithProperties(); if (pTable) { OSNumber * linkStatus = (OSNumber *) pTable->getObject(kIOLinkStatus); if (linkStatus) ifmr->ifm_status = linkStatus->unsigned32BitValue(); if (mediumDict) { IONetworkMedium * medium; OSSymbol * mediumName; if ((mediumName = (OSSymbol *) pTable->getObject(kIOSelectedMedium)) && (medium = (IONetworkMedium *) mediumDict->getObject(mediumName))) { ifmr->ifm_current = medium->getType(); } if ((mediumName = (OSSymbol *) pTable->getObject(kIOActiveMedium)) && (medium = (IONetworkMedium *) mediumDict->getObject(mediumName))) { ifmr->ifm_active = medium->getType(); } } pTable->release(); } if (iter) iter->release(); if (mediumDict) mediumDict->release(); return error; } //------------------------------------------------------------------------------ // Handle ioctl commands sent to the network interface. SInt32 IONetworkInterface::performCommand(IONetworkController * ctr, unsigned long cmd, void * arg0, void * arg1) { struct ifreq * ifr = (struct ifreq *) arg1; SInt32 ret = EOPNOTSUPP; if ( (ifr == 0) || (ctr == 0) ) return EINVAL; switch ( cmd ) { // Get interface MTU. case SIOCGIFMTU: ifr->ifr_mtu = getMaxTransferUnit(); ret = 0; // no error break; // Get interface media type and status. case SIOCGIFMEDIA32: case SIOCGIFMEDIA64: ret = syncSIOCGIFMEDIA(ctr, ifr, cmd); break; case SIOCSIFMTU: case SIOCSIFMEDIA: case SIOCSIFLLADDR: ret = (int) ctr->executeCommand( this, /* client */ (IONetworkController::Action) &IONetworkInterface::performGatedCommand, this, /* target */ ctr, /* param0 */ (void *) cmd, /* param1 */ arg0, /* param2 */ arg1 ); /* param3 */ break; default: // DLOG(%s: command not handled (%08lx), getName(), cmd); break; } return ret; } //------------------------------------------------------------------------------ // Perform an ioctl command on the controller's workloop context. int IONetworkInterface::performGatedCommand(void * target, void * arg1_ctr, void * arg2_cmd, void * arg3_0, void * arg4_1) { IONetworkInterface * self = (IONetworkInterface *) target; IONetworkController * ctr = (IONetworkController *) arg1_ctr; struct ifreq * ifr = (struct ifreq *) arg4_1; SInt32 ret = EOPNOTSUPP; // Refuse to issue I/O to the controller if it is in a power state // that renders it "unusable". if ( self->getInterfaceState() & kIONetworkInterfaceDisabledState ) return EPWROFF; switch ( (uintptr_t) arg2_cmd ) { // Set interface MTU. case SIOCSIFMTU: ret = self->syncSIOCSIFMTU(ctr, ifr); break; // Set interface (controller) media type. case SIOCSIFMEDIA: ret = self->syncSIOCSIFMEDIA(ctr, ifr); break; // Set link layer address. case SIOCSIFLLADDR: ret = ctr->errnoFromReturn( ctr->setHardwareAddress( ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len ) ); break; } return ret; } //------------------------------------------------------------------------------ // if_ioctl() handler - Calls performCommand() when we receive an ioctl // from DLIL. errno_t IONetworkInterface::if_ioctl( ifnet_t ifp, unsigned long cmd, void * data ) { IONetworkInterface * self = IFNET_TO_THIS(ifp); IONetworkController * driver; errno_t err; if (!self || self->isInactive()) { return EOPNOTSUPP; } driver = self->_driver; if (!driver) { return EINVAL; } assert(ifp == self->_backingIfnet); err = self->performCommand( driver, cmd, (void *) ifp, data ); return err; } //------------------------------------------------------------------------------ // if_output() handler. // // Handle a call from the network stack to transmit the given mbuf. // For now, we can assume that the mbuf is singular, and never chained. int IONetworkInterface::if_output( ifnet_t ifp, mbuf_t m ) { UInt32 noraceTemp; int delta; u_int32_t outPackets, outErrors; IONetworkInterface * self = IFNET_TO_THIS(ifp); assert(ifp == self->_backingIfnet); if ( m == 0 ) { DLOG("%s: NULL output mbuf\n", self->getName()); return EINVAL; } if ( (mbuf_flags(m) & MBUF_PKTHDR) == 0 ) { DLOG("%s: MBUF_PKTHDR bit not set\n", self->getName()); mbuf_freem(m); return EINVAL; } // Increment output related statistics. // update the stats that the driver maintains noraceTemp = self->_driverStats.outputErrors; delta = noraceTemp - self->_lastDriverStats.outputErrors; outErrors = ABS(delta); self->_lastDriverStats.outputErrors = noraceTemp; noraceTemp = self->_driverStats.outputPackets; delta = noraceTemp - self->_lastDriverStats.outputPackets; outPackets = ABS(delta); self->_lastDriverStats.outputPackets = noraceTemp; // update the stats in the interface ifnet_stat_increment_out(self->getIfnet(), outPackets, mbuf_pkthdr_len(m), outErrors); // Feed the output filter tap. if(self->_outputFilterFunc) self->feedPacketOutputTap(m); // Forward the packet to the registered output packet handler. return ((self->_outTarget)->*(self->_outAction))(m, 0); } //------------------------------------------------------------------------------ // if_set_bpf_tap() handler. Handles request from the DLIL to enable or // disable the input/output filter taps. errno_t IONetworkInterface::if_set_bpf_tap( ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func ) { IONetworkInterface * self = IFNET_TO_THIS(ifp); bool changed = false; assert(ifp == self->_backingIfnet); if (self->isInactive()) { return 0; } IOLockLock(self->_privateLock); switch ( mode ) { case BPF_TAP_DISABLE: changed = (self->_inputFilterFunc != 0); self->_inputFilterFunc = self->_outputFilterFunc = 0; break; case BPF_TAP_INPUT: assert(func); changed = (self->_inputFilterFunc == 0); self->_inputFilterFunc = func; break; case BPF_TAP_OUTPUT: assert(func); self->_outputFilterFunc = func; break; case BPF_TAP_INPUT_OUTPUT: assert(func); changed = (self->_inputFilterFunc == 0); self->_inputFilterFunc = self->_outputFilterFunc = func; break; default: DLOG("%s: Unknown BPF tap mode %d\n", self->getName(), mode); break; } IOLockUnlock(self->_privateLock); if (changed) self->notifyDriver(kIONetworkNotificationBPFTapStateChange, 0); return 0; } //------------------------------------------------------------------------------ void IONetworkInterface::if_detach( ifnet_t ifp ) { IONetworkInterface * self = IFNET_TO_THIS(ifp); assert(WAITING_FOR_DETACH(self) == 1); self->retain(); IOLockLock(self->_privateLock); WAITING_FOR_DETACH(self) = 0; thread_wakeup((void *) self); IOLockUnlock(self->_privateLock); self->release(); } //------------------------------------------------------------------------------ // ifnet field (and property table) getter/setter. #define IO_IFNET_GET(func, type, field, kpi) \ type IONetworkInterface::func(void) const \ { \ type val = (_backingIfnet != NULL) ? \ kpi(_backingIfnet) : _##field; \ return val; \ } #define IO_IFNET_SET(func, type, field, kpi) \ bool IONetworkInterface::func(type value) \ { \ if (_backingIfnet) \ kpi(_backingIfnet, value); \ else \ _##field = value; \ return true; \ } #define IO_IFNET_RMW(func, type, field, kpi) \ bool IONetworkInterface::func(type set, type clear) \ { \ if (_backingIfnet) \ kpi(_backingIfnet, set, set | clear); \ else \ _##field = (_##field & ~clear) | set; \ return true; \ } //------------------------------------------------------------------------------ // Interface type accessors (ifp->if_type). The list of interface types is // defined in . bool IONetworkInterface::setInterfaceType(UInt8 type) { // once attached to dlil, we can't change the interface type if (_backingIfnet) return false; else _type = type; return true; } UInt8 IONetworkInterface::getInterfaceType(void) const { return _type; } IO_IFNET_SET(setMaxTransferUnit, UInt32, mtu, ifnet_set_mtu) IO_IFNET_GET(getMaxTransferUnit, UInt32, mtu, ifnet_mtu) IO_IFNET_RMW(setFlags, UInt16, flags, ifnet_set_flags) IO_IFNET_GET(getFlags, UInt16, flags, ifnet_flags) bool IONetworkInterface::setExtraFlags( UInt32 set, UInt32 clear ) { _eflags = (_eflags & ~clear) | set; return true; } IO_IFNET_GET(getExtraFlags, UInt32, eflags, ifnet_eflags) IO_IFNET_SET(setMediaAddressLength, UInt8, addrlen, ifnet_set_addrlen) IO_IFNET_GET(getMediaAddressLength, UInt8, addrlen, ifnet_addrlen) IO_IFNET_SET(setMediaHeaderLength, UInt8, hdrlen, ifnet_set_hdrlen) IO_IFNET_GET(getMediaHeaderLength, UInt8, hdrlen, ifnet_hdrlen) IO_IFNET_GET(getUnitNumber, UInt16, unit, ifnet_unit) bool IONetworkInterface::setUnitNumber( UInt16 value ) { // once we've attached to dlil, unit can't be changed if (_backingIfnet) return false; if (setProperty( kIOInterfaceUnit, value, 32 )) { char name[128]; _unit = value; setProperty( kIOPrimaryInterface, isPrimaryInterface() ); snprintf(name, sizeof(name), "%s%u", getNamePrefix(), _unit); setName(name); return true; } else return false; } bool IONetworkInterface::setInterfaceSubType( uint32_t subType ) { // once attached to dlil, we can't change the interface sub-type if (_backingIfnet) return false; else _subType = subType; return true; } //------------------------------------------------------------------------------ // Return true if the interface has been registered with the network layer, // false otherwise. bool IONetworkInterface::isRegistered() const { return (bool)(getInterfaceState() & kIONetworkInterfaceRegisteredState); } //------------------------------------------------------------------------------ // serialize bool IONetworkInterface::serializeProperties( OSSerialize * s ) const { IONetworkInterface * self = (IONetworkInterface *) this; self->setProperty( kIOInterfaceType, getInterfaceType(), 8 ); self->setProperty( kIOMaxTransferUnit, getMaxTransferUnit(), 32 ); self->setProperty( kIOInterfaceFlags, getFlags(), 16 ); self->setProperty( kIOInterfaceExtraFlags, getExtraFlags(), 32 ); self->setProperty( kIOMediaAddressLength, getMediaAddressLength(), 8 ); self->setProperty( kIOMediaHeaderLength, getMediaHeaderLength(), 8 ); return super::serializeProperties( s ); } //------------------------------------------------------------------------------ // Return the interface state flags. UInt32 IONetworkInterface::getInterfaceState() const { return _stateBits->unsigned32BitValue(); } //------------------------------------------------------------------------------ // Set (or clear) the interface state flags. UInt32 IONetworkInterface::setInterfaceState( UInt32 set, UInt32 clear ) { UInt32 val; assert( _stateBits ); IOLockLock(_privateLock); val = ( _stateBits->unsigned32BitValue() | set ) & ~clear; _stateBits->setValue( val ); IOLockUnlock(_privateLock); return val; } //------------------------------------------------------------------------------ // 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::_syncNetworkDataDict() { OSDictionary * aCopy = OSDictionary::withDictionary(_dataDict); bool ret = false; if (aCopy) { ret = setProperty(kIONetworkData, aCopy); aCopy->release(); } return ret; } //------------------------------------------------------------------------------ // Remove an entry from the IONetworkData dictionary managed by the interface. // The removed object is released. bool IONetworkInterface::removeNetworkData(const OSSymbol * aKey) { bool ret; IOLockLock(_privateLock); _dataDict->removeObject(aKey); ret = _syncNetworkDataDict(); IOLockUnlock(_privateLock); return ret; } bool IONetworkInterface::removeNetworkData(const char * aKey) { bool ret; IOLockLock(_privateLock); _dataDict->removeObject(aKey); ret = _syncNetworkDataDict(); IOLockUnlock(_privateLock); return ret; } //------------------------------------------------------------------------------ // Add an IONetworkData object to a dictionary managed by the interface. bool IONetworkInterface::addNetworkData(IONetworkData * aData) { bool ret = false; if (OSDynamicCast(IONetworkData, aData) == 0) return false; IOLockLock(_privateLock); if (_dataDict->setObject(aData->getKey(), aData)) ret = _syncNetworkDataDict(); IOLockUnlock(_privateLock); return ret; } //------------------------------------------------------------------------------ // Create a new IOUserClient to handle client requests. The default // implementation will create an IONetworkUserClient instance if // the type given is kIONetworkUserClientTypeID. IOReturn IONetworkInterface::newUserClient(task_t owningTask, void * /*security_id*/, UInt32 type, IOUserClient ** handler) { IOReturn err = kIOReturnSuccess; IONetworkUserClient * client; if (type != kIONetworkUserClientTypeID) return kIOReturnBadArgument; client = IONetworkUserClient::withTask(owningTask); if (!client || !client->attach(this) || !client->start(this)) { if (client) { client->detach(this); client->release(); client = 0; } err = kIOReturnNoMemory; } *handler = client; return err; } //------------------------------------------------------------------------------ // Power change notices are posted by the controller's policy-maker to // inform the interface that the controller is changing power states. // There are two notifications for each state change, delivered prior // to the state change, and after the state change has occurred. struct IONetworkPowerChangeNotice { IOService * policyMaker; IOPMPowerFlags powerFlags; uint32_t stateNumber; uint32_t phase; }; enum { kPhasePowerStateWillChange = 0x01, kPhasePowerStateDidChange = 0x02 }; //------------------------------------------------------------------------------ // Handle controller's power state transitions. IOReturn IONetworkInterface::controllerWillChangePowerState( IONetworkController * controller, IOPMPowerFlags flags, UInt32 stateNumber, IOService * policyMaker ) { if ( ( flags & IOPMDeviceUsable ) == 0 ) { setInterfaceState( kIONetworkInterfaceDisabledState ); } return kIOReturnSuccess; } IOReturn IONetworkInterface::controllerDidChangePowerState( IONetworkController * controller, IOPMPowerFlags flags, UInt32 stateNumber, IOService * policyMaker ) { if ( flags & IOPMDeviceUsable ) { setInterfaceState( 0, kIONetworkInterfaceDisabledState ); } return kIOReturnSuccess; } //------------------------------------------------------------------------------ // Static member functions called by power-management notification handlers. // Act as stub functions that will simply forward the call to virtual member // functions. void IONetworkInterface::powerChangeHandler( void * target, void * param0, void * param1 ) { IONetworkInterface * self = (IONetworkInterface *) target; IONetworkPowerChangeNotice * notice; assert( self ); if (!self->_driver) return; if ( param1 == 0 ) { // Issue a call to this same function synchronized with the // work loop thread. self->_driver->executeCommand( /* client */ self, /* action */ (IONetworkController::Action) &powerChangeHandler, /* target */ self, /* param0 */ param0, /* param1 */ (void *) true ); return; } notice = (IONetworkPowerChangeNotice *) param0; assert( notice ); DLOG("%s: power change flags:%08x state:%d from:%p phase:%d\n", self->getName(), (uint32_t) notice->powerFlags, notice->stateNumber, notice->policyMaker, notice->phase ); if ( notice->phase == kPhasePowerStateWillChange ) { self->controllerWillChangePowerState( self->_driver, notice->powerFlags, notice->stateNumber, notice->policyMaker ); } else if ( notice->phase == kPhasePowerStateDidChange ) { self->controllerDidChangePowerState( self->_driver, notice->powerFlags, notice->stateNumber, notice->policyMaker ); } } //------------------------------------------------------------------------------ // Handle notitifications triggered by controller's power state change. IOReturn IONetworkInterface::powerStateWillChangeTo( IOPMPowerFlags powerFlags, unsigned long stateNumber, IOService * policyMaker ) { IONetworkPowerChangeNotice notice; notice.policyMaker = policyMaker; notice.powerFlags = powerFlags; notice.stateNumber = stateNumber; notice.phase = kPhasePowerStateWillChange; powerChangeHandler( (void *) this, (void *) ¬ice, /* param1: inside gate */ (void *) false ); return kIOPMAckImplied; } IOReturn IONetworkInterface::powerStateDidChangeTo( IOPMPowerFlags powerFlags, unsigned long stateNumber, IOService * policyMaker ) { IONetworkPowerChangeNotice notice; notice.policyMaker = policyMaker; notice.powerFlags = powerFlags; notice.stateNumber = stateNumber; notice.phase = kPhasePowerStateDidChange; powerChangeHandler( (void *) this, (void *) ¬ice, /* param1: inside gate */ (void *) false ); return kIOPMAckImplied; } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::message( UInt32 type, IOService * provider, void * argument ) { if (kMessageControllerWillShutdown == type) { // Handle system shutdown or restarts. Handle this by performing the // same work when driver is transitioning to an unusable power state. DLOG("%s: kMessageControllerWillShutdown\n", getName()); haltOutputThread( kTxThreadStateDetach ); powerStateWillChangeTo( 0, 0, NULL ); return kIOReturnSuccess; } return super::message( type, provider, argument ); } //------------------------------------------------------------------------------ // Termination bool IONetworkInterface::requestTerminate( IOService * provider, IOOptionBits options ) { #if EVALUATE_FOR_LATER_REMOVAL // Early indication that provider started termination if (provider == _driver) { // Stop transmit thread before a later disable by the // driver on work loop context, which can deadlock. DLOG("%s::requestTerminate(%s)\n", getName(), provider->getName()); haltOutputThread( kTxThreadStateDetach ); } #endif return super::requestTerminate(provider, options); } bool IONetworkInterface::willTerminate( IOService * provider, IOOptionBits options ) { DLOG("%s::%s(%s, 0x%x)\n", getName(), __FUNCTION__, provider->getName(), (uint32_t) options); setInterfaceState( kIONetworkInterfaceDisabledState ); return super::willTerminate( provider, options ); } //------------------------------------------------------------------------------ // Inlined functions pulled from header file to ensure // binary compatibility with drivers built with gcc2.95. IONetworkData * IONetworkInterface::getParameter(const char * aKey) const { return getNetworkData(aKey); } bool IONetworkInterface::setExtendedFlags(UInt32 flags, UInt32 clear) { return true; } //------------------------------------------------------------------------------ ifnet_t IONetworkInterface::getIfnet( void ) const { return _backingIfnet; } OSMetaClassDefineReservedUsed(IONetworkInterface, 0); IOReturn IONetworkInterface::attachToDataLinkLayer( IOOptionBits options, void * parameter ) { ifnet_init_params params; ifnet_init_eparams eparams; struct sockaddr_dl * ll_addr = 0; char buffer[2 * sizeof(struct sockaddr_dl)]; errno_t error; OSObject * prop; IOReturn result = kIOReturnInternalError; if (!_driver) { LOG("%s: BSD attach failed, no driver\n", getName()); goto fail; } memset(¶ms, 0, sizeof(params)); if (!initIfnetParams(¶ms)) { LOG("%s: initIfnetParams failed\n", getName()); goto fail; } memset(&eparams, 0, sizeof(eparams)); IOLockLock(_privateLock); _configFlags |= kConfigFrozen; // Pass ifnet_init_params to subclass which is then converted to // the new ifnet_init_eparams. All this to avoid burning another // vtable pad slot, and subclasses don't need to change. eparams.ver = IFNET_INIT_CURRENT_VERSION; eparams.len = sizeof(eparams); eparams.uniqueid = params.uniqueid; eparams.uniqueid_len = params.uniqueid_len; eparams.name = params.name; eparams.unit = params.unit; eparams.family = params.family; eparams.type = params.type; eparams.subfamily = _subType; eparams.demux = params.demux; eparams.add_proto = params.add_proto; eparams.del_proto = params.del_proto; eparams.check_multi = params.check_multi; eparams.framer = params.framer; eparams.softc = params.softc; eparams.ioctl = params.ioctl; eparams.set_bpf_tap = params.set_bpf_tap; eparams.detach = params.detach; eparams.event = params.event; eparams.broadcast_addr = params.broadcast_addr; eparams.broadcast_len = params.broadcast_len; if (!(_configFlags & kConfigRxPoll) && !(_configFlags & kConfigTxPull)) { eparams.flags = IFNET_INIT_LEGACY; } if (_configFlags & kConfigRxPoll) { eparams.flags = IFNET_INIT_INPUT_POLL; eparams.input_ctl = if_input_ctl; eparams.input_poll = (_rxPollOptions & kIONetworkWorkLoopSynchronous) ? if_input_poll_gated : if_input_poll; // cache the driver's inputPacketPoll action _rxPollAction = (void *) OSMemberFunctionCast( IONetworkController::Action, _driver, &IONetworkController::pollInputPackets); _rxCtlAction = (void *) OSMemberFunctionCast( IONetworkController::Action, this, &IONetworkInterface::actionInputCtl); DLOG("%s: supports input polling\n", getName()); } if (_configFlags & kConfigTxPull) { eparams.sndq_maxlen = _txQueueSize; if (_txQueueSize) DLOG("%s: sndq_maxlen = %u\n", getName(), _txQueueSize); eparams.output_sched_model = _txSchedulingModel; if (_txPullOptions & kIONetworkWorkLoopSynchronous) { eparams.start = if_start_gated; // retain work loop for transmitThreadStop() _txWorkLoop = _driver->getWorkLoop(); if (!_txWorkLoop) { IOLockUnlock(_privateLock); goto fail; } _txWorkLoop->retain(); _txStartAction = (void *) OSMemberFunctionCast( IONetworkController::Action, this, &IONetworkInterface::drainOutputQueue); } else { eparams.start = if_start; } if (_configFlags & kConfigPreEnqueue) { eparams.pre_enqueue = if_output_pre_enqueue; } DLOG("%s: supports %stransmit pull, pre_enqueue %d\n", getName(), _txWorkLoop ? "gated " : "", (eparams.pre_enqueue != 0)); } else { eparams.output = params.output; } eparams.output_ctl = if_output_ctl; // u_int64_t output_bw // u_int64_t input_bw IOLockUnlock(_privateLock); error = ifnet_allocate_extended(&eparams, &_backingIfnet); if (error) { LOG("%s: ifnet_allocate_extended error %d\n", getName(), error); goto fail; } error = ifnet_set_offload(_backingIfnet, getIfnetHardwareAssistValue(_driver)); if (error) LOG("%s: ifnet_set_offload error %d\n", getName(), error); prop = _driver->copyProperty(kIOMACAddress); if (prop) { OSData * macAddr = OSDynamicCast(OSData, prop); uint32_t len; memset(buffer, 0, sizeof(buffer)); len = sizeof(buffer) - offsetof(struct sockaddr_dl, sdl_data); if (macAddr && macAddr->getLength() && (macAddr->getLength() <= len)) { len = macAddr->getLength(); ll_addr = (struct sockaddr_dl *) buffer; bcopy(macAddr->getBytesNoCopy(), ll_addr->sdl_data, len); ll_addr->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + len; ll_addr->sdl_family = AF_LINK; ll_addr->sdl_alen = len; } prop->release(); if (!ll_addr) { LOG("%s: BSD attach failed, bad MAC address\n", getName()); goto fail; } } ifnet_set_mtu(_backingIfnet, _mtu); ifnet_set_flags(_backingIfnet, _flags, 0xffff); ifnet_set_addrlen(_backingIfnet, _addrlen); ifnet_set_hdrlen(_backingIfnet, _hdrlen); if (_configFlags & kConfigRxPoll) ifnet_set_rcvq_maxlen(_backingIfnet, _rxRingSize); error = ifnet_attach(_backingIfnet, ll_addr); if (!error) result = kIOReturnSuccess; else LOG("%s: ifnet_attach error %d\n", getName(), error); fail: if ((result != kIOReturnSuccess) && _backingIfnet) { // attach failed, clean up ifnet_release(_backingIfnet); _backingIfnet = NULL; } return result; } //------------------------------------------------------------------------------ OSMetaClassDefineReservedUsed(IONetworkInterface, 1); void IONetworkInterface::detachFromDataLinkLayer( IOOptionBits options, void * parameter ) { // Running on thread call context // Permanently halt the transmit thread before ifnet detach DLOG("%s: detachFromDataLinkLayer\n", getName()); haltOutputThread( kTxThreadStateDetach ); WAITING_FOR_DETACH(this) = true; // this will lead to another thread calling if_detach() ifnet_detach(_backingIfnet); // protect against if_detach() running before we block IOLockLock(_privateLock); while (WAITING_FOR_DETACH(this)) // if false, if_detach is done { IOLockSleep(_privateLock, this, THREAD_UNINT); } IOLockUnlock(_privateLock); } //------------------------------------------------------------------------------ void IONetworkInterface::debuggerRegistered( void ) { char buffer[REMOTE_NMI_PATTERN_LEN + 2]; unsigned int i; if (_remote_NMI_len) return; memset(buffer, 0, sizeof(buffer)); if (!PE_parse_boot_argn(kRemoteNMI, buffer, sizeof(buffer))) return; for (i = 0; i < (REMOTE_NMI_PATTERN_LEN >> 1); i++) { unsigned int val; if (sscanf(buffer + (i << 1), "%02X", &val) != 1) break; buffer[i] = val; } _remote_NMI_pattern = (char *) IOMalloc(sizeof(char) * i + 1); if (!_remote_NMI_pattern) return; _remote_NMI_pattern[i] = '\0'; memcpy(_remote_NMI_pattern, buffer, i); _remote_NMI_len = i; } //------------------------------------------------------------------------------ void IONetworkInterface::reportDataTransferRates( uint64_t outputRateMax, uint64_t inputRateMax, uint64_t outputRateEffective, uint64_t inputRateEffective ) { if_bandwidths_t bw_out, bw_in; if ((_configFlags & kConfigDataRates) == 0) { IOLockLock(_privateLock); _configFlags |= kConfigDataRates; IOLockUnlock(_privateLock); } if (!outputRateEffective) outputRateEffective = outputRateMax; if (!inputRateEffective) inputRateEffective = inputRateMax; bw_out.max_bw = outputRateMax; bw_out.eff_bw = outputRateEffective; bw_in.max_bw = inputRateMax; bw_in.eff_bw = inputRateEffective; if (_backingIfnet) ifnet_set_bandwidths(_backingIfnet, &bw_out, &bw_in); } //------------------------------------------------------------------------------ // Driver-pull output model //------------------------------------------------------------------------------ IOReturn IONetworkInterface::configureOutputPullModel( uint32_t driverQueueSize, IOOptionBits options, uint32_t outputQueueSize, uint32_t outputSchedulingModel ) { IOReturn ret = kIOReturnError; IOLockLock(_privateLock); if ((_configFlags & kConfigFrozen) == 0) { _txRingSize = driverQueueSize; _txPullOptions = options; _txQueueSize = outputQueueSize; _txSchedulingModel = outputSchedulingModel; _configFlags |= kConfigTxPull; ret = kIOReturnSuccess; } IOLockUnlock(_privateLock); return ret; } IOReturn IONetworkInterface::installOutputPreEnqueueHandler( OutputPreEnqueueHandler handler, void * target, void * refCon ) { IOReturn ret = kIOReturnError; if (!handler) return kIOReturnBadArgument; IOLockLock(_privateLock); if ((_configFlags & kConfigFrozen) == 0) { _peqHandler = handler; _peqTarget = target; _peqRefcon = refCon; _configFlags |= kConfigPreEnqueue; ret = kIOReturnSuccess; } IOLockUnlock(_privateLock); return ret; } errno_t IONetworkInterface::if_output_pre_enqueue( ifnet_t ifp, mbuf_t packet ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); errno_t ret; assert(ifp == me->_backingIfnet); assert(me->_peqHandler); ret = me->_peqHandler(me->_peqTarget, me->_peqRefcon, packet); return ret; } int IONetworkInterface::if_start_precheck( ifnet_t ifp ) { int halted = 0; if (__builtin_expect((_txThreadState != 0), 0)) { IOLockLock(_privateLock); if (_txThreadState & kTxThreadStateInit) { // The initial if_start call assert(_txStartThread == 0); _txStartThread = current_thread(); _txThreadState &= ~kTxThreadStateInit; } if (_txThreadState & (kTxThreadStateStop | kTxThreadStateDetach)) { // Disable request or interface detached from DLIL _txThreadState &= ~kTxThreadStateStop; _txThreadState |= kTxThreadStateHalted; thread_wakeup(&_txThreadState); } if (_txThreadState & kTxThreadStateHalted) { halted = true; } IOLockUnlock(_privateLock); if (halted) { // Thread will be halted before we drop our open/retain // on the controller. if (_txThreadState & kTxThreadStatePurge) ifnet_purge(ifp); } } return halted; } void IONetworkInterface::if_start( ifnet_t ifp ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); IONetworkController * driver; assert(ifp == me->_backingIfnet); if (me->if_start_precheck(ifp)) return; driver = me->_driver; assert(driver); if (__builtin_expect(!driver, 0)) { IOLockLock(me->_privateLock); me->_txThreadState |= kTxThreadStateHalted; IOLockUnlock(me->_privateLock); return; } me->drainOutputQueue(ifp, driver); } void IONetworkInterface::if_start_gated( ifnet_t ifp ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); IONetworkController * driver; assert(ifp == me->_backingIfnet); if (me->if_start_precheck(ifp)) return; driver = me->_driver; assert(driver); if (__builtin_expect(!driver, 0)) { IOLockLock(me->_privateLock); me->_txThreadState |= kTxThreadStateHalted; IOLockUnlock(me->_privateLock); return; } driver->executeCommand( /* client */ me, /* action */ (IONetworkController::Action) me->_txStartAction, /* target */ me, /* param0 */ ifp, /* param1 */ driver, /* param2 */ 0, /* param3 */ 0 ); } //------------------------------------------------------------------------------ errno_t IONetworkInterface::enqueueOutputPacket( mbuf_t packet, IOOptionBits options ) { return ifnet_enqueue(_backingIfnet, packet); } //------------------------------------------------------------------------------ void IONetworkInterface::drainOutputQueue( ifnet_t ifp, IONetworkController * driver ) { uint32_t count; IOReturn status; while (true) { // check for queue empty if (ifnet_get_sndq_len(ifp, &count) || !count) break; _txThreadFlags = 0; status = driver->outputStart(this, 0); if (kIOReturnSuccess != status) { if (kIOReturnNoResources == status) { // Try again on next packet enqueue, or when driver // calls outputThreadSignal(). // Retry transmit if preempted by outputThreadSignal() if (OSCompareAndSwap(0, kTxThreadWakeupEnable, &_txThreadFlags)) break; } else { // Driver error, or dequeue failed break; } } } } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::dequeueOutputPackets( uint32_t maxCount, mbuf_t * packetHead, mbuf_t * packetTail, uint32_t * packetCount, uint64_t * packetBytes ) { uint32_t txByteCount, temp, txPackets = 0, txErrors = 0; int delta; errno_t error; if (!maxCount || !packetHead) return kIOReturnBadArgument; if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach)) { goto no_frames; } assert(_backingIfnet); if (maxCount == 1) { error = ifnet_dequeue(_backingIfnet, packetHead); if (!error) { if (packetTail) *packetTail = *packetHead; if (packetCount) *packetCount = 1; txByteCount = mbuf_pkthdr_len(*packetHead); } } else { error = ifnet_dequeue_multi( _backingIfnet, maxCount, packetHead, packetTail, packetCount, &txByteCount); } if (error) goto no_frames; // feed output tap if (_outputFilterFunc) { mbuf_t m, n; m = *packetHead; assert(m); assert((mbuf_flags(m) & MBUF_PKTHDR)); for (n = m; n != 0; n = mbuf_nextpkt(n)) feedPacketOutputTap(n); } if (_txThreadSignal != _txThreadSignalLast) { // Update the stats that the driver maintains temp = _driverStats.outputErrors; delta = temp - _lastDriverStats.outputErrors; if (delta) { txErrors = ABS(delta); _lastDriverStats.outputErrors = temp; } temp = _driverStats.outputPackets; delta = temp - _lastDriverStats.outputPackets; txPackets = ABS(delta); _lastDriverStats.outputPackets = temp; _txThreadSignalLast = _txThreadSignal; } // update interface output byte count ifnet_stat_increment_out(_backingIfnet, txPackets, txByteCount, txErrors); if (packetBytes) *packetBytes = txByteCount; return kIOReturnSuccess; no_frames: *packetHead = 0; if (packetTail) packetTail = 0; if (packetCount) packetCount = 0; if (packetBytes) packetBytes = 0; return kIOReturnNoFrames; } IOReturn IONetworkInterface::dequeueOutputPacketsWithServiceClass( uint32_t maxCount, IOMbufServiceClass serviceClass, mbuf_t * packetHead, mbuf_t * packetTail, uint32_t * packetCount, uint64_t * packetBytes ) { uint32_t txByteCount, temp, txPackets = 0, txErrors = 0; int delta; mbuf_svc_class_t mbufSC; errno_t error; if (!maxCount || !packetHead) return kIOReturnBadArgument; if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach)) { goto no_frames; } assert(_backingIfnet); // convert from I/O Kit SC to mbuf SC switch (serviceClass) { case kIOMbufServiceClassBE: mbufSC = MBUF_SC_BE; break; case kIOMbufServiceClassBKSYS: mbufSC = MBUF_SC_BK_SYS; break; case kIOMbufServiceClassBK: mbufSC = MBUF_SC_BK; break; case kIOMbufServiceClassRD: mbufSC = MBUF_SC_RD; break; case kIOMbufServiceClassOAM: mbufSC = MBUF_SC_OAM; break; case kIOMbufServiceClassAV: mbufSC = MBUF_SC_AV; break; case kIOMbufServiceClassRV: mbufSC = MBUF_SC_RV; break; case kIOMbufServiceClassVI: mbufSC = MBUF_SC_VI; break; case kIOMbufServiceClassVO: mbufSC = MBUF_SC_VO; break; case kIOMbufServiceClassCTL: mbufSC = MBUF_SC_CTL; break; default: return kIOReturnBadArgument; } if (maxCount == 1) { error = ifnet_dequeue_service_class( _backingIfnet, mbufSC, packetHead); if (!error) { if (packetTail) *packetTail = *packetHead; if (packetCount) *packetCount = 1; txByteCount = mbuf_pkthdr_len(*packetHead); } } else { error = ifnet_dequeue_service_class_multi( _backingIfnet, mbufSC, maxCount, packetHead, packetTail, packetCount, &txByteCount); } if (error) goto no_frames; // feed output tap if (_outputFilterFunc) { mbuf_t m, n; m = *packetHead; assert(m); assert((mbuf_flags(m) & MBUF_PKTHDR)); for (n = m; n != 0; n = mbuf_nextpkt(n)) feedPacketOutputTap(n); } if (_txThreadSignal != _txThreadSignalLast) { // Update the stats that the driver maintains temp = _driverStats.outputErrors; delta = temp - _lastDriverStats.outputErrors; if (delta) { txErrors = ABS(delta); _lastDriverStats.outputErrors = temp; } temp = _driverStats.outputPackets; delta = temp - _lastDriverStats.outputPackets; txPackets = ABS(delta); _lastDriverStats.outputPackets = temp; _txThreadSignalLast = _txThreadSignal; } // update interface output byte count ifnet_stat_increment_out(_backingIfnet, txPackets, txByteCount, txErrors); if (packetBytes) *packetBytes = txByteCount; return kIOReturnSuccess; no_frames: *packetHead = 0; if (packetTail) packetTail = 0; if (packetCount) packetCount = 0; if (packetBytes) packetBytes = 0; return kIOReturnNoFrames; } //------------------------------------------------------------------------------ void IONetworkInterface::signalOutputThread( IOOptionBits options ) { // Unconditionally signal completion, to trigger drain loop retry UInt32 old = OSBitOrAtomic(kTxThreadWakeupSignal, &_txThreadFlags); // Interface detached from network stack if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach)) { return; } // Only wake if_start thread if drain loop left wakeup enabled if ((old & kTxThreadWakeupMask) == kTxThreadWakeupEnable) { assert(_backingIfnet); ifnet_start(_backingIfnet); } _txThreadSignal++; } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::startOutputThread( IOOptionBits options ) { const uint32_t mask = (kTxThreadStateHalted | kTxThreadStateStop); IOReturn error = kIOReturnUnsupported; bool purge = false; DLOG("%s: %s(0x%x)\n", getName(), __FUNCTION__, _txThreadState); IOLockLock(_privateLock); if (_txThreadState & kTxThreadStateDetach) { error = kIOReturnNotAttached; } else { error = kIOReturnSuccess; purge = ((_txThreadState & kTxThreadStatePurge) != 0); _txThreadState &= ~kTxThreadStatePurge; if (_txThreadState & mask) { _txThreadState &= ~mask; // No need to kick if_start thread if it hasn't called us yet. // This safety check covers the time before ifnet_attach. if ((_txThreadState & kTxThreadStateInit) == 0) { assert(_backingIfnet); if (purge) ifnet_purge(_backingIfnet); ifnet_start(_backingIfnet); } } } IOLockUnlock(_privateLock); return error; } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::haltOutputThread( uint32_t stateBit ) { AbsoluteTime deadline; uint32_t count = 0; const uint32_t timeout = 100; IOReturn error = kIOReturnSuccess; DLOG("%s: %s(0x%x, 0x%x)\n", getName(), __FUNCTION__, _txThreadState, stateBit); IOLockLock(_privateLock); do { // Prevent queue enable while we drop the lock _txThreadState |= (stateBit & kTxThreadStateDetach); _txThreadState &= ~kTxThreadStatePurge; // Already halted, thread may have terminated if (_txThreadState & kTxThreadStateHalted) break; if (!_txStartThread) { // Before the initial if_start call. Can update state directly // since this thread holds the lock that blocks if_start. _txThreadState |= kTxThreadStateHalted; break; } // if_start thread cannot call us without ifnet attach assert(_backingIfnet); if (current_thread() == _txStartThread) { // Driver called stop from if_start context _txThreadState |= kTxThreadStateHalted; break; } if (_txWorkLoop && _txWorkLoop->inGate()) { // stopped from gated context _txThreadState |= kTxThreadStateHalted; break; } // Wait for halt confirmation from if_start thread while ((_txThreadState & kTxThreadStateHalted) == 0) { if (count) { LOG("%s: %s(0x%x, 0x%x) retry %u\n", getName(), __FUNCTION__, _txThreadState, stateBit, count); if (count >= timeout) { error = kIOReturnTimeout; break; } } _txThreadState |= stateBit; clock_interval_to_deadline(100, kMillisecondScale, &deadline); ifnet_start(_backingIfnet); IOLockSleepDeadline(_privateLock, &_txThreadState, deadline, THREAD_UNINT); count++; } } while (false); IOLockUnlock(_privateLock); DLOG("%s: %s(0x%x, 0x%x) done after %u try\n", getName(), __FUNCTION__, _txThreadState, stateBit, count); return error; } IOReturn IONetworkInterface::stopOutputThread( IOOptionBits options ) { return haltOutputThread( kTxThreadStateStop ); } //------------------------------------------------------------------------------ void IONetworkInterface::flushOutputQueue( IOOptionBits options ) { DLOG("%s: %s(0x%x)\n", getName(), __FUNCTION__, _txThreadState); IOLockLock(_privateLock); // synchronized with interface detach if (_backingIfnet && ((_txThreadState & kTxThreadStateDetach) == 0)) { // Drop new packets if output thread stopped if (_txThreadState & kTxThreadStateHalted) _txThreadState |= kTxThreadStatePurge; ifnet_purge(_backingIfnet); } IOLockUnlock(_privateLock); } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::reportTransmitCompletionStatus( mbuf_t packet, IOReturn status, uint32_t param1 __unused, uint32_t param2 __unused, IOOptionBits options __unused ) { errno_t error = ifnet_tx_compl_status(getIfnet(), packet, status); return (error) ? kIOReturnError : kIOReturnSuccess; } //------------------------------------------------------------------------------ // Stack-poll input model //------------------------------------------------------------------------------ IOReturn IONetworkInterface::configureInputPacketPolling( uint32_t driverQueueSize, IOOptionBits options ) { IOReturn ret = kIOReturnError; IOLockLock(_privateLock); if ((_configFlags & kConfigFrozen) == 0) { _rxRingSize = driverQueueSize; _rxPollOptions = options; _configFlags |= kConfigRxPoll; ret = kIOReturnSuccess; } IOLockUnlock(_privateLock); return ret; } void IONetworkInterface::if_input_poll( ifnet_t ifp, uint32_t flags, uint32_t max_count, mbuf_t * first_packet, mbuf_t * last_packet, uint32_t * cnt, uint32_t * len ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); IONetworkController * driver; IOMbufQueue queue; assert(ifp == me->_backingIfnet); assert(max_count != 0); driver = me->_driver; assert(driver); if (__builtin_expect(!driver, 0)) { return; } me->_rxPollTotal++; IOMbufQueueInit(&queue); driver->pollInputPackets(me, max_count, &queue, 0); if (!IOMbufQueueIsEmpty(&queue)) { *first_packet = queue.head; *last_packet = queue.tail; *cnt = queue.count; *len = queue.bytes; } else { me->_rxPollEmpty++; *first_packet = 0; *last_packet = 0; *cnt = 0; *len = 0; } } void IONetworkInterface::if_input_poll_gated( ifnet_t ifp, uint32_t flags, uint32_t max_count, mbuf_t * first_packet, mbuf_t * last_packet, uint32_t * cnt, uint32_t * len ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); IONetworkController * driver; IOMbufQueue queue; assert(ifp == me->_backingIfnet); assert(max_count != 0); driver = me->_driver; assert(driver); if (__builtin_expect(!driver, 0)) { return; } me->_rxPollTotal++; IOMbufQueueInit(&queue); driver->executeCommand( /* client */ me, /* action */ (IONetworkController::Action) me->_rxPollAction, /* target */ driver, /* param0 */ me, /* param1 */ (void *)(uintptr_t) max_count, /* param2 */ &queue, /* param3 */ 0 ); if (!IOMbufQueueIsEmpty(&queue)) { *first_packet = queue.head; *last_packet = queue.tail; *cnt = queue.count; *len = queue.bytes; } else { me->_rxPollEmpty++; *first_packet = 0; *last_packet = 0; *cnt = 0; *len = 0; } } IOReturn IONetworkInterface::setPacketPollingParameters( const IONetworkPacketPollingParameters * params, IOOptionBits options ) { struct ifnet_poll_params ifParams; errno_t error; if (!params) return kIOReturnBadArgument; if (!_backingIfnet) return kIOReturnNotAttached; bzero(&ifParams, sizeof(ifParams)); ifParams.flags = options; ifParams.packets_limit = params->maxPacketCount; ifParams.packets_lowat = params->lowThresholdPackets; ifParams.packets_hiwat = params->highThresholdPackets; ifParams.bytes_lowat = params->lowThresholdBytes; ifParams.bytes_hiwat = params->highThresholdBytes; ifParams.interval_time = params->pollIntervalTime; error = ifnet_set_poll_params(_backingIfnet, &ifParams); return errnoToIOReturn(error); } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::enqueueInputPacket( mbuf_t packet, IOMbufQueue * queue, IOOptionBits options ) { const uint32_t hdrlen = _hdrlen; void * mdata; uint32_t length; assert(packet); assert(_backingIfnet); if (!packet) return kIOReturnBadArgument; if (!_backingIfnet) { mbuf_freem(packet); return kIOReturnNotAttached; } if (!queue) { // use the push model queue queue = _inputPushQueue; } assert((mbuf_flags(packet) & MBUF_PKTHDR)); assert((mbuf_nextpkt(packet) == 0)); mbuf_pkthdr_setrcvif(packet, _backingIfnet); length = mbuf_pkthdr_len(packet); assert(length != 0); // check for special debugger packet if (_remote_NMI_len) { const char *data = get_icmp_data(&packet, hdrlen, _remote_NMI_len); if (data && (memcmp(data, _remote_NMI_pattern, _remote_NMI_len) == 0)) { IOKernelDebugger::signalDebugger(); } } // input BPF tap if (_inputFilterFunc) feedPacketInputTap(packet); // frame header at start of mbuf data mdata = mbuf_data(packet); mbuf_pkthdr_setheader(packet, mdata); // packet header length does not include the frame header mbuf_pkthdr_setlen(packet, length - hdrlen); // adjust the mbuf data and length to exclude the frame header mbuf_setdata(packet, (char *)mdata + hdrlen, mbuf_len(packet) - hdrlen); IOMbufQueueTailAdd(queue, packet, length); return kIOReturnSuccess; } //------------------------------------------------------------------------------ errno_t IONetworkInterface::if_input_ctl( ifnet_t ifp, ifnet_ctl_cmd_t cmd, u_int32_t arglen, void * arg ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); IONetworkController * driver; assert(ifp == me->_backingIfnet); driver = me->_driver; assert(driver); if (!driver) { return 0; } driver->executeCommand( /* client */ me, /* action */ (IONetworkController::Action) me->_rxCtlAction, /* target */ me, /* param0 */ driver, /* param1 */ (void *)(uintptr_t) cmd, /* param2 */ (void *)(uintptr_t) arglen, /* param3 */ arg ); return 0; } void IONetworkInterface::actionInputCtl( IONetworkController * driver, ifnet_ctl_cmd_t cmd, uint32_t arglen, void * arg ) { ifnet_model_params * params = (ifnet_model_params *) arg; switch (cmd) { case IFNET_CTL_SET_INPUT_MODEL: if (arglen != sizeof(ifnet_model_params)) { LOG("%s: SET_INPUT_MODEL bad params size %u != %u\n", getName(), arglen, (uint32_t) sizeof(ifnet_model_params)); } else if ((params->model != IFNET_MODEL_INPUT_POLL_OFF) && (params->model != IFNET_MODEL_INPUT_POLL_ON)) { LOG("%s: SET_INPUT_MODEL unknown model 0x%x\n", getName(), params->model); } else if (params->model != _rxPollModel) { // IFNET_MODEL_INPUT_POLL_OFF // IFNET_MODEL_INPUT_POLL_ON _rxPollModel = params->model; DLOG("%s: SET_INPUT_MODEL 0x%x\n", getName(), _rxPollModel); DLOG("%s: poll cnt %llu, empty %llu\n", getName(), _rxPollTotal, _rxPollEmpty); _rxPollEmpty = 0; _rxPollTotal = 0; driver->setInputPacketPollingEnable( this, _rxPollModel == IFNET_MODEL_INPUT_POLL_ON ); } break; case IFNET_CTL_GET_INPUT_MODEL: if (arglen != sizeof(ifnet_model_params)) { LOG("%s: GET_INPUT_MODEL bad params size %u != %u\n", getName(), arglen, (uint32_t) sizeof(ifnet_model_params)); } else { params->model = _rxPollModel; } break; default: DLOG("%s: if_input_ctl unknown cmd 0x%x\n", getName(), cmd); } } //------------------------------------------------------------------------------ errno_t IONetworkInterface::if_output_ctl( ifnet_t ifp, ifnet_ctl_cmd_t cmd, u_int32_t arglen, void * arg ) { IONetworkInterface * me = IFNET_TO_THIS(ifp); IONetworkController * driver; errno_t error = ENOTSUP; bool changed = false; assert(ifp == me->_backingIfnet); driver = me->_driver; assert(driver); if (!driver) { return ENODEV; } if (cmd == IFNET_CTL_SET_LOG) { if (arg && (arglen == sizeof(struct ifnet_log_params))) { const struct ifnet_log_params * lp = (const struct ifnet_log_params *) arg; IONetworkInterfaceLoggingParameters params; IOLockLock(me->_privateLock); if (me->_loggingLevel != lp->level) changed = true; me->_loggingLevel = lp->level; IOLockUnlock(me->_privateLock); error = 0; bzero(¶ms, sizeof(params)); params.level = lp->level; params.flags = lp->flags; params.category = lp->category; params.subCategory = lp->subcategory; me->notifyDriver(kIONetworkNotificationLoggingParametersChange, ¶ms); } else { error = EINVAL; } if (changed) me->notifyDriver(kIONetworkNotificationLoggingLevelChange, 0); } else if (cmd == IFNET_CTL_NOTIFY_ADDRESS) { if (arg && (arglen == sizeof(struct ifnet_notify_address_params))) { const struct ifnet_notify_address_params * ap = (const struct ifnet_notify_address_params *) arg; IONetworkInterfaceAddressChangeParameters params; bzero(¶ms, sizeof(params)); params.addressFamily = ap->address_family; me->notifyDriver(kIONetworkNotificationInterfaceAddressChange, ¶ms); } else { error = EINVAL; } } return error; } //------------------------------------------------------------------------------ bool IONetworkInterface::isBPFTapEnabled( IOOptionBits options __unused ) const { bool enabled; IOLockLock(_privateLock); enabled = (_inputFilterFunc != 0); IOLockUnlock(_privateLock); return enabled; } int32_t IONetworkInterface::getLoggingLevel( IOOptionBits options __unused ) const { int32_t level; IOLockLock(_privateLock); level = _loggingLevel; IOLockUnlock(_privateLock); return level; } void IONetworkInterface::notifyDriver( uint32_t notifyType, void * data ) { if (_driver) _driver->networkInterfaceNotification(this, notifyType, data); } //------------------------------------------------------------------------------ IOReturn IONetworkInterface::errnoToIOReturn( errno_t error ) { switch (error) { case 0: return kIOReturnSuccess; case EINVAL: return kIOReturnBadArgument; case ENOMEM: return kIOReturnNoMemory; case EPERM: return kIOReturnNotPermitted; case EACCES: return kIOReturnNotPrivileged; case ENOTSUP: return kIOReturnUnsupported; case EBUSY: return kIOReturnBusy; case ETIMEDOUT: return kIOReturnTimeout; default: return kIOReturnError; } } IOReturn IONetworkInterface::reportDatapathIssue( IOReturn issue, void * data __unused, IOByteCount length __unused ) { errno_t error; // SHA-1 hash of com.apple.iokit.IONetworkingFamily static const uint8_t modid[DLIL_MODIDLEN] = { 0xe3, 0x26, 0x1e, 0x7d, 0xf2, 0x15, 0xf1, 0x3d, 0x9d, 0x9d, 0xbc, 0x1f, 0x75, 0x4d, 0xa5, 0xa4, 0x1c, 0xac, 0x2a, 0xab }; union { uint8_t errorData[ DLIL_MODARGLEN ]; IOReturn errorCode; } issueInfo; bzero(&issueInfo, sizeof(issueInfo)); issueInfo.errorCode = issue; error = ifnet_report_issues(getIfnet(), (uint8_t *) modid, issueInfo.errorData); return errnoToIOReturn(error); }