SrvIntNetR0.cpp revision c7ff622115966b69b482bd2896662e40d823b22f
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * Internal networking - The ring 0 service.
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * Copyright (C) 2006-2010 Oracle Corporation
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * available from http://www.virtualbox.org. This file is free software;
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * General Public License (GPL) as published by the Free Software
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
cf25f919d659bf00f73e1551230cd6165961061dvboxsync/*******************************************************************************
cf25f919d659bf00f73e1551230cd6165961061dvboxsync* Header Files *
cf25f919d659bf00f73e1551230cd6165961061dvboxsync*******************************************************************************/
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/*******************************************************************************
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync* Defined Constants And Macros *
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync*******************************************************************************/
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** @def INTNET_WITH_DHCP_SNOOPING
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync/** The maximum number of interface in a network. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync/** The number of entries to grow the destination tables with. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync/** The wakeup bit in the INTNETIF::cBusy and INTNETRUNKIF::cBusy counters. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync/*******************************************************************************
cf25f919d659bf00f73e1551230cd6165961061dvboxsync* Structures and Typedefs *
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync*******************************************************************************/
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * MAC address lookup table entry.
cf25f919d659bf00f73e1551230cd6165961061dvboxsync /** The MAC address of this entry. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync /** Is it promiscuous. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync /** Is it active.
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * We ignore the entry if this is clear and may end up sending packets addressed
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * to this interface onto the trunk. The reasoning for this is that this could
cf25f919d659bf00f73e1551230cd6165961061dvboxsync * be the interface of a VM that just has been teleported to a different host. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync /** Pointer to the network interface. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync/** Pointer to a MAC address lookup table entry. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * MAC address lookup table.
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * @todo Having this in a separate structure didn't work out as well as it
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * should. Consider merging it into INTNETNETWORK.
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsynctypedef struct INTNETMACTAB
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** The current number of entries. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** The number of entries we've allocated space for. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** Table entries. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** The host MAC address (reported). */
aef51041e0fe31e8ea903dd7e67fe12cef645654vboxsync /** The host promisucous setting (reported). */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** Whether the host is active. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** Whether the wire is promiscuous (config). */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Whether the wire is active. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Pointer to the the trunk interface. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to a MAC address . */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * Destination table.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsynctypedef struct INTNETDSTTAB
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The trunk destinations. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Pointer to the trunk interface (referenced) if fTrunkDst is non-zero. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The number of destination interfaces. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The interfaces (referenced). Variable sized array. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The destination interface. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Whether to replace the destination MAC address.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * This is used when sharing MAC address with the host on the wire(less). */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to a destination table. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to a const destination table. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Network layer address type. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The invalid 0 entry. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** IP version 4. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** IP version 6. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** IPX. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The end of the valid values. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The usual 32-bit hack. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to a network layer address type. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * Address and type.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsynctypedef struct INTNETADDR
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The address type. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The address. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to an address. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to a const address. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * Address cache for a specific network layer.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Pointer to the table of addresses. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The number of valid address entries. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The number of allocated address entries. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The address size. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The size of an entry. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to an address cache. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync/** Pointer to a const address cache. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * A network interface.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * Unless explicitly stated, all members are protect by the network semaphore.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsynctypedef struct INTNETIF
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** The MAC address.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * This is shadowed by INTNETMACTABENTRY::MacAddr. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Set if the INTNET::MacAddr member has been explicitly set. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Set if the interface is in promiscuous mode.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * This is shadowed by INTNETMACTABENTRY::fPromiscuous. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Whether the interface is active or not.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * This is shadowed by INTNETMACTABENTRY::fActive. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Whether someone is currently in the destructor or has indicated that
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * the end is nigh by means of IntNetR0IfAbortWait. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync bool volatile fDestroying;
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Number of yields done to try make the interface read pending data.
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * We will stop yielding when this reaches a threshold assuming that the VM is
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * paused or that it simply isn't worth all the delay. It is cleared when a
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync * successful send has been done. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Pointer to the current exchange buffer (ring-0). */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Pointer to ring-3 mapping of the current exchange buffer. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** Pointer to the default exchange buffer for the interface. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** Pointer to ring-3 mapping of the default exchange buffer. */
25be0b1daccd180b98ac7bd0a81e2acb75db6146vboxsync /** Event semaphore which a receiver/consumer thread will sleep on while
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * waiting for data to arrive. */
25be0b1daccd180b98ac7bd0a81e2acb75db6146vboxsync /** Number of threads sleeping on the event semaphore. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** The interface handle.
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * should return with the appropriate error condition. */
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync /** Pointer to the network this interface is connected to.
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * This is protected by the INTNET::hMtxCreateOpenDestroy. */
25be0b1daccd180b98ac7bd0a81e2acb75db6146vboxsync /** The session this interface is associated with. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** The SUPR0 object id. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** The network layer address cache. (Indexed by type, 0 entry isn't used.)
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * This is protected by the address spinlock of the network. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Spinlock protecting the input (producer) side of the receive ring. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Busy count for tracking destination table references and active sends.
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * Usually incremented while owning the switch table spinlock. The 30th bit
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * is used to indicate wakeup. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** The preallocated destination table.
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * This is NULL when it's in use as a precaution against unserialized
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * transmitting. This is grown when new interfaces are added to the network. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Pointer to the trunk's per interface data. Can be NULL. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync/** Pointer to an internal network interface. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * A trunk interface.
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** The port interface we present to the component. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** The port interface we get from the component. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Pointer to the network we're connect to.
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * This may be NULL if we're orphaned? */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** The current MAC address for the interface. (reported)
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync * Updated while owning the switch table spinlock. */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Whether to supply physical addresses with the outbound SGs. (reported) */
54d13d84f69d613c39bf7672d4095c8d863176efvboxsync /** Explicit alignment. */
daf1b2aee694fc8aca9e056e825b3359170ecf37vboxsync /** Busy count for tracking destination table references and active sends.
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * Usually incremented while owning the switch table spinlock. The 30th bit
b0ba81213264a60e4207772639b53f5f4cbe4cc7vboxsync * is used to indicate wakeup. */
a0cebf7fbe7defe88df7dcb64e51af63480b7ab0vboxsync /** Mask of destinations that pfnXmit cope with disabled preemption for. */
cf25f919d659bf00f73e1551230cd6165961061dvboxsync /** The GSO capabilities of the wire destination. (reported) */
typedef struct INTNETNETWORK
void *pvObj;
typedef struct INTNET
} INTNET;
unsigned iSeg = 0;
iSeg++;
} while (cb > 0);
unsigned iSeg = 0;
iSeg++;
} while (cb > 0);
if (cCurBusy == 0)
//AssertMsg(RT_SUCCESS(rc2), ("rc=%Rrc *pcBusy=%#x (%#x)\n", rc2, ASMAtomicReadU32(pcBusy), cCurBusy ));
&& pNetwork))
return VINF_SUCCESS;
static DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
return VINF_SUCCESS;
DECLINLINE(PINTNETMACTABENTRY) intnetR0NetworkFindMacAddrEntry(PINTNETNETWORK pNetwork, PINTNETIF pIf)
while (iIf-- > 0)
return NULL;
switch (enmType)
default: AssertFailedReturn(0);
DECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
switch (cbAddr)
AssertFailedReturn(false);
static int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
if (RT_UNLIKELY( i > 0
if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
#ifdef LOG_ENABLED
switch (enmAddrType)
case kIntNetAddrType_IPv4:
pIf->hIf, &pIf->MacAddr, iEntry, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
if (RT_UNLIKELY(i >= 0))
DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
while (iIf--)
if (RT_UNLIKELY(i >= 0))
DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
while (iIf--)
if (RT_UNLIKELY(i >= 0))
DECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
while (iIf--)
return pIf;
return NULL;
static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, const char *pszMsg)
memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
#ifdef LOG_ENABLED
switch (enmAddrType)
case kIntNetAddrType_IPv4:
pIf->hIf, &pIf->MacAddr, pCache->cEntries, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
if (RT_LIKELY( i > 0
&& intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))) ))
static int intnetR0IfAddrCacheInit(PINTNETADDRCACHE pCache, INTNETADDRTYPE enmAddrType, bool fEnabled)
if (fEnabled)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
while (iIfMac-- > 0)
if ( fExact
static INTNETSWDECISION intnetR0NetworkSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
while (iIfMac-- > 0)
if ( fExact
if ( fExact
static INTNETSWDECISION intnetR0NetworkSwitchBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
while (iIfMac-- > 0)
return INTNETSWDECISION_BROADCAST;
static INTNETSWDECISION intnetR0NetworkSwitchTrunkAndPromisc(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
while (iIfMac-- > 0)
static INTNETSWDECISION intnetR0NetworkSwitchTrunk(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
if ( pOld
ppDstTab++)
if (pvOld)
if (pvOld)
if (paNew)
return rc;
#ifdef INTNET_WITH_DHCP_SNOOPING
static void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
#ifdef LOG_ENABLED
switch (MsgType)
Log6(("DHCP msg: %d (%s) client %.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d\n", MsgType, pszType, &pDhcp->bp_chaddr,
pDhcp->bp_yiaddr.au8[0], pDhcp->bp_yiaddr.au8[1], pDhcp->bp_yiaddr.au8[2], pDhcp->bp_yiaddr.au8[3]));
switch (MsgType)
case RTNET_DHCP_MT_REQUEST:
case RTNET_DHCP_MT_ACK:
while (iIf-- > 0)
if (!pMatchingIf)
if (pMatchingIf)
case RTNET_DHCP_MT_RELEASE:
while (iIf-- > 0)
if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fChecksum*/))
intnetR0NetworkSnoopDhcp(pNetwork, pIpHdr, (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr), cbPacket - cbIpHdr);
#ifdef INTNET_WITH_DHCP_SNOOPING
switch (EtherType)
case RTNET_ETHERTYPE_IPV4:
uint8_t b;
Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR) + cbIpHdr);
if ( b != RTNETIPV4_PORT_BOOTPS
&& b != RTNETIPV4_PORT_BOOTPC)
if ( b != RTNETIPV4_PORT_BOOTPS
&& b != RTNETIPV4_PORT_BOOTPC)
if (b == SrcPort)
case RTNET_ETHERTYPE_IPV6:
case RTNET_ETHERTYPE_ARP:
static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket, bool fGso)
bool fValidatedIpHdr = false;
&& intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
fValidatedIpHdr = true;
#ifdef INTNET_WITH_DHCP_SNOOPING
if ( fValidatedIpHdr
static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, bool fGso, uint16_t *pfSgFlags)
switch (EtherType)
case RTNET_ETHERTYPE_IPV4:
case RTNET_ETHERTYPE_IPV6:
intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso, pfSgFlags);
case RTNET_ETHERTYPE_IPX_1:
case RTNET_ETHERTYPE_IPX_2:
case RTNET_ETHERTYPE_IPX_3:
case RTNET_ETHERTYPE_ARP:
int rc;
if (pNewDstMac)
return VINF_SUCCESS;
return rc;
while (--cYields > 0)
static int intnetR0TrunkIfSendGsoFallback(PINTNETTRUNKIF pThis, PINTNETIF pIfSender, PINTNETSG pSG, uint32_t fDst)
uint32_t offSegPayload = PDMNetGsoCarveSegment(&pSG->GsoCtx, (uint8_t *)pSG->aSegs[0].pv, pSG->cbTotal, iSeg, cSegs,
return rc;
return VINF_SUCCESS;
DECLINLINE(bool) intnetR0TrunkIfCanHandleGsoFrame(PINTNETTRUNKIF pThis, PINTNETSG pSG, uint32_t fDst)
int rc;
static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
if (pIf)
static void intnetR0NetworkEditDhcpFromIntNet(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN))
if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fCheckSum*/))
switch (MsgType)
case RTNET_DHCP_MT_DISCOVER:
case RTNET_DHCP_MT_REQUEST:
Log6(("intnetR0NetworkEditDhcpFromIntNet: Setting broadcast flag in DHCP %#x, previously %x\n", MsgType, pDhcp->bp_flags));
intnetR0SgWritePart(pSG, (uintptr_t)&pDhcp->bp_flags - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR), sizeof(uFlags), &uFlags);
intnetR0SgWritePart(pSG, (uintptr_t)&pUdpHdr->uh_sum - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR), sizeof(pUdpHdr->uh_sum), &uChecksum);
DECLINLINE(bool) intnetR0NetworkIsContextOk(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCINTNETDSTTAB pDstTab)
if (!pIfSender)
if (!fTrunkDst)
if (fSrc)
return fRc;
return INTNETSWDECISION_BAD_CONTEXT;
if ( !fSrc
if (fSrc)
#ifdef INTNET_WITH_DHCP_SNOOPING
&& pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchUnicast(PINTNETNETWORK pNetwork, PINTNETSG pSG,
case RTNET_ETHERTYPE_IPV4:
if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
case RTNET_ETHERTYPE_IPV6
if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
case RTNET_ETHERTYPE_IPX_1:
case RTNET_ETHERTYPE_IPX_2:
case RTNET_ETHERTYPE_IPX_3:
if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPX, ipx_dstnet), sizeof(Addr.IPX), &Addr)))
case RTNET_ETHERTYPE_ARP:
return intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, INTNETTRUNKDIR_WIRE, NULL, pSG, pEthHdr, pDstTab);
#ifdef INTNET_WITH_DHCP_SNOOPING
&& pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
return enmSwDecision;
while (iIf-- > 0)
static void intnetR0NetworkDeliver(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab, PINTNETSG pSG, PINTNETIF pIfSender)
while (iIf-- > 0)
if (pIfSender)
static INTNETSWDECISION intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
return INTNETSWDECISION_INVALID;
return INTNETSWDECISION_INVALID;
if (pIfEntry)
enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, fSrc, pIfSender, pSG, &EthHdr, pDstTab);
return enmSwDecision;
if (!pIf)
return VERR_INVALID_HANDLE;
intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, false /*fGso*/, (uint16_t *)&Sg.fFlags);
return rc;
return VERR_INVALID_PARAMETER;
LogFlow(("IntNetR0IfGetBufferPtrs: hIf=%RX32 ppRing3Buf=%p ppRing0Buf=%p\n", hIf, ppRing3Buf, ppRing0Buf));
if (ppRing3Buf)
*ppRing3Buf = 0;
if (ppRing0Buf)
*ppRing0Buf = 0;
if (!pIf)
return VERR_INVALID_HANDLE;
if (ppRing3Buf)
if (ppRing0Buf)
return rc;
INTNETR0DECL(int) IntNetR0IfGetBufferPtrsReq(PSUPDRVSESSION pSession, PINTNETIFGETBUFFERPTRSREQ pReq)
return VERR_INVALID_PARAMETER;
if (!pIf)
return VERR_INVALID_HANDLE;
/** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
return VERR_NOT_IMPLEMENTED;
INTNETR0DECL(int) IntNetR0IfSetPromiscuousMode(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
if (!pIf)
return VERR_INVALID_HANDLE;
if (pNetwork)
return rc;
INTNETR0DECL(int) IntNetR0IfSetPromiscuousModeReq(PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
return VERR_INVALID_PARAMETER;
INTNETR0DECL(int) IntNetR0IfSetMacAddress(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCRTMAC pMac)
if (!pIf)
return VERR_INVALID_HANDLE;
if (pNetwork)
if (pTrunk)
if (pTrunk)
if (pIfPort)
return rc;
INTNETR0DECL(int) IntNetR0IfSetMacAddressReq(PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
return VERR_INVALID_PARAMETER;
if (fActive)
if (pTrunk)
pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, fActive ? INTNETTRUNKIFSTATE_ACTIVE : INTNETTRUNKIFSTATE_INACTIVE);
return VINF_SUCCESS;
if (!pIf)
return VERR_INVALID_HANDLE;
int rc;
if (pNetwork)
return rc;
return VERR_INVALID_PARAMETER;
if (!pIf)
return VERR_INVALID_HANDLE;
|| fDestroying
return VERR_SEM_DESTROYED;
return rc;
return VERR_INVALID_PARAMETER;
INTNETR0DECL(int) IntNetR0IfAbortWait(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fNoMoreWaits)
if (!pIf)
return VERR_INVALID_HANDLE;
|| fDestroying
return VERR_SEM_DESTROYED;
if (fNoMoreWaits)
while (cSleepers-- > 0)
return VINF_SUCCESS;
return VERR_INVALID_PARAMETER;
if (!pIf)
return VERR_INVALID_HANDLE;
return rc;
return VERR_INVALID_PARAMETER;
if (pNetwork)
while (iIf-- > 0)
static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv,
return rc;
if (!pIf)
return VERR_NO_MEMORY;
rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
if (pTrunk)
if (pTrunk)
return VINF_SUCCESS;
return rc;
return rc;
static DECLCALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
static DECLCALLBACK(void) intnetR0TrunkIfPortReportMacAddress(PINTNETTRUNKSWPORT pSwitchPort, PCRTMAC pMacAddr)
if (pNetwork)
static DECLCALLBACK(void) intnetR0TrunkIfPortReportPromiscuousMode(PINTNETTRUNKSWPORT pSwitchPort, bool fPromiscuous)
if (pNetwork)
static DECLCALLBACK(void) intnetR0TrunkIfPortReportNoPreemptDsts(PINTNETTRUNKSWPORT pSwitchPort, uint32_t fNoPreemptDsts)
return INTNETSWDECISION_BROADCAST;
static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, void *pvIf, PINTNETSG pSG, uint32_t fSrc)
unsigned iDstTab = 0;
if (fIntCtx)
while (iDstTab-- > 0)
if (pDstTab)
if (!pDstTab)
if (pDstTab)
AssertMsg(enmSwDecision != INTNETSWDECISION_BAD_CONTEXT, ("fSrc=%#x fTrunkDst=%#x hdr=%.14Rhxs\n", fSrc, pDstTab->fTrunkDst, pSG->aSegs[0].pv));
while (iDstTab-- > 0)
return fRc;
static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
return pThis;
return NULL;
if (!pThis)
if (pIfPort)
const char *pszName;
case kIntNetTrunkType_None:
return VINF_SUCCESS;
return VERR_NOT_IMPLEMENTED;
case kIntNetTrunkType_NetFlt:
case kIntNetTrunkType_NetAdp:
case kIntNetTrunkType_SrvNat:
PINTNETTRUNKIF pTrunk = (PINTNETTRUNKIF)RTMemAllocZ(RT_OFFSETOF(INTNETTRUNKIF, apIntDstTabs[cCpus]));
if (!pTrunk)
return VERR_NO_MEMORY;
pNetwork->MacTab.fWirePromiscuous = false; /** @todo !!(fFlags & INTNET_OPEN_FLAGS_PROMISC_TRUNK_WIRE); */
rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
pszName, pNetwork->szTrunk, pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE ? " shared-mac" : "", pNetwork->szName));
return VINF_SUCCESS;
return rc;
Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
if (pTrunk)
while (iIf-- > 0)
if (pTrunk)
while (iIf-- > 0)
if (pTrunk)
static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
while (pCur)
int rc;
return rc;
return VERR_NOT_FOUND;
static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
if (!pNetwork)
return VERR_NO_MEMORY;
pNetwork->MacTab.paEntries = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * pNetwork->MacTab.cEntriesAllocated);
pNetwork->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNetwork, pIntNet);
return VINF_SUCCESS;
return rc;
return rc;
LogFlow(("IntNetR0Open: pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
if (pszTrunk)
switch (enmTrunkType)
case kIntNetTrunkType_None:
if (*pszTrunk)
return VERR_INVALID_PARAMETER;
case kIntNetTrunkType_NetFlt:
case kIntNetTrunkType_NetAdp:
if (!*pszTrunk)
return VERR_INVALID_PARAMETER;
return VERR_NOT_IMPLEMENTED;
return rc;
rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
return rc;
return VERR_INVALID_PARAMETER;
if (!pIntNet)
cNetworks++;
return cNetworks;
if (!pIntNet)
if (pIntNet)
rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
return VINF_SUCCESS;
return rc;