SrvIntNetR0.cpp revision a5f2a572272cb3944e43d779d1e4e774891dd359
1N/A * Internal networking - The ring 0 service. 1N/A * Copyright (C) 2006-2008 Sun Microsystems, Inc. 1N/A * This file is part of VirtualBox Open Source Edition (OSE), as 1N/A * you can redistribute it and/or modify it under the terms of the GNU 1N/A * General Public License (GPL) as published by the Free Software 1N/A * Foundation, in version 2 as it comes in the "COPYING" file of the 1N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the 1N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. 1N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa 1N/A * additional information or have any questions. 1N/A/******************************************************************************* 1N/A*******************************************************************************/ 1N/A/******************************************************************************* 1N/A* Defined Constants And Macros * 1N/A*******************************************************************************/ 1N/A/** @def INTNET_WITH_DHCP_SNOOPING 1N/A * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */ 1N/A/******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** The invalid 0 entry. */ /** The end of the valid values. */ /** The usual 32-bit hack. */ /** Pointer to a network layer address type. */ /** Pointer to an address. */ /** Pointer to a const address. */ * Address cache for a specific network layer. /** Pointer to the table of addresses. */ /** The number of valid address entries. */ /** The number of allocated address entries. */ /** The size of an entry. */ /** Pointer to an address cache. */ /** Pointer to a const address cache. */ * Unless explicitly stated, all members are protect by the network semaphore. /** Pointer to the next interface. * This is protected by the INTNET::FastMutex. */ /** The current MAC address for the interface. */ /** Set if the INTNET::Mac member is valid. */ /** Set if the interface is in promiscuous mode. * In promiscuous mode the interface will receive all packages except the one it's sending. */ /** Whether the interface is active or not. */ /** Whether someone is currently in the destructor. */ /** Number of yields done to try make the interface read pending data. * We will stop yeilding when this reaches a threshold assuming that the VM is paused or * that it simply isn't worth all the delay. It is cleared when a successful send has been done. /** Pointer to the current exchange buffer (ring-0). */ /** Pointer to ring-3 mapping of the current exchange buffer. */ /** Pointer to the default exchange buffer for the interface. */ /** Pointer to ring-3 mapping of the default exchange buffer. */ /** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */ /** Number of threads sleeping on the Event semaphore. */ /** The interface handle. * When this is INTNET_HANDLE_INVALID a sleeper which is waking up * should return with the appropriate error condition. */ /** Pointer to the network this interface is connected to. * This is protected by the INTNET::FastMutex. */ /** The session this interface is associated with. */ /** The SUPR0 object id. */ /** The network layer address cache. (Indexed by type, 0 entry isn't used.) */ /** Pointer to an internal network interface. */ /** The port interface we present to the component. */ /** The port interface we get from the component. */ /** The trunk mutex that serializes all calls <b>to</b> the component. */ /** Pointer to the network we're connect to. * This may be NULL if we're orphaned? */ /** The cached MAC address of the interface the trunk is attached to. * This is for the situations where we cannot take the out-bound * semaphore (the recv case) but need to make frame edits (ARP). */ /** Whether to supply physical addresses with the outbound SGs. */ /** Set if the 'wire' is in promiscuous mode. * The state of the 'host' is queried each time. */ /** Pointer to a trunk interface. */ /** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */ * Internal representation of a network. /** The Next network in the chain. * This is protected by the INTNET::FastMutex. */ /** List of interfaces connected to the network. * This is protected by the INTNET::FastMutex. */ /** Pointer to the trunk interface. * Can be NULL if there is no trunk connection. */ * It protects everything dealing with this network. */ /** Pointer to the instance data. */ /** The SUPR0 object id. */ /** Pointer to the temporary buffer that is used when snooping fragmented packets. * This is allocated after this structure if we're sharing the MAC address with * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundrary. */ /** Network creation flags (INTNET_OPEN_FLAGS_*). */ /** The number of active interfaces (excluding the trunk). */ /** The length of the network name. */ /** Pointer to an internal network. */ /** The size of the buffer INTNETNETWORK::pbTmp points at. */ * Internal networking instance. /** Mutex protecting the network creation, opening and destruction. * (This means all operations affecting the pNetworks list.) */ /** List of networks. Protected by INTNET::Spinlock. */ /** Handle table for the interfaces. */ /******************************************************************************* *******************************************************************************/ * Initializes a scatter / gather buffer from a simple linear buffer. * @returns Pointer to the start of the frame. * @param pSG Pointer to the scatter / gather structure. * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.) * @param pvFrame Pointer to the frame * @param cbFrame The size of the frame. * Initializes a scatter / gather buffer from a internal networking packet. * @returns Pointer to the start of the frame. * @param pSG Pointer to the scatter / gather structure. * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.) * @param pHdr Pointer to the packet header. * @param pBuf The buffer the header is within. Only used in strict builds. * Worker for intnetR0SgWritePart that deals with the case where the * request doesn't fit into the first segment. * @returns true, unless the request or SG invalid. * @param pSG The SG list to write to. * @param off Where to start writing (offset into the SG). * @param cb How much to write. * @param pvBuf The buffer to containing the bits to write. * Skip ahead to the segment where off starts. * Copy the data, hoping that it's all from one segment... /* copy the portion in the current segment. */ /* copy the portions in the other segments. */ * Writes to a part of an SG. * @returns true on success, false on failure (out of bounds). * @param pSG The SG list to write to. * @param off Where to start writing (offset into the SG). * @param cb How much to write. * @param pvBuf The buffer to containing the bits to write. /* The optimized case. */ * Reads a byte from a SG list. * @returns The byte on success. 0xff on failure. * @param pSG The SG list to read. * @param off The offset (into the SG) off the byte. * Worker for intnetR0SgReadPart that deals with the case where the * requested data isn't in the first segment. * @returns true, unless the SG is invalid. * @param pSG The SG list to read. * @param off Where to start reading (offset into the SG). * @param cb How much to read. * @param pvBuf The buffer to read into. * Skip ahead to the segment where off starts. * Copy the data, hoping that it's all from one segment... /* copy the portion in the current segment. */ /* copy the portions in the other segments. */ * Reads a part of an SG into a buffer. * @returns true on success, false on failure (out of bounds). * @param pSG The SG list to read. * @param off Where to start reading (offset into the SG). * @param cb How much to read. * @param pvBuf The buffer to read into. /* The optimized case. */ * Reads an entire SG into a fittingly size buffer. * @param pSG The SG list to read. * @param pvBuf The buffer to read into (at least pSG->cbTotal in size). * @returns VBox status code, can assume success in most situations. * @param pIf The interface instance. * @param pSession The current session. * Release an interface previously retained by intnetR0IfRetain or * @returns true if destroyed, false if not. * @param pIf The interface instance. * @param pSession The current session. * RTHandleCreateEx callback that retains an object in the * handle table before returning it. * (Avoids racing the freeing of the handle.) * @returns VBox status code. * @param hHandleTable The handle table (ignored). * @param pvObj The object (INTNETIF). * @param pvCtx The context (SUPDRVSESSION). * @param pvUser The user context (ignored). * Checks if the IPv4 address is a broadcast address. * @param Addr The address, network endian. /* Just check for 255.255.255.255 atm. */ * Checks if the IPv4 address is a good interface address. * @param Addr The address, network endian. ||
Addr.
au8[0] == 0)
/* Current network, can be used as source address. */ || (
Addr.
au8[0] &
0xf0) ==
224 /* Multicast */ * Gets the address size of a network layer type. * @returns size in bytes. * @param enmType The type. * Compares two address to see if they are equal, assuming naturally align structures. * @returns true if equal, false if not. * @param pAddr1 The first address. * @param pAddr2 The second address. * @param cbAddr The address size. * Worker for intnetR0IfAddrCacheLookup that performs the lookup * in the remaining cache entries after the caller has check the * @returns -1 if not found, the index of the cache entry if found. * @param pCache The cache. * @param pAddr The address. * @param cbAddr The address size (optimization). * Lookup an address in a cache without any expectations. * @returns -1 if not found, the index of the cache entry if found. * @param pCache The cache. * @param pAddr The address. * @param cbAddr The address size (optimization). * The optimized case is when there is one cache entry and /** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */ /** @todo implement this. */ * Worker for intnetR0IfAddrCacheLookupUnlikely that performs * the lookup in the remaining cache entries after the caller * has check the most likely ones. * The routine is expecting not to find the address. * @returns -1 if not found, the index of the cache entry if found. * @param pCache The cache. * @param pAddr The address. * @param cbAddr The address size (optimization). * Perform a full table lookup. * Lookup an address in a cache expecting not to find it. * @returns -1 if not found, the index of the cache entry if found. * @param pCache The cache. * @param pAddr The address. * @param cbAddr The address size (optimization). * The optimized case is when there is one cache entry and * Then check the last entry and return if there are just two cache entries. * Deletes a specific cache entry. * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf. * @param pIf The interface (for logging). * @param pCache The cache. * @param iEntry The entry to delete. * @param pszMsg Log message. Log((
"intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
Log((
"intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
* Deletes an address from the cache, assuming it isn't actually in the cache. * @param pIf The interface (for logging). * @param pCache The cache. * @param pAddr The address. * @param cbAddr The address size (optimization). * Deletes the address from all the interface caches. * This is used to remove stale entries that has been reassigned to * other machines on the network. * @param pNetwork The network. * @param pAddr The address. * @param enmType The address type. * @param cbAddr The address size (optimization). * @param pszMsg Log message. * Deletes the address from all the interface caches except the specified one. * This is used to remove stale entries that has been reassigned to * other machines on the network. * @param pNetwork The network. * @param pAddr The address. * @param enmType The address type. * @param cbAddr The address size (optimization). * @param pszMsg Log message. * Lookup an address on the network, returning the (first) interface * having it in its address cache. * @returns Pointer to the interface on success, NULL if not found. * @param pNetwork The network. * @param pAddr The address to lookup. * @param enmType The address type. * @param cbAddr The size of the address. * Adds an address to the cache, the caller is responsible for making sure it' * s not already in the cache. * @param pIf The interface (for logging). * @param pCache The address cache. * @param pAddr The address. * @param pszMsg log message. /* Allocate the first array */ Log((
"intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
* Add the new entry to the end of the array. Log((
"intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
Log((
"intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs type=%d added #%d %.*Rhxs %s\n",
* A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup. * @param pIf The interface (for logging). * @param pCache The address cache. * @param pAddr The address. * @param cbAddr The size of the address (optimization). * @param pszMsg Log message. * Check all but the first and last entries, the caller * has already checked those. * Adds an address to the cache if it's not already there. * @param pIf The interface (for logging). * @param pCache The address cache. * @param pAddr The address. * @param cbAddr The size of the address (optimization). * @param pszMsg Log message. * The optimized case is when the address the first or last cache entry. * Snoops IP assignments and releases from the DHCPv4 traffic. * The caller is responsible for making sure this traffic between the * BOOTPS and BOOTPC ports and validate the IP header. The UDP packet * need not be validated beyond the ports. * @param pNetwork The network this frame was seen on. * @param pIpHdr Pointer to a valid IP header. This is for pseudo * header validation, so only the minimum header size * needs to be available and valid here. * @param pUdpHdr Pointer to the UDP header in the frame. * @param cbUdpPkt What's left of the frame when starting at the UDP header. * Check if the DHCP message is valid and get the type. Log6((
"Bad UDP packet\n"));
Log6((
"Bad DHCP packet\n"));
/** @todo Check for valid non-broadcast requests w/ IP for any of the MACs we * know, and add the IP to the cache. */ * Lookup the interface by its MAC address and insert the IPv4 address into the cache. * Delete the old client address first, just in case it changed in a renewal. * Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache. * Worker for intnetR0TrunkIfSnoopAddr that takes care of what * is likely to be a DHCP message. * The caller has already check that the UDP source and destination ports * @param pNetwork The network this frame was seen on. * @param pSG The gather list for the frame. * Get a pointer to a linear copy of the full packet, using the * temporary buffer if necessary. //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP; * Validate the IP header and find the UDP packet. Log((
"intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
* Hand it over to the common DHCP snooper. #
endif /* INTNET_WITH_DHCP_SNOOPING */ * Snoops up source addresses from ARP requests and purge these * from the address caches. * The purpose of this purging is to get rid of stale addresses. * @param pNetwork The network this frame was seen on. * @param pSG The gather list for the frame. * Check the minimum size first. * Copy to temporary buffer if necessary. * Ignore packets which doesn't interest us or we perceive as malformed. * Delete the source address if it's OK. * Snoop up addresses from ARP and DHCP traffic from frames comming * over the trunk connection. * The caller is responsible for do some basic filtering before calling * For IPv4 this means checking against the minimum DHCPv4 frame size. * @param pNetwork The network. * @param pSG The SG list for the frame. * @param EtherType The Ethertype of the frame. /* check if the protocol is UDP */ /* get the TCP header length */ /* check if the protocol is UDP */ /* get the TCP header length */ /* get the lower byte of the UDP source port number. */ /* get the lower byte of the UDP destination port number. */ /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might * need to be edited. Check out how NDP works... */ #
endif /* INTNET_WITH_DHCP_SNOOPING */ * Deals with an IPv4 packet. * This will fish out the source IP address and add it to the cache. * Then it will look for DHCPRELEASE requests (?) and anything else * that we migh find useful later. * @param pIf The interface that's sending the frame. * @param pIpHdr Pointer to the IPv4 header in the frame. * @param cbPacket The size of the packet, or more correctly the * size of the frame without the ethernet header. * Check the header size first to prevent access invalid data. * If the source address is good (not broadcast or my network) and * not already in the address cache of the sender, add it. Validate * the IP header before adding it. Log((
"intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
* Check for potential DHCP packets. Log((
"intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
#
endif /* INTNET_WITH_DHCP_SNOOPING */ * Snoop up source addresses from an ARP request or reply. * @param pIf The interface that's sending the frame. * @param pHdr The ARP header. * @param cbPacket The size of the packet (migth be larger than the ARP * request 'cause of min ethernet frame size). * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we * don't have to repeat the frame parsing in intnetR0TrunkIfSend. * Ignore packets which doesn't interest us or we perceive as malformed. * Tag the SG as ARP IPv4 for later editing, then check for addresses * which can be removed or added to the address cache of the sender. * Checks packets send by a normal interface for new network * @param pIf The interface that's sending the frame. * @param pbFrame The frame. * @param cbFrame The size of the frame. * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we * don't have to repeat the frame parsing in intnetR0TrunkIfSend. * Fish out the ethertype and look for stuff we can handle. #
if 0
/** @todo IntNet: implement IPv6 for wireless MAC sharing. */ /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might * need to be edited. Check out how NDP works... */ #
if 0
/** @todo IntNet: implement IPX for wireless MAC sharing? */ * Reads the next frame in the buffer. * The caller is responsible for ensuring that there is a valid frame in the buffer. * @returns Size of the frame in bytes. * @param pBuf The buffer. * @param pRingBuff The ring buffer to read from. * @param pvFrame Where to put the frame. The caller is responsible for * ensuring that there is sufficient space for the frame. #
endif /* IN_INTNET_TESTCASE */ * Writes a frame packet to the buffer. * @returns VBox status code. * @param pBuf The buffer. * @param pRingBuf The ring buffer to read from. * @param pSG The gather list. * @param pNewDstMac Set the destination MAC address to the address if specified. * Try fit it all before the end of the buffer. * Try fit the frame at the start of the buffer. * (The header fits before the end of the buffer because of alignment.) * The reader is ahead of the writer, try fit it into that space. * Sends a frame to a specific interface. * @param pIf The interface. * @param pIfSender The interface sending the frame. This is NULL if it's the trunk. * @param pSG The gather buffer which data is being sent to the interface. * @param pNewDstMac Set the destination MAC address to the address if specified. // LogFlow(("intnetR0IfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf)); #
if 0
/* This is bad stuff now as we're blocking while locking down the network. we really shouldn't delay the network traffic on the host just because some bugger isn't responding. Will have to deal with this in a different * Retry a few times, yielding the CPU in between. * But don't let a unresponsive VM harm performance, so give up after a couple of tries. * Scheduling hack, for unicore machines primarily. &&
pIfSender /* but not if it's from the trunk */)
/* ok, the frame is lost. */ * Sends a frame down the trunk. * The caller must own the network mutex, might be abandond temporarily. * The fTrunkLock parameter indicates whether the trunk lock is held. * @param pThis The trunk. * @param pNetwork The network the frame is being sent to. * @param pIfSender The IF sending the frame. Used for MAC address checks in shared MAC mode. * @param fDst The destination flags. * @param pSG Pointer to the gather list. * @param fTrunkLocked Whether the caller owns the out-bound trunk lock. * Edit the frame if we're sharing the MAC address with the host on the wire. * If the frame is headed for both the host and the wire, we'll have to send * it to the host before making any modifications, and force the OS specific * backend to copy it. We do this by marking it as TEMP (which is always the /* Dispatch it to the host before making changes. */ /* ASSUME frame from INTNETR0IfSend! */ * Get the host mac address and update the ethernet header. * The reason for caching it in the trunk structure is because * we cannot take the trunk out-bound semaphore when we make * edits in the intnetR0TrunkIfPortRecv path. * Deal with tags from the snooping phase. * APR IPv4: replace hardware (MAC) addresses because these end up * in ARP caches. So, if we don't the other machiens will * send the packets to the MAC address of the guest * instead of the one of the host, which won't work on //else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP) //{ /// @todo move the editing into a different function * Temporarily leave the network lock while transmitting the frame. * Note that we're relying on the out-bound lock to serialize threads down * in INTNETR0IfSend. It's theoretically possible for there to be race now * because I didn't implement async SG handling yet. Which is why we currently * require the trunk to be locked, well, one of the reasons. * Another reason is that the intnetR0NetworkSendUnicast code may have to * call into the trunk interface component to do package switching. /** @todo failure statistics? */ Log2((
"intnetR0TrunkIfSend: %Rrc fDst=%d\n",
rc,
fDst));
* Edits an ARP packet arriving from the wire via the trunk connection. * @param pNetwork The network the frame is being sent to. * @param pSG Pointer to the gather list for the frame. * The flags and data content may be updated. * @param pEthHdr Pointer to the ethernet header. This may also be * updated if it's a unicast... * Check the minimum size and get a linear copy of the thing to work on, * using the temporary buffer if necessary. * Ignore packets which doesn't interest us or we perceive as malformed. /* Tag it as ARP IPv4. */ * The thing we're interested in here is a reply to a query made by a guest * since we modified the MAC in the initial request the guest made. /* Write back the packet if we've been making changes to a buffered copy. */ * Detects and edits an DHCP packet arriving from the internal net. * @param pNetwork The network the frame is being sent to. * @param pSG Pointer to the gather list for the frame. * The flags and data content may be updated. * @param pEthHdr Pointer to the ethernet header. This may also be * updated if it's a unicast... * Check the minimum size and get a linear copy of the thing to work on, * using the temporary buffer if necessary. * Get a pointer to a linear copy of the full packet, using the * temporary buffer if necessary. //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP; * Validate the IP header and find the UDP packet. Log6((
"intnetR0NetworkEditDhcpFromIntNet: bad ip header\n"));
/* We are only interested in DHCP packets coming from client to server. */ * Check if the DHCP message is valid and get the type. Log6((
"intnetR0NetworkEditDhcpFromIntNet: Bad UDP packet\n"));
Log6((
"intnetR0NetworkEditDhcpFromIntNet: Bad DHCP packet\n"));
Log6((
"intnetR0NetworkEditDhcpFromIntNet: Setting broadcast flag in DHCP %#x, previously %x\n",
MsgType,
pDhcp->
bp_flags));
* Sends a broadcast frame. * The caller must own the network mutex, might be abandond temporarily. * When pIfSender is not NULL, the caller must also own the trunk semaphore. * @returns true if it's addressed to someone on the network, otherwise false. * @param pNetwork The network the frame is being sent to. * @param pIfSender The interface sending the frame. This is NULL if it's the trunk. * @param fSrc The source flags. This 0 if it's not from the trunk. * @param pSG Pointer to the gather list. * @param fTrunkLocked Whether the caller owns the out-bound trunk lock. * @param pEthHdr Pointer to the ethernet header. * Check for ARP packets from the wire since we'll have to make * modification to them if we're sharing the MAC address with the host. * Check for DHCP packets from the internal net since we'll have to set * broadcast flag in DHCP requests if we're sharing the MAC address with * This is a broadcast or multicast address. For the present we treat those * two as the same - investigating multicast is left for later. * Write the packet to all the interfaces and signal them. * Unless the trunk is the origin, broadcast it to both the wire * Snoop address info from packet orginating from the trunk connection. return false;
/* broadcast frames are never dropped */ * Sends a multicast frame. * The caller must own the network mutex, might be abandond temporarily. * @returns true if it's addressed to someone on the network, otherwise false. * @param pNetwork The network the frame is being sent to. * @param pIfSender The interface sending the frame. This is NULL if it's the trunk. * @param fSrc The source flags. This 0 if it's not from the trunk. * @param pSG Pointer to the gather list. * @param fTrunkLocked Whether the caller owns the out-bound trunk lock. * @param pEthHdr Pointer to the ethernet header. /** @todo implement multicast */ * Sends a unicast frame using the network layer address instead * The caller must own the network mutex, might be abandond temporarily. * @returns true if it's addressed to someone on the network, otherwise false. * @param pNetwork The network the frame is being sent to. * @param pSG Pointer to the gather list. * @param fTrunkLocked Whether the caller owns the out-bound trunk lock. * @param pEthHdr Pointer to the ethernet header. * Extract the network address from the packet. Log((
"intnetshareduni: failed to read ip_dst! cbTotal=%#x\n",
pSG->
cbTotal));
#
if 0
/** @todo IntNet: implement IPv6 for wireless MAC sharing. */ Log((
"intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n",
pSG->
cbTotal));
#
if 0
/** @todo IntNet: implement IPX for wireless MAC sharing? */ Log((
"intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n",
pSG->
cbTotal));
* Treat ARP is broadcast (it shouldn't end up here normally, * so it goes last in the switch). Log6((
"intnetshareduni: ARP\n"));
/** @todo revisit this broadcasting of unicast ARP frames! */ * Unknown packets are sent do all interfaces that are in promiscuous mode. * Send it to interfaces with matching network addresses. #
endif /* INTNET_WITH_DHCP_SNOOPING */ * The caller must own the network mutex, might be abandond temporarily. * @returns true if it's addressed to someone on the network, otherwise false. * @param pNetwork The network the frame is being sent to. * @param pIfSender The interface sending the frame. This is NULL if it's the trunk. * @param fSrc The source flags. This 0 if it's not from the trunk. * @param pSG Pointer to the gather list. * @param fTrunkLocked Whether the caller owns the out-bound trunk lock. * @param pEthHdr Pointer to the ethernet header. * Only send to the interfaces with matching a MAC address. &&
pIf !=
pIfSender /* promiscuous mode: omit the sender */))
* If we didn't find the recipient on the internal network the * frame will hit the wire. /* promiscuous checks first as they are cheaper than pfnIsHostMac. */ * This function will distribute the frame to the interfaces it is addressed to. * It will also update the MAC address of the sender. * The caller must own the network mutex. * @returns true if it's addressed to someone on the network, otherwise false. * @param pNetwork The network the frame is being sent to. * @param pIfSender The interface sending the frame. This is NULL if it's the trunk. * @param fSrc The source flags. This 0 if it's not from the trunk. * @param pSG Pointer to the gather list. * @param fTrunkLocked Whether the caller owns the out-bound trunk lock. * Get the ethernet header (might theoretically involve multiple segments). Log2((
"D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
* Inspect the header updating the mac address of the sender in the process. * Sends one or more frames. * The function will first the frame which is passed as the optional * arguments pvFrame and cbFrame. These are optional since it also * possible to chain together one or more frames in the send buffer * which the function will process after considering it's arguments. * @returns VBox status code. * @param pIntNet The instance data. * @param hIf The interface handle. * @param pSession The caller's session. * @param pvFrame Pointer to the frame. Optional, please don't use. * @param cbFrame Size of the frame. Optional, please don't use. * Validate input and translate the handle. /* This is the better place to crash, probe the buffer. */ * Lock the network. If there is a trunk retain it and grab its * out-bound lock (this requires leaving the network lock first). * Grabbing the out-bound lock here simplifies things quite a bit * later on, so while this is excessive and a bit expensive it's * not worth caring about right now. INTNETSG Sg;
/** @todo this will have to be changed if we're going to use async sending * with buffer sharing for some OS or service. Darwin copies everything so * I won't bother allocating and managing SGs rigth now. Sorry. */ * Process the send buffer. /* Send the frame if the type is sane. */ /* else: ignore the frame */ /* Skip to the next frame. */ * Release the semaphore(s) and release references. * VMMR0 request wrapper for INTNETR0IfSend. * @returns see INTNETR0IfSend. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Maps the default buffer into ring 3. * @returns VBox status code. * @param pIntNet The instance data. * @param hIf The interface handle. * @param pSession The caller's session. * @param ppRing3Buf Where to store the address of the ring-3 mapping. * ASSUMES that only the process that created an interface can use it. * ASSUMES that we created the ring-3 mapping when selecting or * VMMR0 request wrapper for INTNETR0IfGetRing3Buffer. * @returns see INTNETR0IfGetRing3Buffer. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Gets the ring-0 address of the current buffer. * @returns VBox status code. * @param pIntNet The instance data. * @param hIf The interface handle. * @param pSession The caller's session. * @param ppRing0Buf Where to store the address of the ring-3 mapping. * Grab the lock and get the data. * ASSUMES that the handle isn't closed while we're here. * Gets the physical addresses of the default interface buffer. * @returns VBox status code. * @param pIntNet The instance data. * @param hIF The interface handle. * @param paPages Where to store the addresses. (The reserved fields will be set to zero.) * Grab the lock and get the data. * ASSUMES that the handle isn't closed while we're here. /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there * is no need for any extra bookkeeping here.. */ * Sets the promiscuous mode property of an interface. * @returns VBox status code. * @param pIntNet The instance handle. * @param hIf The interface handle. * @param pSession The caller's session. * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not. * Validate & translate input. Log((
"INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
* Grab the network semaphore and make the change. Log((
"INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
* VMMR0 request wrapper for INTNETR0IfSetPromiscuousMode. * @returns see INTNETR0IfSetPromiscuousMode. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Sets the MAC address of an interface. * @returns VBox status code. * @param pIntNet The instance handle. * @param hIf The interface handle. * @param pSession The caller's session. * @param pMAC The new MAC address. * Validate & translate input. Log((
"INTNETR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
* Grab the network semaphore and make the change. Log((
"INTNETR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
* VMMR0 request wrapper for INTNETR0IfSetMacAddress. * @returns see INTNETR0IfSetMacAddress. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Worker for intnetR0IfSetActive. * This function will update the active interface count on the network and * activate or deactivate the trunk connection if necessary. Note that in * order to do this it is necessary to abandond the network semaphore. * @returns VBox status code. * @param pNetwork The network. * @param fIf The interface. * @param fActive What to do. * If we've got a trunk, lock it now in case we need to call out, and * Make the change if necessary. * We'll have to change the trunk status, so, leave * the network semaphore so we don't create any deadlocks. * Activates or deactivates a interface. * This is used to enable and disable the trunk connection on demans as well as * know when not to expect an interface to want to receive packets. * @returns VBox status code. * @param pIf The interface. * @param fActive What to do. * Hand it to the network since it might involve the trunk * and things are tricky there wrt to locking order. * Sets the active property of an interface. * @returns VBox status code. * @param pIntNet The instance handle. * @param hIf The interface handle. * @param pSession The caller's session. * @param fActive The new state. * Validate & translate input. Log((
"INTNETR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
* Hand it to the network since it might involve the trunk * and things are tricky there wrt to locking order. * VMMR0 request wrapper for INTNETR0IfSetActive. * @returns see INTNETR0IfSetActive. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Wait for the interface to get signaled. * The interface will be signaled when is put into the receive buffer. * @returns VBox status code. * @param pIntNet The instance handle. * @param hIf The interface handle. * @param pSession The caller's session. * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be * used if indefinite wait is desired. * Get and validate essential handles. Log((
"INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
Log((
"INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
* It is tempting to check if there is data to be read here, * but the problem with such an approach is that it will cause * one unnecessary supervisor->user->supervisor trip. There is * already a slight risk for such, so no need to increase it. * Increment the number of waiters before starting the wait. * Upon wakeup we must assert reality, checking that we're not * already destroyed or in the process of being destroyed. This * code must be aligned with the waiting code in intnetR0IfDestruct. Log4((
"INTNETR0IfWait: returns %Rrc\n",
rc));
* VMMR0 request wrapper for INTNETR0IfWait. * @returns see INTNETR0IfWait. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * @returns VBox status code. * @param pIntNet The instance handle. * @param hIf The interface handle. * @param pSession The caller's session. * Validate and free the handle. /* mark the handle as freed so intnetR0IfDestruct won't free it again. */ * Release the references to the interface object (handle + free lookup). * But signal the event semaphore first so any waiter holding a reference * will wake up too (he'll see hIf == invalid and return correctly). LogFlow((
"INTNETR0IfClose: returns %Rrc\n",
rc));
* VMMR0 request wrapper for INTNETR0IfCloseReq. * @returns see INTNETR0IfClose. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Interface destructor callback. * This is called for reference counted objectes when the count reaches 0. * @param pvObj The object pointer. * @param pvUser1 Pointer to the interface. * @param pvUser2 Pointer to the INTNET instance data. * Mark the interface as being destroyed so the waiter * can behave appropriately (theoretical case). * Delete the interface handle so the object no longer can be used. * (Can happen if the client didn't close its session.) * If we've got a network deactivate and unlink ourselves from it. * Because of cleanup order we might be an orphan now. * Release our reference to the network. * Wakeup anyone waiting on this interface. * We *must* make sure they have woken up properly and realized * that the interface is no longer valid. * Unmap and Free the default buffer. * Creates a new network interface. * The call must have opened the network for the new interface * and is responsible for closing it on failure. On success * it must leave the network opened so the interface destructor * @returns VBox status code. * @param pNetwork The network. * @param pSession The session handle. * @param cbSend The size of the send buffer. * @param cbRecv The size of the receive buffer. * @param phIf Where to store the interface handle. LogFlow((
"intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
* Allocate and initialize the interface structure. //pIf->fPromiscuous = false; //pIf->fDestroying = false; //pIf->pIntBufR3 = NIL_RTR3PTR; //pIf->pIntBufDefault = 0; //pIf->pIntBufDefaultR3 = NIL_RTR3PTR; //pIf->aAddrCache[kIntNetAddrType_Invalid] = {0}; //pIf->aAddrCache[kIntNetAddrType_IPv4].pbEntries = NULL; //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntries = 0; //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntriesAlloc = 0; //pIf->aAddrCache[kIntNetAddrType_IPv6].pbEntries = NULL; //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntries = 0; //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntriesAlloc = 0; //pIf->aAddrCache[kIntNetAddrType_IPX].pbEntries = NULL; //pIf->aAddrCache[kIntNetAddrType_IPX].cEntries = 0; //pIf->aAddrCache[kIntNetAddrType_IPX].cEntriesAlloc = 0; * Create the default buffer. /** @todo adjust with minimums and apply defaults here. */ /* receive ring buffer. */ * Link the interface to the network. * Register the interface with the session. Log((
"intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
LogFlow((
"intnetR0NetworkCreateIf: returns %Rrc\n",
rc));
LogFlow((
"intnetR0NetworkCreateIf: returns %Rrc\n",
rc));
/* wrapper callback declarations */ #
error "UNSUPPORTED (RT_WITH_W64_UNWIND_HACK)"/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */ AssertMsgFailed((
"Not implemented because it wasn't required on Darwin\n"));
/** @copydoc INTNETTRUNKSWPORT::pfnRecv */ * Lock the network and send the frame to it. fRc =
false;
/* don't drop it */ /** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */ /** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */ * Retain the trunk interface. * @returns pThis if retained. * @param pThis The trunk. * Release the trunk interface. * @param pThis The trunk. * Takes the out-bound trunk lock. * This will ensure that pIfPort is valid. * @returns success indicator. * @param pThis The trunk. * Releases the out-bound trunk lock. * @param pThis The trunk. * Activates the trunk interface. * @param pThis The trunk. * @param fActive What to do with it. * Shutdown the trunk interface. * @param pThis The trunk. * @param pNetworks The network. * @remarks The caller must *NOT* hold the network lock. The global * The interface has already been deactivated, we just to wait for * it to become idle before we can disconnect and release it. /* wait in portions so we can complain ever now an then. */ LogRel((
"intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
LogRel((
"intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
LogRel((
"intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
/* disconnect & release it. */ * Creates the trunk connection (if any). * @returns VBox status code. * @param pNetwork The newly created network. * @param pSession The session handle. * The 'None' case, simple. /* Can't happen, but makes GCC happy. */ * Translate enum to component factory name. * Allocate the trunk interface. //pTrunkIF->pIfPort = NULL; //pTrunkIF->fPhysSG = false; //pTrunkIF->fPromiscuousWire = false; #
ifdef IN_RING0 /* (testcase is ring-3) */ * Query the factory we want, then use it create and connect the trunk. Log((
"intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s%s Network=%s\n",
LogFlow((
"intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
* Close a network which was opened/created using intnetR0OpenNetwork()/intnetR0CreateNetwork(). * @param pNetwork The network to close. * @param pSession The session handle. LogFlow((
"intnetR0NetworkClose: return %Rrc\n",
rc));
* Object destructor callback. * This is called for reference counted objectes when the count reaches 0. * @param pvObj The object pointer. * @param pvUser1 Pointer to the network. * @param pvUser2 Pointer to the INTNET instance data. * Deactivate the trunk connection first (if any). * Note that it needn't be in the list if we failed during creation. * Because of the undefined order of the per session object dereferencing when closing a session, * we have to handle the case where the network is destroyed before the interfaces. We'll * deal with this by simply orphaning the interfaces. /* Grab and zap the trunk pointer before leaving the mutex. */ * If there is a trunk, delete it. * Note that this may tak a while if we're unlucky... /* release the create/destroy sem. (can be done before trunk destruction.) */ * Opens an existing network. * @returns VBox status code. * @param pIntNet The instance data. * @param pSession The current session. * @param pszNetwork The network name. This has a valid length. * @param enmTrunkType The trunk type. * @param pszTrunk The trunk name. Its meaning is specfic to the type. * @param fFlags Flags, see INTNET_OPEN_FLAGS_*. * @param ppNetwork Where to store the pointer to the network on success. LogFlow((
"intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
/* just pro forma validation, the caller is internal. */ * Search networks by name. * Found the network, now check that we have the same ideas * about the trunk setup and security. * Increment the reference and check that the session * can access this network. LogFlow((
"intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
* The call must own the INTNET::FastMutex and has already attempted * opening the network and found it to be non-existing. * @returns VBox status code. * @param pIntNet The instance data. * @param pSession The session handle. * @param pszNetwork The name of the network. This must be at least one character long and no longer * than the INTNETNETWORK::szName. * @param enmTrunkType The trunk type. * @param pszTrunk The trunk name. Its meaning is specfic to the type. * @param fFlags Flags, see INTNET_OPEN_FLAGS_*. * @param ppNetwork Where to store the network. In the case of failure whatever is returned * here should be dereferenced outside the INTNET::FastMutex. LogFlow((
"intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
/* just pro forma validation, the caller is internal. */ * Allocate and initialize. * Register the object in the current session and link it into the network list. * Check if the current session is actually allowed to create and open * the network. It is possible to implement network name based policies * and these must be checked now. SUPR0ObjRegister does no such checks. LogFlow((
"intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n",
pNew));
* We unlink it here so it cannot be opened when the caller leaves * INTNET::FastMutex before dereferencing it. LogFlow((
"intnetR0CreateNetwork: returns %Rrc\n",
rc));
LogFlow((
"intnetR0CreateNetwork: returns %Rrc\n",
rc));
* Opens a network interface and connects it to the specified network. * @returns VBox status code. * @param pIntNet The internal network instance. * @param pSession The session handle. * @param pszNetwork The network name. * @param enmTrunkType The trunk type. * @param pszTrunk The trunk name. Its meaning is specfic to the type. * @param fFlags Flags, see INTNET_OPEN_FLAGS_*. * @param fRestrictAccess Whether new participants should be subjected to access check or not. * @param cbSend The send buffer size. * @param cbRecv The receive buffer size. * @param phIf Where to store the handle to the network interface. LogFlow((
"INTNETR0Open: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
pIntNet,
pSession,
pszNetwork,
pszNetwork,
pszTrunk,
pszTrunk,
enmTrunkType,
fFlags,
cbSend,
cbRecv,
phIf));
* Try open / create the network and create an interface on it for the caller to use. * Note that because of the destructors grabbing INTNET::FastMutex and us being required * to own this semaphore for the entire network opening / creation and interface creation * sequence, intnetR0CreateNetwork will have to defer the network cleanup to us on failure. * VMMR0 request wrapper for GMMR0MapUnmapChunk. * @returns see GMMR0MapUnmapChunk. * @param pIntNet The internal networking instance. * @param pSession The caller's session. * @param pReq The request packet. * Destroys an instance of the Ring-0 internal networking service. * @param pIntNet Pointer to the instance data. * There is not supposed to be any networks hanging around at this time. /** @todo does it make sense to have a deleter here? */ * Create an instance of the Ring-0 internal networking service. * @returns VBox status code. * @param ppIntNet Where to store the instance pointer. //pIntNet->pNetworks = NULL; LogFlow((
"INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n",
pIntNet));
LogFlow((
"INTNETR0Create: returns %Rrc\n",
rc));