/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * IOEthernetInterface.cpp * * HISTORY * 8-Jan-1999 Joe Liu (jliu) created. * */ #include #include #include #include #include #include "IONetworkUserClient.h" #include // publishFeature() extern "C" { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include } //--------------------------------------------------------------------------- #define super IONetworkInterface OSDefineMetaClassAndStructors( IOEthernetInterface, IONetworkInterface ) OSMetaClassDefineReservedUnused( IOEthernetInterface, 0); OSMetaClassDefineReservedUnused( IOEthernetInterface, 1); OSMetaClassDefineReservedUnused( IOEthernetInterface, 2); OSMetaClassDefineReservedUnused( IOEthernetInterface, 3); OSMetaClassDefineReservedUnused( IOEthernetInterface, 4); OSMetaClassDefineReservedUnused( IOEthernetInterface, 5); OSMetaClassDefineReservedUnused( IOEthernetInterface, 6); OSMetaClassDefineReservedUnused( IOEthernetInterface, 7); OSMetaClassDefineReservedUnused( IOEthernetInterface, 8); OSMetaClassDefineReservedUnused( IOEthernetInterface, 9); OSMetaClassDefineReservedUnused( IOEthernetInterface, 10); OSMetaClassDefineReservedUnused( IOEthernetInterface, 11); OSMetaClassDefineReservedUnused( IOEthernetInterface, 12); OSMetaClassDefineReservedUnused( IOEthernetInterface, 13); OSMetaClassDefineReservedUnused( IOEthernetInterface, 14); OSMetaClassDefineReservedUnused( IOEthernetInterface, 15); // The name prefix for all BSD Ethernet interfaces. // #define kIOEthernetInterfaceNamePrefix "en" // Options used for enableFilter(), disableFilter(). enum { kFilterOptionDeferIO = 0x0001, kFilterOptionNotInsideGate = 0x0002, kFilterOptionNoStateChange = 0x0004, kFilterOptionDisableZeroBits = 0x0008, kFilterOptionSyncPendingIO = 0x0010 }; //--------------------------------------------------------------------------- // Macros #define _altMTU _reserved->altMTU #ifdef DEBUG #define DLOG(fmt, args...) IOLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif UInt32 IOEthernetInterface::getFilters(const OSDictionary * dict, const OSSymbol * group) { OSNumber * num; UInt32 filters = 0; assert( dict && group ); if (( num = (OSNumber *) dict->getObject(group) )) { filters = num->unsigned32BitValue(); } return filters; } bool IOEthernetInterface::setFilters(OSDictionary * dict, const OSSymbol * group, UInt32 filters) { OSNumber * num; bool ret = false; assert( dict && group ); num = (OSNumber *) dict->getObject(group); if ( num == 0 ) { if (( num = OSNumber::withNumber(filters, 32) )) { ret = dict->setObject(group, num); num->release(); } } else { num->setValue(filters); ret = true; } return ret; } #define GET_REQUIRED_FILTERS(g) getFilters(_requiredFilters, (g)) #define GET_ACTIVE_FILTERS(g) getFilters(_activeFilters, (g)) #define GET_SUPPORTED_FILTERS(g) getFilters(_supportedFilters, (g)) #define SET_REQUIRED_FILTERS(g, v) setFilters(_requiredFilters, (g), (v)) #define SET_ACTIVE_FILTERS(g, v) setFilters(_activeFilters, (g), (v)) //--------------------------------------------------------------------------- // Initialize an IOEthernetInterface instance. Instance variables are // initialized bool IOEthernetInterface::init(IONetworkController * controller) { _reserved = IONew( ExpansionData, 1 ); if( _reserved == 0 ) return false; memset(_reserved, 0, sizeof(ExpansionData)); if ( super::init(controller) == false ) return false; // initialize enet specific fields. setInterfaceType(IFT_ETHER); setMaxTransferUnit( ETHERMTU ); setMediaAddressLength( ETHER_ADDR_LEN ); setMediaHeaderLength( ETHER_HDR_LEN ); setFlags( IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS, IFF_RUNNING | IFF_MULTICAST ); // Add an IONetworkData with room to hold an IOEthernetStats structure. // This class does not reference the data object created, and no harm // is done if the data object is released or replaced. IONetworkData * data = IONetworkData::withInternalBuffer( kIOEthernetStatsKey, sizeof(IOEthernetStats)); if (data) { addNetworkData(data); data->release(); } // Create and initialize the filter dictionaries. _requiredFilters = OSDictionary::withCapacity(4); _activeFilters = OSDictionary::withCapacity(4); if ( (_requiredFilters == 0) || (_activeFilters == 0) ) return false; _supportedFilters = OSDynamicCast(OSDictionary, controller->getProperty(kIOPacketFilters)); if ( _supportedFilters == 0 ) return false; _supportedFilters->retain(); // Controller's Unicast (directed) and Broadcast filters should always // be enabled. Those bits should never be cleared. if ( !SET_REQUIRED_FILTERS( gIONetworkFilterGroup, kIOPacketFilterUnicast | kIOPacketFilterBroadcast ) || !SET_REQUIRED_FILTERS( gIOEthernetWakeOnLANFilterGroup, 0 ) || !SET_ACTIVE_FILTERS( gIONetworkFilterGroup, 0 ) || !SET_ACTIVE_FILTERS( gIOEthernetWakeOnLANFilterGroup, 0 ) ) { return false; } // Publish filter dictionaries to property table. setProperty( kIORequiredPacketFilters, _requiredFilters ); setProperty( kIOActivePacketFilters, _activeFilters ); return true; } static const u_char ether_broadcast_addr[ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; bool IOEthernetInterface::initIfnetParams(struct ifnet_init_params *params) { OSData *uniqueID; //get the default values super::initIfnetParams( params ); uniqueID = OSDynamicCast(OSData, getProvider()->getProperty(kIOMACAddress)); if ( (uniqueID == 0) || (uniqueID->getLength() != ETHER_ADDR_LEN) ) { DLOG("%s: kIOMACAddress property access error (len %d)\n", getName(), uniqueID ? uniqueID->getLength() : 0); return false; } // fill in ethernet specific values params->uniqueid = uniqueID->getBytesNoCopy(); params->uniqueid_len = uniqueID->getLength(); params->family = APPLE_IF_FAM_ETHERNET; params->demux = ether_demux; params->add_proto = ether_add_proto; params->del_proto = ether_del_proto; params->framer = ether_frameout; params->check_multi = ether_check_multi; params->broadcast_addr = ether_broadcast_addr; params->broadcast_len = sizeof(ether_broadcast_addr); return true; } //--------------------------------------------------------------------------- // Free the IOEthernetInterface instance. void IOEthernetInterface::free() { if ( _requiredFilters ) { _requiredFilters->release(); _requiredFilters = 0; } if ( _activeFilters ) { _activeFilters->release(); _activeFilters = 0; } if ( _supportedFilters ) { _supportedFilters->release(); _supportedFilters = 0; } if ( _reserved ) { IODelete( _reserved, ExpansionData, 1 ); _reserved = 0; } super::free(); } //--------------------------------------------------------------------------- // The name of the interface advertised to the network layer // is generated by concatenating the string returned by this method, // and an unit number. // // Returns a pointer to a constant string "en". Thus Ethernet interfaces // will be registered as en0, en1, etc. const char * IOEthernetInterface::getNamePrefix() const { return kIOEthernetInterfaceNamePrefix; } //--------------------------------------------------------------------------- // Prepare the 'Ethernet' controller after it has been opened. This is called // by IONetworkInterface after a controller has accepted an open from this // interface. IOEthernetInterface uses this method to inspect the controller, // and to cache certain controller properties, such as its hardware address. // This method is called with the arbitration lock held. // // controller: The controller object that was opened. // // Returns true on success, false otherwise // (which will cause the controller to be closed). bool IOEthernetInterface::controllerDidOpen(IONetworkController * ctr) { bool ret = false; OSData * addrData; IOEthernetAddress * addr; do { // Call the controllerDidOpen() in superclass first. if ( (ctr == 0) || (super::controllerDidOpen(ctr) == false) ) break; // If the controller supports some form of multicast filtering, // then set the ifnet IFF_MULTICAST flag. if ( GET_SUPPORTED_FILTERS(gIONetworkFilterGroup) & (kIOPacketFilterMulticast | kIOPacketFilterMulticastAll) ) { setFlags(IFF_MULTICAST); } // Advertise Wake on Magic Packet feature if supported. if ( GET_SUPPORTED_FILTERS( gIOEthernetWakeOnLANFilterGroup ) & kIOEthernetWakeOnMagicPacket ) { IOPMrootDomain * root = getPMRootDomain(); if ( root ) root->publishFeature( "WakeOnMagicPacket" ); } // Get the controller's MAC/Ethernet address. addrData = OSDynamicCast(OSData, ctr->getProperty(kIOMACAddress)); if ( (addrData == 0) || (addrData->getLength() != ETHER_ADDR_LEN) ) { DLOG("%s: kIOMACAddress property access error (len %d)\n", getName(), addrData ? addrData->getLength() : 0); break; } addr = (IOEthernetAddress *) addrData->getBytesNoCopy(); #if 1 // Print the address IOLog("%s: Ethernet address %02x:%02x:%02x:%02x:%02x:%02x\n", ctr->getName(), addr->bytes[0], addr->bytes[1], addr->bytes[2], addr->bytes[3], addr->bytes[4], addr->bytes[5]); #endif ret = true; } while (0); return ret; } //--------------------------------------------------------------------------- // When a close from our last client is received, the interface will // close the controller. But before the controller is closed, this method // will be called by our superclass to perform any final cleanup. This // method is called with the arbitration lock held. // // IOEthernetInterface will ensure that the controller is disabled. // // controller: The currently opened controller object. void IOEthernetInterface::controllerWillClose(IONetworkController * ctr) { super::controllerWillClose(ctr); } //--------------------------------------------------------------------------- // Handle ioctl commands originated from the network layer. // Commands not handled by this method are passed to our superclass. // // Argument convention is: // // arg0 - (struct ifnet *) // arg1 - (void *) // // The commands handled by IOEthernetInterface are: // // SIOCSIFADDR // SIOCSIFFLAGS // SIOCADDMULTI // SIOCDELMULTI // // Returns an error code defined in errno.h (BSD). SInt32 IOEthernetInterface::performCommand( IONetworkController * ctr, UInt32 cmd, void * arg0, void * arg1 ) { SInt32 ret; assert( arg0 == _arpcom ); if ( ctr == 0 ) return EINVAL; switch ( cmd ) { case SIOCSIFFLAGS: case SIOCADDMULTI: case SIOCDELMULTI: case SIOCSIFADDR: case SIOCSIFMTU: case SIOCSIFDEVMTU: case SIOCGIFDEVMTU: case SIOCSIFLLADDR: ret = (int) ctr->executeCommand( this, /* client */ (IONetworkController::Action) &IOEthernetInterface::performGatedCommand, this, /* target */ ctr, /* param0 */ (void *) cmd, /* param1 */ arg0, /* param2 */ arg1 ); /* param3 */ break; default: // Unknown command, let our superclass deal with it. ret = super::performCommand(ctr, cmd, arg0, arg1); break; } return ret; } //--------------------------------------------------------------------------- // Handle an ioctl command on the controller's workloop context. int IOEthernetInterface::performGatedCommand(void * target, void * arg1_ctr, void * arg2_cmd, void * arg3_0, void * arg4_1) { IOEthernetInterface * self = (IOEthernetInterface *) target; IONetworkController * ctr = (IONetworkController *) arg1_ctr; struct ifreq * ifr = (struct ifreq *) arg4_1; SInt ret = EOPNOTSUPP; // Refuse to perform controller I/O if the controller is in a // low-power state that makes it "unusable". if ( self->_controllerLostPower || ( self->getInterfaceState() & kIONetworkInterfaceDisabledState ) ) return EPWROFF; switch ( (UInt32) arg2_cmd ) { case SIOCSIFADDR: ret = self->syncSIOCSIFADDR(ctr); break; case SIOCSIFFLAGS: ret = self->syncSIOCSIFFLAGS(ctr); break; case SIOCADDMULTI: ret = self->syncSIOCADDMULTI(ctr); break; case SIOCDELMULTI: ret = self->syncSIOCDELMULTI(ctr); break; case SIOCSIFMTU: ret = self->syncSIOCSIFMTU( ctr, ifr, 0 ); break; case SIOCSIFDEVMTU: ret = self->syncSIOCSIFMTU(ctr, ifr, 1); break; case SIOCGIFDEVMTU: ret = self->syncSIOCGIFDEVMTU(ctr, ifr); break; case SIOCSIFLLADDR: ret = self->syncSIOCSIFLLADDR( ctr, ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len ); break; } return ret; } //--------------------------------------------------------------------------- // enableController() is reponsible for calling the controller's enable() // method and restoring the state of the controller. We assume that // controllers can completely reset its state upon receiving a disable() // method call. And when it is brought back up, the interface should // assist in restoring the previous state of the Ethernet controller. IOReturn IOEthernetInterface::enableController(IONetworkController * ctr) { IOReturn ret = kIOReturnSuccess; bool enabled = false; assert(ctr); do { // Is controller already enabled? If so, exit and return success. if ( _ctrEnabled ) break; // Send the controller an enable command. if ( (ret = ctr->doEnable(this)) != kIOReturnSuccess ) break; // unable to enable the controller. enabled = true; // Disable all Wake-On-LAN filters. disableFilter(ctr, gIOEthernetWakeOnLANFilterGroup, ~0, kFilterOptionNoStateChange); // Restore current filter selection. SET_ACTIVE_FILTERS(gIONetworkFilterGroup, 0); ret = enableFilter(ctr, gIONetworkFilterGroup, 0, kFilterOptionSyncPendingIO); if ( ret != kIOReturnSuccess ) break; // Restore multicast filter settings. syncSIOCADDMULTI(ctr); // Re-apply the user supplied link-level address. OSData * lladdr = OSDynamicCast(OSData, getProperty(kIOMACAddress)); if ( lladdr && lladdr->getLength() == ETHER_ADDR_LEN ) { ctr->setHardwareAddress( lladdr->getBytesNoCopy(), lladdr->getLength() ); } _ctrEnabled = true; } while (false); // Disable the controller if a serious error has occurred after the // controller has been enabled. if ( enabled && (ret != kIOReturnSuccess) ) { ctr->doDisable(this); } return ret; } //--------------------------------------------------------------------------- // Handles SIOCSIFFLAGS ioctl command for Ethernet interfaces. The network // stack has changed the if_flags field in ifnet. Our job is to go // through if_flags and see what has changed, and act accordingly. // // The fact that if_flags contains both generic and Ethernet specific bits // means that we cannot move some of the default flag processing to the // superclass. int IOEthernetInterface::syncSIOCSIFFLAGS(IONetworkController * ctr) { UInt16 flags = getFlags(); IOReturn ret = kIOReturnSuccess; if ( ( ((flags & IFF_UP) == 0) || _controllerLostPower ) && ( flags & IFF_RUNNING ) ) { // If interface is marked down and it is currently running, // then stop it. ctr->doDisable(this); flags &= ~IFF_RUNNING; _ctrEnabled = false; } else if ( ( flags & IFF_UP ) && ( _controllerLostPower == false ) && ((flags & IFF_RUNNING) == 0) ) { // If interface is marked up and it is currently stopped, // then start it. if ( (ret = enableController(ctr)) == kIOReturnSuccess ) flags |= IFF_RUNNING; } if ( flags & IFF_RUNNING ) { IOReturn rc; // We don't expect multiple flags to be changed for a given // SIOCSIFFLAGS call. // Promiscuous mode rc = (flags & IFF_PROMISC) ? enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterPromiscuous) : disableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterPromiscuous); if (ret == kIOReturnSuccess) ret = rc; // Multicast-All mode rc = (flags & IFF_ALLMULTI) ? enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticastAll) : disableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticastAll); if (ret == kIOReturnSuccess) ret = rc; } // Update the flags field to pick up any modifications. Also update the // property table to reflect any flag changes. setFlags(flags, ~flags); return errnoFromReturn(ret); } //--------------------------------------------------------------------------- // Handles SIOCSIFADDR ioctl. SInt IOEthernetInterface::syncSIOCSIFADDR(IONetworkController * ctr) { IOReturn ret = kIOReturnSuccess; // Interface is implicitly brought up by an SIOCSIFADDR ioctl. setFlags(IFF_UP); if ( (getFlags() & IFF_RUNNING) == 0 ) { if ( (ret = enableController(ctr)) == kIOReturnSuccess ) setFlags(IFF_RUNNING); } return errnoFromReturn(ret); } //--------------------------------------------------------------------------- // Handle SIOCADDMULTI ioctl command. SInt IOEthernetInterface::syncSIOCADDMULTI(IONetworkController * ctr) { IOReturn ret; // Make sure multicast filter is active. ret = enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticast); if ( ret == kIOReturnSuccess ) { // Load multicast addresses only if the filter was activated. ret = setupMulticastFilter(ctr); // If the list is now empty, then deactivate the multicast filter. if ( _mcAddrCount == 0 ) { IOReturn dret = disableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticast); if (ret == kIOReturnSuccess) ret = dret; } } return errnoFromReturn(ret); } //--------------------------------------------------------------------------- // Handle SIOCDELMULTI ioctl command. SInt IOEthernetInterface::syncSIOCDELMULTI(IONetworkController * ctr) { return syncSIOCADDMULTI(ctr); } #define MTU_TO_FRAMESIZE(x) \ ((x) + kIOEthernetCRCSize + sizeof(struct ether_header)) #define FRAMESIZE_TO_MTU(x) \ ((x) - kIOEthernetCRCSize - sizeof(struct ether_header)) int IOEthernetInterface::syncSIOCGIFDEVMTU( IONetworkController * ctr, struct ifreq * ifr ) { IOReturn ret; UInt32 size; ifr->ifr_devmtu.ifdm_current = max(_altMTU, getMaxTransferUnit()); ret = ctr->getMaxPacketSize( &size ); if(ret == kIOReturnSuccess ) { ifr->ifr_devmtu.ifdm_max = FRAMESIZE_TO_MTU(size); ret = ctr->getMinPacketSize( &size ); if(ret == kIOReturnSuccess) ifr->ifr_devmtu.ifdm_min = FRAMESIZE_TO_MTU( size ); } return errnoFromReturn( ret ); } //--------------------------------------------------------------------------- // Handle SIOCSIFMTU ioctl. int IOEthernetInterface::syncSIOCSIFMTU( IONetworkController * ctr, struct ifreq * ifr, bool setAltMTU) { IOReturn ret = kIOReturnSuccess; UInt32 newControllerMTU, oldControllerMTU; UInt32 softMTU = getMaxTransferUnit(); UInt32 requestedMTU = ifr->ifr_mtu; UInt32 maxFrameSize = kIOEthernetMaxPacketSize; // 1518 UInt32 minFrameSize = kIOEthernetMinPacketSize; UInt32 tempSize; // Determine controller's max allowable mtu... if ( ctr->getMaxPacketSize( &tempSize ) == kIOReturnSuccess ) maxFrameSize = max( tempSize, kIOEthernetMaxPacketSize ); // ...and reject requests that are too big. if ( MTU_TO_FRAMESIZE( requestedMTU ) > maxFrameSize ) return EINVAL; // MTU is too large for the controller. // Determine controller's min allowable mtu... ctr->getMinPacketSize( &minFrameSize ); // ...and reject requests that are too small. if ( MTU_TO_FRAMESIZE( requestedMTU ) < minFrameSize && !(setAltMTU && requestedMTU==0)) //allow setting dev MTU to 0 to turn it off return EINVAL; // MTU is too small for the controller. //the controller gets the max of the mtu we're changing and the one we're not. newControllerMTU = max(requestedMTU, setAltMTU ? softMTU : _altMTU); // determine what's currently set on the controller (the max of current soft and alt settings) oldControllerMTU = max(softMTU, _altMTU); // we only have to change the controller if the new value is different from the old, // and either the new value is bigger than the standard max ethernet or the old value was. if(newControllerMTU != oldControllerMTU && ( (MTU_TO_FRAMESIZE(newControllerMTU) > kIOEthernetMaxPacketSize) || (MTU_TO_FRAMESIZE(oldControllerMTU) > kIOEthernetMaxPacketSize) ) ) { ret = ctr->setMaxPacketSize( max(MTU_TO_FRAMESIZE(newControllerMTU), kIOEthernetMaxPacketSize)); //don't set smaller than standard max ethernet } if(ret == kIOReturnSuccess) //if we successfully set value in controller (or didn't need to) then store the new value { if(setAltMTU) _altMTU = requestedMTU; else setMaxTransferUnit( requestedMTU ); } return errnoFromReturn( ret ); } //--------------------------------------------------------------------------- int IOEthernetInterface::syncSIOCSIFLLADDR( IONetworkController * ctr, const char * lladdr, int len ) { unsigned char tempaddr[kIOEthernetAddressSize]; OSData *hardAddr; if(len != kIOEthernetAddressSize) return EINVAL; if (_ctrEnabled != true) /* reject if interface is down */ return (ENETDOWN); // keep a backup in case stack refuses our change hardAddr = OSDynamicCast(OSData, getProperty(kIOMACAddress)); if(hardAddr && hardAddr->getLength() == kIOEthernetAddressSize) bcopy(hardAddr->getBytesNoCopy(), tempaddr, kIOEthernetAddressSize); // change the hardware- we do it before the stack, in case the stack // needs to generate traffic as a result. if ( ctr->setHardwareAddress( lladdr, len ) == kIOReturnSuccess ) { if( ifnet_set_lladdr(getIfnet(), lladdr, len) ) //uh-oh, stack didn't like this { // restore previous address if(hardAddr) ctr->setHardwareAddress(tempaddr, sizeof(tempaddr)); return EINVAL; } setProperty(kIOMACAddress, (void *)lladdr, len); DLOG("%s: SIOCSIFLLADDR %02x:%02x:%02x:%02x:%02x:%02x\n", ctr->getName(), lladdr[0], lladdr[1], lladdr[2], lladdr[3], lladdr[4], lladdr[5]); } return 0; } //--------------------------------------------------------------------------- // Enable a packet filter. #define getOneFilter(x) ((x) & (~((x) - 1))) // this stub's sole purpose is to eliminate warning generated by gcc in enableFilter // when we try to cast a ptr to member function to a ptr to a 'C' function. IOReturn IOEthernetInterface::enableFilter_Wrapper(IOEthernetInterface *self, IONetworkController *ctr, const OSSymbol *group, UInt32 filters, IOOptionBits options) { return self->enableFilter(ctr, group, filters, options); } IOReturn IOEthernetInterface::enableFilter(IONetworkController * ctr, const OSSymbol * group, UInt32 filters, IOOptionBits options) { IOReturn ret = kIOReturnSuccess; if ( options & kFilterOptionNotInsideGate ) { options &= ~kFilterOptionNotInsideGate; return ctr->executeCommand( this, /* client */ (IONetworkController::Action) &IOEthernetInterface::enableFilter_Wrapper, this, /* target */ (void *) ctr, /* param0 */ (void *) group, /* param1 */ (void *) filters, /* param2 */ (void *) options ); /* param3 */ } if ( options & kFilterOptionDisableZeroBits ) { ret = disableFilter(ctr, group, ~filters, options); if ( ret != kIOReturnSuccess) return ret; } // If the controller does not support the packet filter, // there's no need to proceed. if (( GET_SUPPORTED_FILTERS(group) & filters ) != filters) return kIOReturnUnsupported; do { // Add new filter to the set of required filters. UInt32 reqFilters = GET_REQUIRED_FILTERS(group) | filters; UInt32 actFilters = GET_ACTIVE_FILTERS(group); UInt32 resFilters = ( actFilters ^ reqFilters ); if ( (options & kFilterOptionSyncPendingIO) == 0 ) { // Restrict filter update by using 'filters' as a mask. resFilters &= filters; } while ( resFilters && ((options & kFilterOptionDeferIO) == 0) ) { UInt32 oneFilter = getOneFilter(resFilters); // Send a command to the controller driver. ret = ctr->enablePacketFilter(group, oneFilter, actFilters, options); if ( ret != kIOReturnSuccess ) break; resFilters &= ~oneFilter; actFilters |= oneFilter; } if ( (options & kFilterOptionNoStateChange) == 0 ) SET_REQUIRED_FILTERS(group, reqFilters); SET_ACTIVE_FILTERS(group, actFilters); } while (false); return ret; } //--------------------------------------------------------------------------- // Disable a packet filter. IOReturn IOEthernetInterface::disableFilter(IONetworkController * ctr, const OSSymbol * group, UInt32 filters, IOOptionBits options) { IOReturn ret = kIOReturnSuccess; #if 0 if ( options & kFilterOptionNotInsideGate ) { options &= ~kFilterOptionNotInsideGate; return ctr->executeCommand( this, /* client */ (IONetworkController::Action) &IOEthernetInterface::disableFilter, this, /* target */ (void *) ctr, /* param0 */ (void *) group, /* param1 */ (void *) filters, /* param2 */ (void *) options ); /* param3 */ } #endif do { // Remove specified filter from the set of required filters. UInt32 reqFilters = GET_REQUIRED_FILTERS(group) & ~filters; UInt32 actFilters = GET_ACTIVE_FILTERS(group); UInt32 resFilters = ( actFilters ^ reqFilters ) & filters; while ( resFilters && ((options & kFilterOptionDeferIO) == 0) ) { UInt32 oneFilter = getOneFilter(resFilters); // Send a command to the controller driver. ret = ctr->disablePacketFilter(group, oneFilter, actFilters, options); if ( ret != kIOReturnSuccess ) break; resFilters &= ~oneFilter; actFilters &= ~oneFilter; } if ( (options & kFilterOptionNoStateChange) == 0 ) SET_REQUIRED_FILTERS(group, reqFilters); SET_ACTIVE_FILTERS(group, actFilters); } while (false); return ret; } //--------------------------------------------------------------------------- // Cache the list of multicast addresses and send a command to the // controller to update the multicast list. IOReturn IOEthernetInterface::setupMulticastFilter(IONetworkController * ctr) { void * multiAddrs = 0; UInt mcount; OSData * mcData = 0; ifnet_t interface; struct sockaddr dlAddress; IOReturn ret = kIOReturnSuccess; bool ok; ifmultiaddr_t *addressList; interface = getIfnet(); assert(interface); // get the list and count how many mcast link addresses there are if(ifnet_get_multicast_list(interface, &addressList)) return kIOReturnNoMemory; mcount = 0; for(int i=0; addressList[i]; i++) { ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress)); if (dlAddress.sa_family == AF_UNSPEC || dlAddress.sa_family == AF_LINK) mcount++; } _mcAddrCount = mcount; // now rewalk the list and copy the addresses to a format suitable to give to the controller if ( mcount ) { char * addrp; mcData = OSData::withCapacity(mcount * ETHER_ADDR_LEN); if (!mcData) { DLOG("%s: no memory for multicast address list\n", getName()); ifnet_free_multicast_list(addressList); return kIOReturnNoMemory; } // Loop through the list and copy the link multicast // address to the OSData. for(int i = 0; addressList[i]; i++) { //retrieve the datalink mcast address ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress)); if (dlAddress.sa_family == AF_UNSPEC) addrp = &dlAddress.sa_data[0]; else if (dlAddress.sa_family == AF_LINK) addrp = LLADDR((struct sockaddr_dl *)&dlAddress); else continue; ok = mcData->appendBytes((const void *) addrp, ETHER_ADDR_LEN); assert(ok); } multiAddrs = (void *) mcData->getBytesNoCopy(); assert(multiAddrs); } // Issue a controller command to setup the multicast filter. ret = ((IOEthernetController *)ctr)->setMulticastList( (IOEthernetAddress *) multiAddrs, mcount); if (mcData) { if (ret == kIOReturnSuccess) setProperty(kIOMulticastAddressList, mcData); mcData->release(); } else { removeProperty(kIOMulticastAddressList); } ifnet_free_multicast_list(addressList); return ret; } //--------------------------------------------------------------------------- // Power management support. // // Handlers called, with the controller's gate closed, in response to a // controller power state change. IOReturn IOEthernetInterface::controllerWillChangePowerState( IONetworkController * ctr, IOPMPowerFlags flags, UInt32 stateNumber, IOService * policyMaker ) { if ( ( (flags & IOPMDeviceUsable ) == 0) && ( _controllerLostPower == false ) && _ctrEnabled ) { UInt32 filters; _controllerLostPower = true; // Get the "aggressiveness" factor from the policy maker. ctr->getAggressiveness( kPMEthernetWakeOnLANSettings, &filters ); filters &= GET_SUPPORTED_FILTERS( gIOEthernetWakeOnLANFilterGroup ); // Is the link up? If it is, leave the WOL filters intact, // otherwise mask out the WOL filters that would not function // without a proper link. This will reduce power consumption // for cases when a machine is put to sleep and there is no // network connection. OSNumber * linkStatusNumber = (OSNumber *) ctr->getProperty( kIOLinkStatus ); if ( ( linkStatusNumber == 0 ) || ( ( linkStatusNumber->unsigned32BitValue() & (kIONetworkLinkValid | kIONetworkLinkActive) ) == kIONetworkLinkValid ) ) { filters &= ~( kIOEthernetWakeOnMagicPacket | kIOEthernetWakeOnPacketAddressMatch ); } // Before controller is disabled, program its wake up filters. // The WOL setting is formed by a bitwise OR between the WOL filter // settings, and the aggresiveness factor from the policy maker. enableFilter( ctr, gIOEthernetWakeOnLANFilterGroup, filters, kFilterOptionNoStateChange | kFilterOptionSyncPendingIO ); // Call SIOCSIFFLAGS handler to disable the controller, // and mark the interface as Not Running. syncSIOCSIFFLAGS(ctr); } return super::controllerWillChangePowerState( ctr, flags, stateNumber, policyMaker ); } IOReturn IOEthernetInterface::controllerDidChangePowerState( IONetworkController * ctr, IOPMPowerFlags flags, UInt32 stateNumber, IOService * policyMaker ) { IOReturn ret = super::controllerDidChangePowerState( ctr, flags, stateNumber, policyMaker ); if ( ( flags & IOPMDeviceUsable ) && ( _controllerLostPower == true ) ) { _controllerLostPower = false; // Clear _controllerLostPower, then call the SIOCSIFFLAGS handler to // perhaps enable the controller, restore all Ethernet controller // state, then mark the interface as Running. syncSIOCSIFFLAGS(ctr); } return ret; } #define kIONetworkInterfaceProperties "IONetworkInterfaceProperties" //--------------------------------------------------------------------------- // Handle a request to set properties from kernel or non-kernel clients. // For non-kernel clients, the preferred mechanism is through an user // client. IOReturn IOEthernetInterface::setProperties( OSObject * properties ) { IOReturn ret; OSDictionary * dict = (OSDictionary *) properties; OSNumber * num; // Call IONetworkInterface::setProperties() first. ret = super::setProperties(properties); if ( (ret == kIOReturnSuccess) || (ret == kIOReturnUnsupported) ) { dict = OSDynamicCast( OSDictionary, dict->getObject(kIONetworkInterfaceProperties) ); if ( dict ) { dict = OSDynamicCast( OSDictionary, dict->getObject(kIORequiredPacketFilters) ); if ( dict ) { num = OSDynamicCast( OSNumber, dict->getObject(kIOEthernetWakeOnLANFilterGroup) ); if ( num ) { ret = enableFilter( getController(), gIOEthernetWakeOnLANFilterGroup, num->unsigned32BitValue(), kFilterOptionDeferIO | kFilterOptionNotInsideGate | kFilterOptionDisableZeroBits ); } } } } return ret; } //--------------------------------------------------------------------------- // willTerminate bool IOEthernetInterface::willTerminate( IOService * provider, IOOptionBits options ) { bool ret = super::willTerminate( provider, options ); // We assume that willTerminate() is always called from the // provider's work loop context. // Once the gated ioctl path has been blocked, disable the controller. // The hardware may have already been removed from the system. if ( _ctrEnabled && getController() ) { DLOG("IOEthernetInterface::willTerminate disabling controller\n"); getController()->doDisable( this ); _ctrEnabled = false; } return ret; } //--------------------------------------------------------------------------- IOReturn IOEthernetInterface::attachToDataLinkLayer( IOOptionBits options, void * parameter ) { IOReturn ret = super::attachToDataLinkLayer( options, parameter ); if ( ret == kIOReturnSuccess ) { // after successful attach, the interface is backed by an actual BSD ifnet_t. // now we can set a few important ethernet specific values on it. // Set defaults suitable for Ethernet interfaces. ifnet_set_baudrate( getIfnet(), 10000000); //FIXME maybe IONetworkInterface should provide accessor bpfattach( getIfnet(), DLT_EN10MB, sizeof(struct ether_header) ); } return ret; } #define VLAN_HEADER_LEN 4 // Temporarily stuff a vlan tag back into a packet so that tag shows up to bpf. // We do it by creating a temp header mbuf with the enet/vlan header in it and // then point its next field to the proper place (after the dest+src addresses) in the original // mbuf. void IOEthernetInterface::_fixupVlanPacket(mbuf_t mt, u_int16_t vlan_tag, int inputPacket) { mbuf_t newmb; mbuf_t chain; size_t remainingBytes; size_t copyBytes = 0; //initialize to prevent annoying, incorrect warning that it's used uninitialized char * destptr; if( mbuf_gethdr(M_DONTWAIT, MT_DATA, &newmb) ) return; //init enough of the mbuf to keep bpf happy mbuf_setlen(newmb, ETHER_ADDR_LEN*2 + VLAN_HEADER_LEN); mbuf_pkthdr_setlen(newmb, mbuf_pkthdr_len( mt ) + VLAN_HEADER_LEN); mbuf_pkthdr_setrcvif(newmb, mbuf_pkthdr_rcvif( mt ) ); //now walk the incoming mbuf to copy out its dst & src address and //locate the type/len field in the packet. chain = mt; remainingBytes = ETHER_ADDR_LEN*2; destptr = (char *)mbuf_data( newmb ); while(chain && remainingBytes) { copyBytes = remainingBytes > mbuf_len( chain ) ? mbuf_len( chain ): remainingBytes; remainingBytes -= copyBytes; bcopy( mbuf_data( chain ), destptr, copyBytes); destptr += copyBytes; if (mbuf_len( chain ) == copyBytes) //we've completely drained this mbuf { chain = mbuf_next( chain ); //advance to next copyBytes = 0; //if we break out of loop now, make sure the offset is correct } } // chain points to the mbuf that contains the packet data with type/len field // and copyBytes indicates the offset it's at. if(chain==0 || remainingBytes) { mbuf_freem( newmb ); return; //if we can't munge the packet, just return } //patch mbuf so its data points after the dst+src address mbuf_setdata(chain, (char *)mbuf_data( chain ) + copyBytes, mbuf_len( chain ) - copyBytes ); //finish setting up our head mbuf *(short *)(destptr) = htons(ETHERTYPE_VLAN); //vlan magic number *(short *)(destptr + 2) = htons( vlan_tag ); // and the tag's value mbuf_setnext( newmb, chain ); //stick it infront of the rest of the packet // feed the tap if(inputPacket) super::feedPacketInputTap( newmb ); else super::feedPacketOutputTap( newmb ); //release the fake header mbuf_setnext( newmb, NULL ); mbuf_freem( newmb ); //and repair our old mbuf mbuf_setdata( chain, (char *)mbuf_data( chain ) - copyBytes, mbuf_len( chain ) + copyBytes ); } //we need to feed the taps the packets as they appear //on the wire...but devices with hardware vlan stuffing/stripping //put that info oob. so we must munge the packet with the //vlan, feed the tap, then unmunge it. void IOEthernetInterface::feedPacketInputTap(mbuf_t mt) { u_int16_t vlan; if( mbuf_get_vlan_tag(mt, &vlan) == 0) //does this packet have oob vlan tag? _fixupVlanPacket(mt, vlan, 1); else super::feedPacketInputTap(mt); } void IOEthernetInterface::feedPacketOutputTap(mbuf_t mt) { u_int16_t vlan; if( mbuf_get_vlan_tag(mt, &vlan) == 0) //does this packet have oob vlan tag? _fixupVlanPacket(mt, vlan, 0); else super::feedPacketOutputTap(mt); }