SrvIntNetR0.cpp revision 93793641488de08fd9526ca9707da8d041d552d5
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/* $Id$ */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** @file
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync * Internal networking - The ring 0 service.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Copyright (C) 2006-2008 Sun Microsystems, Inc.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * available from http://www.virtualbox.org. This file is free software;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * you can redistribute it and/or modify it under the terms of the GNU
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * General Public License (GPL) as published by the Free Software
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * additional information or have any questions.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*******************************************************************************
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync* Header Files *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync*******************************************************************************/
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#define LOG_GROUP LOG_GROUP_SRV_INTNET
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/intnet.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/sup.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/pdm.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/log.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/asm.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/alloc.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/semaphore.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/spinlock.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/thread.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/assert.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/string.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/time.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/handletable.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/net.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*******************************************************************************
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync* Defined Constants And Macros *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync*******************************************************************************/
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** @def INTNET_WITH_DHCP_SNOOPING
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync#define INTNET_WITH_DHCP_SNOOPING
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/*******************************************************************************
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync* Structures and Typedefs *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync*******************************************************************************/
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef enum INTNETADDRTYPE
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
c889bbab784ba8552102ce776b6c67b982017861vboxsync /** The invalid 0 entry. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync kIntNetAddrType_Invalid = 0,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** IP version 4. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync kIntNetAddrType_IPv4,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** IP version 6. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync kIntNetAddrType_IPv6,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** IPX. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync kIntNetAddrType_IPX,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The end of the valid values. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync kIntNetAddrType_End,
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The usual 32-bit hack. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync kIntNetAddrType_32BitHack = 0x7fffffff
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync} INTNETADDRTYPE;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync/** Pointer to a network layer address type. */
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsynctypedef INTNETADDRTYPE *PINTNETADDRTYPE;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync/**
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * Address and type.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsynctypedef struct INTNETADDR
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /** The address type. */
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync INTNETADDRTYPE enmType;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /** The address. */
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync RTNETADDRU Addr;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync} INTNETADDR;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync/** Pointer to an address. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsynctypedef INTNETADDR *PINTNETADDR;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** Pointer to a const address. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsynctypedef INTNETADDR const *PCINTNETADDR;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Address cache for a specific network layer.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsynctypedef struct INTNETADDRCACHE
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Pointer to the table of addresses. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint8_t *pbEntries;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The number of valid address entries. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint8_t cEntries;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The number of allocated address entries. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint8_t cEntriesAlloc;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The address size. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint8_t cbAddress;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The size of an entry. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint8_t cbEntry;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync} INTNETADDRCACHE;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** Pointer to an address cache. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef INTNETADDRCACHE *PINTNETADDRCACHE;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** Pointer to a const address cache. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * A network interface.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Unless explicitly stated, all members are protect by the network semaphore.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef struct INTNETIF
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Pointer to the next interface.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * This is protected by the INTNET::FastMutex. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync struct INTNETIF *pNext;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The current MAC address for the interface. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync RTMAC Mac;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Set if the INTNET::Mac member is valid. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync bool fMacSet;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Set if the interface is in promiscuous mode.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * In promiscuous mode the interface will receive all packages except the one it's sending. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync bool fPromiscuous;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Whether the interface is active or not. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync bool fActive;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Whether someone is currently in the destructor. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync bool volatile fDestroying;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** Number of yields done to try make the interface read pending data.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * We will stop yeilding when this reaches a threshold assuming that the VM is paused or
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * that it simply isn't worth all the delay. It is cleared when a successful send has been done.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync uint32_t cYields;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** Pointer to the current exchange buffer (ring-0). */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync PINTNETBUF pIntBuf;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** Pointer to ring-3 mapping of the current exchange buffer. */
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync R3PTRTYPE(PINTNETBUF) pIntBufR3;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Pointer to the default exchange buffer for the interface. */
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync PINTNETBUF pIntBufDefault;
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync /** Pointer to ring-3 mapping of the default exchange buffer. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync RTSEMEVENT volatile Event;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Number of threads sleeping on the Event semaphore. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint32_t cSleepers;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The interface handle.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * should return with the appropriate error condition. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync INTNETIFHANDLE volatile hIf;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Pointer to the network this interface is connected to.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * This is protected by the INTNET::FastMutex. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync struct INTNETNETWORK *pNetwork;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The session this interface is associated with. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync PSUPDRVSESSION pSession;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The SUPR0 object id. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync void *pvObj;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The network layer address cache. (Indexed by type, 0 entry isn't used.) */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync} INTNETIF;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/** Pointer to an internal network interface. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsynctypedef INTNETIF *PINTNETIF;
445661c86e95894713da707c6c9787b7507dfce6vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
445661c86e95894713da707c6c9787b7507dfce6vboxsync * A trunk interface.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsynctypedef struct INTNETTRUNKIF
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The port interface we present to the component. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync INTNETTRUNKSWPORT SwitchPort;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The port interface we get from the component. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync PINTNETTRUNKIFPORT pIfPort;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The trunk mutex that serializes all calls <b>to</b> the component. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync RTSEMFASTMUTEX FastMutex;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Pointer to the network we're connect to.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * This may be NULL if we're orphaned? */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync struct INTNETNETWORK *pNetwork;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** The cached MAC address of the interface the trunk is attached to.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * This is for the situations where we cannot take the out-bound
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * semaphore (the recv case) but need to make frame edits (ARP). */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync RTMAC CachedMac;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Whether to supply physical addresses with the outbound SGs. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync bool volatile fPhysSG;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Set if the 'wire' is in promiscuous mode.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * The state of the 'host' is queried each time. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync bool fPromiscuousWire;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync} INTNETTRUNKIF;
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/** Pointer to a trunk interface. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsynctypedef INTNETTRUNKIF *PINTNETTRUNKIF;
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
c889bbab784ba8552102ce776b6c67b982017861vboxsync
c889bbab784ba8552102ce776b6c67b982017861vboxsync/**
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * Internal representation of a network.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsynctypedef struct INTNETNETWORK
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** The Next network in the chain.
c5d2523548cc57504b829f53f1362b848a84542cvboxsync * This is protected by the INTNET::FastMutex. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync struct INTNETNETWORK *pNext;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** List of interfaces connected to the network.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * This is protected by the INTNET::FastMutex. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync PINTNETIF pIFs;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Pointer to the trunk interface.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Can be NULL if there is no trunk connection. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync PINTNETTRUNKIF pTrunkIF;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The network mutex.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * It protects everything dealing with this network. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync RTSEMFASTMUTEX FastMutex;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /** Pointer to the instance data. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync struct INTNET *pIntNet;
234af146205f61c4aa0be736abb06601a89facb8vboxsync /** The SUPR0 object id. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync void *pvObj;
234af146205f61c4aa0be736abb06601a89facb8vboxsync /** Pointer to the temporary buffer that is used when snooping fragmented packets.
234af146205f61c4aa0be736abb06601a89facb8vboxsync * This is allocated after this structure if we're sharing the MAC address with
234af146205f61c4aa0be736abb06601a89facb8vboxsync * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundrary. */
234af146205f61c4aa0be736abb06601a89facb8vboxsync uint8_t *pbTmp;
234af146205f61c4aa0be736abb06601a89facb8vboxsync /** Network creation flags (INTNET_OPEN_FLAGS_*). */
234af146205f61c4aa0be736abb06601a89facb8vboxsync uint32_t fFlags;
234af146205f61c4aa0be736abb06601a89facb8vboxsync /** The number of active interfaces (excluding the trunk). */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint32_t cActiveIFs;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The length of the network name. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint8_t cchName;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The network name. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync char szName[INTNET_MAX_NETWORK_NAME];
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The trunk type. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync INTNETTRUNKTYPE enmTrunkType;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The trunk name. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync char szTrunk[INTNET_MAX_TRUNK_NAME];
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync} INTNETNETWORK;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/** Pointer to an internal network. */
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsynctypedef INTNETNETWORK *PINTNETNETWORK;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** The size of the buffer INTNETNETWORK::pbTmp points at. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync#define INTNETNETWORK_TMP_SIZE 2048
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * Internal networking instance.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef struct INTNET
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Mutex protecting the network creation, opening and destruction.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * (This means all operations affecting the pNetworks list.) */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync RTSEMFASTMUTEX FastMutex;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** List of networks. Protected by INTNET::Spinlock. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync PINTNETNETWORK volatile pNetworks;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Handle table for the interfaces. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync RTHANDLETABLE hHtIfs;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync} INTNET;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/*******************************************************************************
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync* Internal Functions *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync*******************************************************************************/
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsyncstatic PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncstatic void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncstatic bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncstatic void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis);
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Initializes a scatter / gather buffer from a simple linear buffer.
1829a716128b3e2d42bcee064a15c553dbd44798vboxsync *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @returns Pointer to the start of the frame.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param pSG Pointer to the scatter / gather structure.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param pvFrame Pointer to the frame
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param cbFrame The size of the frame.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncDECLINLINE(void) intnetR0SgInitTemp(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame)
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->pvOwnerData = NULL;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->pvUserData = NULL;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->pvUserData2 = NULL;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->cbTotal = cbFrame;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->cUsers = 1;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync pSG->fFlags = INTNETSG_FLAGS_TEMP;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->cSegsAlloc = 1;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->cSegsUsed = 1;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync pSG->aSegs[0].Phys = NIL_RTHCPHYS;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync pSG->aSegs[0].pv = pvFrame;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->aSegs[0].cb = cbFrame;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync}
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Initializes a scatter / gather buffer from a internal networking packet.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @returns Pointer to the start of the frame.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param pSG Pointer to the scatter / gather structure.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param pHdr Pointer to the packet header.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @param pBuf The buffer the header is within. Only used in strict builds.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncDECLINLINE(void) intnetR0SgInitFromPkt(PINTNETSG pSG, PCINTNETHDR pPktHdr, PCINTNETBUF pBuf)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->cSegsUsed = 1;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->cbTotal = pSG->aSegs[0].cb = pPktHdr->cbFrame;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync pSG->aSegs[0].pv = INTNETHdrGetFramePtr(pPktHdr, pBuf);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pSG->aSegs[0].Phys = NIL_RTHCPHYS;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync}
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Worker for intnetR0SgWritePart that deals with the case where the
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * request doesn't fit into the first segment.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns true, unless the request or SG invalid.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param pSG The SG list to write to.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param off Where to start writing (offset into the SG).
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param cb How much to write.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pvBuf The buffer to containing the bits to write.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_UNLIKELY(off + cb > pSG->cbTotal))
234af146205f61c4aa0be736abb06601a89facb8vboxsync return false;
234af146205f61c4aa0be736abb06601a89facb8vboxsync
234af146205f61c4aa0be736abb06601a89facb8vboxsync /*
234af146205f61c4aa0be736abb06601a89facb8vboxsync * Skip ahead to the segment where off starts.
234af146205f61c4aa0be736abb06601a89facb8vboxsync */
234af146205f61c4aa0be736abb06601a89facb8vboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
234af146205f61c4aa0be736abb06601a89facb8vboxsync unsigned iSeg = 0;
234af146205f61c4aa0be736abb06601a89facb8vboxsync while (off > pSG->aSegs[iSeg].cb)
234af146205f61c4aa0be736abb06601a89facb8vboxsync {
234af146205f61c4aa0be736abb06601a89facb8vboxsync off -= pSG->aSegs[iSeg++].cb;
234af146205f61c4aa0be736abb06601a89facb8vboxsync AssertReturn(iSeg < cSegs, false);
234af146205f61c4aa0be736abb06601a89facb8vboxsync }
234af146205f61c4aa0be736abb06601a89facb8vboxsync
234af146205f61c4aa0be736abb06601a89facb8vboxsync /*
234af146205f61c4aa0be736abb06601a89facb8vboxsync * Copy the data, hoping that it's all from one segment...
234af146205f61c4aa0be736abb06601a89facb8vboxsync */
234af146205f61c4aa0be736abb06601a89facb8vboxsync uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
234af146205f61c4aa0be736abb06601a89facb8vboxsync if (cbCanCopy >= cb)
234af146205f61c4aa0be736abb06601a89facb8vboxsync memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
234af146205f61c4aa0be736abb06601a89facb8vboxsync else
234af146205f61c4aa0be736abb06601a89facb8vboxsync {
234af146205f61c4aa0be736abb06601a89facb8vboxsync /* copy the portion in the current segment. */
234af146205f61c4aa0be736abb06601a89facb8vboxsync memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync cb -= cbCanCopy;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync /* copy the portions in the other segments. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync do
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync {
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync iSeg++;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync AssertReturn(iSeg < cSegs, false);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync cb -= cbCanCopy;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync } while (cb > 0);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync }
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync return true;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
234af146205f61c4aa0be736abb06601a89facb8vboxsync * Writes to a part of an SG.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns true on success, false on failure (out of bounds).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pSG The SG list to write to.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param off Where to start writing (offset into the SG).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param cb How much to write.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pvBuf The buffer to containing the bits to write.
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncDECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync Assert(off + cb > off);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync /* The optimized case. */
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync if (RT_LIKELY( pSG->cSegsUsed == 1
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync || pSG->aSegs[0].cb >= off + cb))
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync {
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync Assert(pSG->cbTotal == pSG->aSegs[0].cb);
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync return true;
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync }
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * Reads a byte from a SG list.
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns The byte on success. 0xff on failure.
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync * @param pSG The SG list to read.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param off The offset (into the SG) off the byte.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncDECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (RT_LIKELY(pSG->aSegs[0].cb > off))
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return ((uint8_t const *)pSG->aSegs[0].pv)[off];
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync off -= pSG->aSegs[0].cb;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync {
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (pSG->aSegs[iSeg].cb > off)
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync off -= pSG->aSegs[iSeg].cb;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync }
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return false;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync}
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync/**
041d531fb5794a8a4cf6c35886d89ec25cbbdde2vboxsync * Worker for intnetR0SgReadPart that deals with the case where the
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * requested data isn't in the first segment.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync *
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @returns true, unless the SG is invalid.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pSG The SG list to read.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param off Where to start reading (offset into the SG).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param cb How much to read.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @param pvBuf The buffer to read into.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsyncstatic bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync if (RT_UNLIKELY(off + cb > pSG->cbTotal))
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return false;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /*
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * Skip ahead to the segment where off starts.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync unsigned iSeg = 0;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync while (off > pSG->aSegs[iSeg].cb)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync {
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync off -= pSG->aSegs[iSeg++].cb;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync AssertReturn(iSeg < cSegs, false);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync }
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /*
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * Copy the data, hoping that it's all from one segment...
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync if (cbCanCopy >= cb)
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync else
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync {
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /* copy the portion in the current segment. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync cb -= cbCanCopy;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /* copy the portions in the other segments. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync do
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync {
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pvBuf = (uint8_t *)pvBuf + cbCanCopy;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync iSeg++;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync AssertReturn(iSeg < cSegs, false);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync cb -= cbCanCopy;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync } while (cb > 0);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync }
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return true;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync}
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync/**
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * Reads a part of an SG into a buffer.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync *
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @returns true on success, false on failure (out of bounds).
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @param pSG The SG list to read.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @param off Where to start reading (offset into the SG).
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @param cb How much to read.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @param pvBuf The buffer to read into.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncDECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync Assert(off + cb > off);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /* The optimized case. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync if (RT_LIKELY( pSG->cSegsUsed == 1
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync || pSG->aSegs[0].cb >= off + cb))
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync {
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync Assert(pSG->cbTotal == pSG->aSegs[0].cb);
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync return true;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync }
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync}
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync/**
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * Reads an entire SG into a fittingly size buffer.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync *
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @param pSG The SG list to read.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @param pvBuf The buffer to read into (at least pSG->cbTotal in size).
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync */
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsyncDECLINLINE(void) intnetR0SgRead(PCINTNETSG pSG, void *pvBuf)
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync{
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync if (pSG->cSegsUsed == 1)
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync {
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync Assert(pSG->cbTotal == pSG->aSegs[0].cb);
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync memcpy(pvBuf, pSG->aSegs[0].pv, pSG->cbTotal);
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync }
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync else
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync {
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync uint8_t *pbDst = (uint8_t *)pvBuf;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync for (unsigned iSeg = 0; iSeg < cSegs; iSeg++)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint32_t cbSeg = pSG->aSegs[iSeg].cb;
c5d2523548cc57504b829f53f1362b848a84542cvboxsync Assert(cbSeg <= pSG->cbTotal && (uintptr_t)(pbDst - (uint8_t *)pvBuf) + cbSeg <= pSG->cbTotal);
c5d2523548cc57504b829f53f1362b848a84542cvboxsync memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pbDst += cbSeg;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync }
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync }
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync}
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
c5d2523548cc57504b829f53f1362b848a84542cvboxsync
c5d2523548cc57504b829f53f1362b848a84542cvboxsync
c5d2523548cc57504b829f53f1362b848a84542cvboxsync
e4527e0a08e2d635a679ae2947d42195f30a2ce2vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
e4527e0a08e2d635a679ae2947d42195f30a2ce2vboxsync
8929a16e87a515b7071399479548158b8c5fbdd2vboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Retain an interface.
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns VBox status code, can assume success in most situations.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pIf The interface instance.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pSession The current session.
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncDECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
e4527e0a08e2d635a679ae2947d42195f30a2ce2vboxsync int rc = SUPR0ObjAddRefEx(pIf->pvObj, pSession, true /* fNoBlocking */);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync AssertRCReturn(rc, rc);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return VINF_SUCCESS;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Release an interface previously retained by intnetR0IfRetain or
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * by handle lookup/freeing.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @returns true if destroyed, false if not.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param pIf The interface instance.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pSession The current session.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync */
db3d025f28c59aececbbda4174fa513496c89b2bvboxsyncDECLINLINE(bool) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync AssertRC(rc);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return rc == VINF_OBJECT_DESTROYED;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync}
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync/**
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * RTHandleCreateEx callback that retains an object in the
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * handle table before returning it.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * (Avoids racing the freeing of the handle.)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @returns VBox status code.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param hHandleTable The handle table (ignored).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pvObj The object (INTNETIF).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pvCtx The context (SUPDRVSESSION).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pvUser The user context (ignored).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync NOREF(pvUser);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync NOREF(hHandleTable);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync PINTNETIF pIf = (PINTNETIF)pvObj;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return VINF_SUCCESS;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync}
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Checks if the IPv4 address is a broadcast address.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @returns true/false.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param Addr The address, network endian.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncDECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /* Just check for 255.255.255.255 atm. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return Addr.u == UINT32_MAX;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * Checks if the IPv4 address is a good interface address.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @returns true/false.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param Addr The address, network endian.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsyncDECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /* Usual suspects. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync || Addr.au8[0] == 0) /* Current network, can be used as source address. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync return false;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /* Unusual suspects. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync || (Addr.au8[0] & 0xf0) == 224 /* Multicast */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync ))
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync return false;
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync return true;
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync}
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync/**
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * Gets the address size of a network layer type.
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync *
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @returns size in bytes.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param enmType The type.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsyncDECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync switch (enmType)
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync {
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync case kIntNetAddrType_IPv4: return 4;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync case kIntNetAddrType_IPv6: return 16;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync case kIntNetAddrType_IPX: return 4 + 6;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync default: AssertFailedReturn(0);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync }
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/**
0ff2f0d33dea0e82857c2131d43f637c206a8163vboxsync * Compares two address to see if they are equal, assuming naturally align structures.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync *
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @returns true if equal, false if not.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pAddr1 The first address.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param pAddr2 The second address.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param cbAddr The address size.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsyncDECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync switch (cbAddr)
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync {
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync case 4: /* IPv4 */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync return pAddr1->au32[0] == pAddr2->au32[0];
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync case 16: /* IPv6 */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync return pAddr1->au64[0] == pAddr2->au64[0]
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync && pAddr1->au64[1] == pAddr2->au64[1];
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync case 10: /* IPX */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync return pAddr1->au64[0] == pAddr2->au64[0]
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync && pAddr1->au16[4] == pAddr2->au16[4];
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync default:
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync AssertFailedReturn(false);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync }
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * Worker for intnetR0IfAddrCacheLookup that performs the lookup
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * in the remaining cache entries after the caller has check the
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * most likely ones.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync *
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @returns -1 if not found, the index of the cache entry if found.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pCache The cache.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pAddr The address.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param cbAddr The address size (optimization).
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsyncstatic int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync unsigned i = pCache->cEntries - 2;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync while (i >= 1)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return i;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync pbEntry -= pCache->cbEntry;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync i--;
c5d2523548cc57504b829f53f1362b848a84542cvboxsync }
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
c5d2523548cc57504b829f53f1362b848a84542cvboxsync return -1;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Lookup an address in a cache without any expectations.
fbb3513a43135c633f7f51544c4bdfce748929bfvboxsync *
fbb3513a43135c633f7f51544c4bdfce748929bfvboxsync * @returns -1 if not found, the index of the cache entry if found.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pCache The cache.
fbb3513a43135c633f7f51544c4bdfce748929bfvboxsync * @param pAddr The address.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param cbAddr The address size (optimization).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsyncDECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync{
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync Assert(pCache->cbAddress == cbAddr);
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync /*
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * The optimized case is when there is one cache entry and
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync * it doesn't match.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync unsigned i = pCache->cEntries;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if ( i > 0
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return 0;
c889bbab784ba8552102ce776b6c67b982017861vboxsync if (i <= 1)
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return -1;
c5d2523548cc57504b829f53f1362b848a84542cvboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /*
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * Check the last entry.
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync */
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync i--;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
c5d2523548cc57504b829f53f1362b848a84542cvboxsync return i;
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync if (i <= 1)
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync return -1;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync}
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
c889bbab784ba8552102ce776b6c67b982017861vboxsyncDECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
c889bbab784ba8552102ce776b6c67b982017861vboxsync{
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync /** @todo implement this. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync}
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/**
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * the lookup in the remaining cache entries after the caller
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * has check the most likely ones.
c889bbab784ba8552102ce776b6c67b982017861vboxsync *
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * The routine is expecting not to find the address.
c889bbab784ba8552102ce776b6c67b982017861vboxsync *
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @returns -1 if not found, the index of the cache entry if found.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pCache The cache.
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync * @param pAddr The address.
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync * @param cbAddr The address size (optimization).
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsyncstatic int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /*
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * Perform a full table lookup.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync unsigned i = pCache->cEntries - 2;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync while (i >= 1)
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync {
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return i;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync pbEntry -= pCache->cbEntry;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync i--;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync }
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return -1;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync}
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Lookup an address in a cache expecting not to find it.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync *
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @returns -1 if not found, the index of the cache entry if found.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @param pCache The cache.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @param pAddr The address.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * @param cbAddr The address size (optimization).
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncDECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync Assert(pCache->cbAddress == cbAddr);
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync /*
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * The optimized case is when there is one cache entry and
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * it doesn't match.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync */
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync unsigned i = pCache->cEntries;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync if (RT_UNLIKELY( i > 0
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync return 0;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_LIKELY(i <= 1))
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return -1;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync /*
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * Then check the last entry and return if there are just two cache entries.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync i--;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync return i;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (i <= 1)
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return -1;
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync}
c889bbab784ba8552102ce776b6c67b982017861vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/**
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * Deletes a specific cache entry.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync *
c889bbab784ba8552102ce776b6c67b982017861vboxsync * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync *
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @param pIf The interface (for logging).
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @param pCache The cache.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @param iEntry The entry to delete.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @param pszMsg Log message.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsyncstatic void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync AssertReturnVoid(iEntry < pCache->cEntries);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync AssertReturnVoid(iEntry >= 0);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#ifdef LOG_ENABLED
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync PCRTNETADDRU pAddr = (PCRTNETADDRU)(pCache->pbEntries + iEntry * pCache->cbEntry);
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync switch (enmAddrType)
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync {
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync case kIntNetAddrType_IPv4:
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pIf->hIf, &pIf->Mac, iEntry, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync break;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync default:
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pIf->hIf, &pIf->Mac, enmAddrType, iEntry, pCache->cbAddress, pAddr, pszMsg));
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync break;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync }
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync#endif
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pCache->cEntries--;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (iEntry < pCache->cEntries)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync (pCache->cEntries - iEntry) * pCache->cbEntry);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync}
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Deletes an address from the cache, assuming it isn't actually in the cache.
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync *
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync * @param pIf The interface (for logging).
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync * @param pCache The cache.
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync * @param pAddr The address.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param cbAddr The address size (optimization).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncDECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_UNLIKELY(i >= 0))
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * Deletes the address from all the interface caches.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync *
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * This is used to remove stale entries that has been reassigned to
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * other machines on the network.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync *
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param pNetwork The network.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param pAddr The address.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param enmType The address type.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param cbAddr The address size (optimization).
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param pszMsg Log message.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync */
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsyncDECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync uint8_t const cbAddr, const char *pszMsg)
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync{
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync {
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync if (RT_UNLIKELY(i >= 0))
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync }
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync}
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync/**
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync * Deletes the address from all the interface caches except the specified one.
d9a86bd327291cd44d5f6da6e08bec728aae68bbvboxsync *
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync * This is used to remove stale entries that has been reassigned to
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * other machines on the network.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pNetwork The network.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pAddr The address.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param enmType The address type.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param cbAddr The address size (optimization).
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pszMsg Log message.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
c5d2523548cc57504b829f53f1362b848a84542cvboxsyncDECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync if (pIf != pIfSender)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_UNLIKELY(i >= 0))
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
c889bbab784ba8552102ce776b6c67b982017861vboxsync }
}
/**
* 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.
*/
DECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
{
for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
{
int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
if (i >= 0)
return pIf;
}
return NULL;
}
/**
* 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.
*/
static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, const char *pszMsg)
{
if (!pCache->cEntriesAlloc)
{
/* Allocate the first array */
pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cbEntry * 16);
if (!pCache->pbEntries)
return;
pCache->cEntriesAlloc = 16;
}
else if (pCache->cEntries >= pCache->cEntriesAlloc)
{
bool fReplace = true;
if (pCache->cEntriesAlloc < 64)
{
uint8_t cEntriesAlloc = pCache->cEntriesAlloc + 16;
void *pvNew = RTMemRealloc(pCache->pbEntries, pCache->cbEntry * cEntriesAlloc);
if (pvNew)
{
pCache->pbEntries = (uint8_t *)pvNew;
pCache->cEntriesAlloc = cEntriesAlloc;
fReplace = false;
}
}
if (fReplace)
{
/* simple FIFO, might consider usage/ageing here... */
Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
(int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
pCache->cEntries--;
}
}
/*
* Add the new entry to the end of the array.
*/
uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
memcpy(pbEntry, pAddr, pCache->cbAddress);
memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
#ifdef LOG_ENABLED
INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
switch (enmAddrType)
{
case kIntNetAddrType_IPv4:
Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
pIf->hIf, &pIf->Mac, pCache->cEntries, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
break;
default:
Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs type=%d added #%d %.*Rhxs %s\n",
pIf->hIf, &pIf->Mac, enmAddrType, pCache->cEntries, pCache->cbAddress, pAddr, pszMsg));
break;
}
#endif
pCache->cEntries++;
Assert(pCache->cEntries <= pCache->cEntriesAlloc);
}
/**
* 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.
*/
static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
{
/*
* Check all but the first and last entries, the caller
* has already checked those.
*/
int i = pCache->cEntries - 2;
uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
while (i >= 1)
{
if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
return;
pbEntry += pCache->cbEntry;
i--;
}
/*
* Not found, add it.
*/
intnetR0IfAddrCacheAddIt(pIf, pCache, pAddr, pszMsg);
}
/**
* 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.
*/
DECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
{
Assert(pCache->cbAddress == cbAddr);
/*
* The optimized case is when the address the first or last cache entry.
*/
unsigned i = pCache->cEntries;
if (RT_LIKELY( i > 0
&& ( intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)
|| (i > 1
&& intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))) ))
return;
intnetR0IfAddrCacheAddSlow(pIf, pCache, pAddr, cbAddr, pszMsg);
}
#ifdef INTNET_WITH_DHCP_SNOOPING
/**
* 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.
*/
static void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
{
/*
* Check if the DHCP message is valid and get the type.
*/
if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt))
{
Log6(("Bad UDP packet\n"));
return;
}
PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
uint8_t MsgType;
if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
{
Log6(("Bad DHCP packet\n"));
return;
}
#ifdef LOG_ENABLED
/*
* Log it.
*/
const char *pszType = "unknown";
switch (MsgType)
{
case RTNET_DHCP_MT_DISCOVER: pszType = "discover"; break;
case RTNET_DHCP_MT_OFFER: pszType = "offer"; break;
case RTNET_DHCP_MT_REQUEST: pszType = "request"; break;
case RTNET_DHCP_MT_DECLINE: pszType = "decline"; break;
case RTNET_DHCP_MT_ACK: pszType = "ack";break;
case RTNET_DHCP_MT_NAC: pszType = "nac"; break;
case RTNET_DHCP_MT_RELEASE: pszType = "release"; break;
case RTNET_DHCP_MT_INFORM: pszType = "inform"; break;
}
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_ciaddr.au8[0], pDhcp->bp_ciaddr.au8[1], pDhcp->bp_ciaddr.au8[2], pDhcp->bp_ciaddr.au8[3],
pDhcp->bp_yiaddr.au8[0], pDhcp->bp_yiaddr.au8[1], pDhcp->bp_yiaddr.au8[2], pDhcp->bp_yiaddr.au8[3]));
#endif /* LOG_EANBLED */
/*
* Act upon the message.
*/
switch (MsgType)
{
#if 0
case RTNET_DHCP_MT_REQUEST:
/** @todo Check for valid non-broadcast requests w/ IP for any of the MACs we
* know, and add the IP to the cache. */
break;
#endif
/*
* 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.
*/
case RTNET_DHCP_MT_ACK:
if (intnetR0IPv4AddrIsGood(pDhcp->bp_yiaddr))
for (PINTNETIF pCur = pNetwork->pIFs; pCur; pCur = pCur->pNext)
if ( pCur->fMacSet
&& !memcmp(&pCur->Mac, &pDhcp->bp_chaddr, sizeof(RTMAC)))
{
intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
intnetR0IfAddrCacheAdd(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
break;
}
break;
/*
* Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache.
*/
case RTNET_DHCP_MT_RELEASE:
{
for (PINTNETIF pCur = pNetwork->pIFs; pCur; pCur = pCur->pNext)
if ( pCur->fMacSet
&& !memcmp(&pCur->Mac, &pDhcp->bp_chaddr, sizeof(RTMAC)))
{
intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
}
break;
}
}
}
/**
* 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
* are BOOTPS or BOOTPC.
*
* @param pNetwork The network this frame was seen on.
* @param pSG The gather list for the frame.
*/
static void intnetR0TrunkIfSnoopDhcp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
{
/*
* Get a pointer to a linear copy of the full packet, using the
* temporary buffer if necessary.
*/
PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
size_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
if (pSG->cSegsUsed > 1)
{
cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
Log6(("intnetR0TrunkIfSnoopDhcp: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
return;
//pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
}
/*
* Validate the IP header and find the UDP packet.
*/
if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR)))
{
Log(("intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
return;
}
size_t cbIpHdr = pIpHdr->ip_hl * 4;
/*
* Hand it over to the common DHCP snooper.
*/
intnetR0NetworkSnoopDhcp(pNetwork, pIpHdr, (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr), cbPacket - cbIpHdr);
}
#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.
*/
static void intnetR0TrunkIfSnoopArp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
{
/*
* Check the minimum size first.
*/
if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
return;
/*
* Copy to temporary buffer if necessary.
*/
size_t cbPacket = RT_MIN(pSG->cbTotal, sizeof(RTNETARPIPV4));
PCRTNETARPIPV4 pArpIPv4 = (PCRTNETARPIPV4)((uintptr_t)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
if ( pSG->cSegsUsed != 1
&& pSG->aSegs[0].cb < cbPacket)
{
if ( (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP))
!= (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP)
&& !intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
return;
pArpIPv4 = (PCRTNETARPIPV4)pNetwork->pbTmp;
}
/*
* Ignore packets which doesn't interest us or we perceive as malformed.
*/
if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
|| pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
|| pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
|| pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
return;
uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
&& ar_oper != RTNET_ARPOP_REPLY))
{
Log6(("ts-ar: op=%#x\n", ar_oper));
return;
}
/*
* Delete the source address if it's OK.
*/
if ( !(pArpIPv4->ar_sha.au8[0] & 1)
&& ( pArpIPv4->ar_sha.au16[0]
|| pArpIPv4->ar_sha.au16[1]
|| pArpIPv4->ar_sha.au16[2])
&& intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
{
Log6(("ts-ar: %d.%d.%d.%d / %.6Rhxs\n", pArpIPv4->ar_spa.au8[0], pArpIPv4->ar_spa.au8[1],
pArpIPv4->ar_spa.au8[2], pArpIPv4->ar_spa.au8[3], &pArpIPv4->ar_sha));
intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_spa,
kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_spa), "tif/arp");
}
}
#ifdef INTNET_WITH_DHCP_SNOOPING
/**
* 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
* this function.
* 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.
*/
static void intnetR0TrunkIfSnoopAddr(PINTNETNETWORK pNetwork, PCINTNETSG pSG, uint16_t EtherType)
{
switch (EtherType)
{
case RTNET_ETHERTYPE_IPV4:
{
uint32_t cbIpHdr;
uint8_t b;
Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN)
{
/* check if the protocol is UDP */
PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
return;
/* get the TCP header length */
cbIpHdr = pIpHdr->ip_hl * 4;
}
else
{
/* check if the protocol is UDP */
if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_p))
!= RTNETIPV4_PROT_UDP)
return;
/* get the TCP header length */
b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
cbIpHdr = (b & 0x0f) * 4;
}
if (cbIpHdr < RTNETIPV4_MIN_LEN)
return;
/* compare the ports. */
if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + cbIpHdr + RTNETUDP_MIN_LEN)
{
PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR) + cbIpHdr);
if ( ( RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPS
&& RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS)
|| ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPC
&& RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC))
return;
}
else
{
/* get the lower byte of the UDP source port number. */
b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport) + 1);
if ( b != RTNETIPV4_PORT_BOOTPS
&& b != RTNETIPV4_PORT_BOOTPC)
return;
uint8_t SrcPort = b;
b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport));
if (b)
return;
/* get the lower byte of the UDP destination port number. */
b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport) + 1);
if ( b != RTNETIPV4_PORT_BOOTPS
&& b != RTNETIPV4_PORT_BOOTPC)
return;
if (b == SrcPort)
return;
b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport));
if (b)
return;
}
intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
break;
}
case RTNET_ETHERTYPE_IPV6:
{
/** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
* need to be edited. Check out how NDP works... */
break;
}
case RTNET_ETHERTYPE_ARP:
intnetR0TrunkIfSnoopArp(pNetwork, pSG);
break;
}
}
#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.
*/
static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket)
{
/*
* Check the header size first to prevent access invalid data.
*/
if (cbPacket < RTNETIPV4_MIN_LEN)
return;
uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
if ( cbHdr < RTNETIPV4_MIN_LEN
|| cbPacket < cbHdr)
return;
/*
* 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.
*/
bool fValidatedIpHdr = false;
RTNETADDRU Addr;
Addr.IPv4 = pIpHdr->ip_src;
if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
&& intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
{
if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
{
Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
return;
}
intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, "if/ipv4");
fValidatedIpHdr = true;
}
#ifdef INTNET_WITH_DHCP_SNOOPING
/*
* Check for potential DHCP packets.
*/
if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
&& cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
{
PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
|| RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
&& ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
|| RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
{
if ( fValidatedIpHdr
|| RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
else
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.
*/
static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
{
/*
* Ignore packets which doesn't interest us or we perceive as malformed.
*/
if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
return;
if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
|| pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
|| pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
|| pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
return;
uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
&& ar_oper != RTNET_ARPOP_REPLY))
{
Log6(("ar_oper=%#x\n", ar_oper));
return;
}
/*
* 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.
*/
*pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
if ( ar_oper == RTNET_ARPOP_REPLY
&& !(pArpIPv4->ar_tha.au8[0] & 1)
&& ( pArpIPv4->ar_tha.au16[0]
|| pArpIPv4->ar_tha.au16[1]
|| pArpIPv4->ar_tha.au16[2])
&& intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
if ( !memcmp(&pArpIPv4->ar_sha, &pIf->Mac, sizeof(RTMAC))
&& intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pArpIPv4->ar_spa, sizeof(RTNETADDRIPV4), "if/arp");
}
/**
* Checks packets send by a normal interface for new network
* layer addresses.
*
* @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.
*/
static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, uint16_t *pfSgFlags)
{
/*
* Fish out the ethertype and look for stuff we can handle.
*/
if (cbFrame <= sizeof(RTNETETHERHDR))
return;
cbFrame -= sizeof(RTNETETHERHDR);
uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
switch (EtherType)
{
case RTNET_ETHERTYPE_IPV4:
intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
break;
#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
case RTNET_ETHERTYPE_IPV6:
/** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
* need to be edited. Check out how NDP works... */
intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
break;
#endif
#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
case RTNET_ETHERTYPE_IPX_1:
case RTNET_ETHERTYPE_IPX_2:
case RTNET_ETHERTYPE_IPX_3:
intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
break;
#endif
case RTNET_ETHERTYPE_ARP:
intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
break;
}
}
#ifdef IN_INTNET_TESTCASE
/**
* 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.
*/
static unsigned intnetR0RingReadFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, void *pvFrame)
{
Assert(pRingBuf->offRead < pBuf->cbBuf);
Assert(pRingBuf->offRead >= pRingBuf->offStart);
Assert(pRingBuf->offRead < pRingBuf->offEnd);
uint32_t offRead = pRingBuf->offRead;
PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offRead);
const void *pvFrameIn = INTNETHdrGetFramePtr(pHdr, pBuf);
unsigned cb = pHdr->cbFrame;
memcpy(pvFrame, pvFrameIn, cb);
/* skip the frame */
offRead += pHdr->offFrame + cb;
offRead = RT_ALIGN_32(offRead, sizeof(INTNETHDR));
Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
if (offRead >= pRingBuf->offEnd)
offRead = pRingBuf->offStart;
ASMAtomicXchgU32(&pRingBuf->offRead, offRead);
return cb;
}
#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.
*/
static int intnetR0RingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
{
/*
* Validate input.
*/
AssertPtr(pBuf);
AssertPtr(pRingBuf);
AssertPtr(pSG);
Assert(pSG->cbTotal >= sizeof(RTMAC) * 2);
uint32_t offWrite = pRingBuf->offWrite;
Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
uint32_t offRead = pRingBuf->offRead;
Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));
const uint32_t cb = RT_ALIGN_32(pSG->cbTotal, sizeof(INTNETHDR));
if (offRead <= offWrite)
{
/*
* Try fit it all before the end of the buffer.
*/
if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
{
PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
pHdr->u16Type = INTNETHDR_TYPE_FRAME;
pHdr->cbFrame = pSG->cbTotal;
pHdr->offFrame = sizeof(INTNETHDR);
intnetR0SgRead(pSG, pHdr + 1);
if (pNewDstMac)
((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
offWrite += cb + sizeof(INTNETHDR);
Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
if (offWrite >= pRingBuf->offEnd)
offWrite = pRingBuf->offStart;
Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
return VINF_SUCCESS;
}
/*
* Try fit the frame at the start of the buffer.
* (The header fits before the end of the buffer because of alignment.)
*/
AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
if (offRead - pRingBuf->offStart > cb) /* not >= ! */
{
PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
pHdr->u16Type = INTNETHDR_TYPE_FRAME;
pHdr->cbFrame = pSG->cbTotal;
pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;
intnetR0SgRead(pSG, pvFrameOut);
if (pNewDstMac)
((PRTNETETHERHDR)pvFrameOut)->DstMac = *pNewDstMac;
offWrite = pRingBuf->offStart + cb;
ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
return VINF_SUCCESS;
}
}
/*
* The reader is ahead of the writer, try fit it into that space.
*/
else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
{
PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
pHdr->u16Type = INTNETHDR_TYPE_FRAME;
pHdr->cbFrame = pSG->cbTotal;
pHdr->offFrame = sizeof(INTNETHDR);
intnetR0SgRead(pSG, pHdr + 1);
if (pNewDstMac)
((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
offWrite += cb + sizeof(INTNETHDR);
ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
return VINF_SUCCESS;
}
/* (it didn't fit) */
/** @todo stats */
return VERR_BUFFER_OVERFLOW;
}
/**
* 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.
*/
static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
{
// LogFlow(("intnetR0IfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf));
int rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
if (RT_SUCCESS(rc))
{
pIf->cYields = 0;
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
RTSemEventSignal(pIf->Event);
return;
}
Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, 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
manner if required. */
/*
* 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.
*/
if ( pIf->fActive
&& pIf->cYields < 100)
{
unsigned cYields = 10;
#else
/*
* Scheduling hack, for unicore machines primarily.
*/
if ( pIf->fActive
&& pIf->cYields < 4 /* just twice */
&& pIfSender /* but not if it's from the trunk */)
{
unsigned cYields = 2;
#endif
while (--cYields > 0)
{
RTSemEventSignal(pIf->Event);
RTThreadYield();
rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
if (RT_SUCCESS(rc))
{
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
RTSemEventSignal(pIf->Event);
return;
}
pIf->cYields++;
}
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
}
/* ok, the frame is lost. */
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
RTSemEventSignal(pIf->Event);
}
/**
* 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.
*/
static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
uint32_t fDst, PINTNETSG pSG, bool fTrunkLocked)
{
/*
* Quick sanity check.
*/
AssertPtr(pThis);
AssertPtr(pNetwork);
AssertPtr(pSG);
Assert(fDst);
AssertReturnVoid(pThis->pIfPort);
/*
* 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
* case right now).
*/
if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
&& (fDst & INTNETTRUNKDIR_WIRE))
{
/* Dispatch it to the host before making changes. */
if (fDst & INTNETTRUNKDIR_HOST)
{
Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG, fTrunkLocked);
fDst &= ~INTNETTRUNKDIR_HOST;
}
/* ASSUME frame from INTNETR0IfSend! */
AssertReturnVoid(pSG->cSegsUsed == 1);
AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
AssertReturnVoid(fTrunkLocked);
AssertReturnVoid(pIfSender);
PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
/*
* 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.
*/
pThis->pIfPort->pfnGetMacAddress(pThis->pIfPort, &pThis->CachedMac);
if (!memcmp(&pEthHdr->SrcMac, &pIfSender->Mac, sizeof(RTMAC)))
pEthHdr->SrcMac = pThis->CachedMac;
/*
* Deal with tags from the snooping phase.
*/
if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
{
/*
* 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
* wireless of course...
*/
PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
if (!memcmp(&pArp->ar_sha, &pIfSender->Mac, sizeof(RTMAC)))
{
Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->CachedMac));
pArp->ar_sha = pThis->CachedMac;
}
if (!memcmp(&pArp->ar_tha, &pIfSender->Mac, sizeof(RTMAC))) /* just in case... */
{
Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->CachedMac));
pArp->ar_tha = pThis->CachedMac;
}
}
//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.
*/
AssertReturnVoid(fTrunkLocked); /* to be removed. */
int rc;
if ( fTrunkLocked
|| intnetR0TrunkIfRetain(pThis))
{
rc = RTSemFastMutexRelease(pNetwork->FastMutex);
AssertRC(rc);
if (RT_SUCCESS(rc))
{
if ( fTrunkLocked
|| intnetR0TrunkIfOutLock(pThis))
{
rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pSG, fDst);
if (!fTrunkLocked)
intnetR0TrunkIfOutUnlock(pThis);
}
else
{
AssertFailed();
rc = VERR_SEM_DESTROYED;
}
int rc2 = RTSemFastMutexRequest(pNetwork->FastMutex);
AssertRC(rc2);
}
if (!fTrunkLocked)
intnetR0TrunkIfRelease(pThis);
}
else
{
AssertFailed();
rc = VERR_SEM_DESTROYED;
}
/** @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...
*/
static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
{
/*
* Check the minimum size and get a linear copy of the thing to work on,
* using the temporary buffer if necessary.
*/
if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
return;
PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
if ( pSG->cSegsUsed != 1
&& pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
{
Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
return;
pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
}
/*
* Ignore packets which doesn't interest us or we perceive as malformed.
*/
if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
|| pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
|| pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
|| pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
return;
uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
&& ar_oper != RTNET_ARPOP_REPLY))
{
Log6(("ar_oper=%#x\n", ar_oper));
return;
}
/* Tag it as ARP IPv4. */
pSG->fFlags |= INTNETSG_FLAGS_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.
*/
if ( ar_oper == RTNET_ARPOP_REPLY
&& !memcmp(&pArpIPv4->ar_tha, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
{
PINTNETIF pIf = intnetR0NetworkAddrCacheLookupIf(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_tpa,
kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_tpa));
if (pIf)
{
Log6(("fw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArpIPv4->ar_tha, &pIf->Mac));
pArpIPv4->ar_tha = pIf->Mac;
if (!memcmp(&pEthHdr->DstMac, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
{
Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
pEthHdr->DstMac = pIf->Mac;
if ((void *)pEthHdr != pSG->aSegs[0].pv)
intnetR0SgWritePart(pSG, RT_OFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->Mac);
}
/* Write back the packet if we've been making changes to a buffered copy. */
if (pSG->fFlags & INTNETSG_FLAGS_PKT_CP_IN_TMP)
intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR), sizeof(PRTNETARPIPV4), pArpIPv4);
}
}
}
/**
* 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...
*/
static void intnetR0NetworkEditDhcpFromIntNet(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
{
/*
* Check the minimum size and get a linear copy of the thing to work on,
* using the temporary buffer if necessary.
*/
if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN))
return;
/*
* Get a pointer to a linear copy of the full packet, using the
* temporary buffer if necessary.
*/
PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
size_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
if (pSG->cSegsUsed > 1)
{
cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
Log6(("intnetR0NetworkEditDhcpFromIntNet: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
return;
//pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
}
/*
* Validate the IP header and find the UDP packet.
*/
if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR)))
{
Log6(("intnetR0NetworkEditDhcpFromIntNet: bad ip header\n"));
return;
}
size_t cbIpHdr = pIpHdr->ip_hl * 4;
if ( pIpHdr->ip_p != RTNETIPV4_PROT_UDP /* DHCP is UDP. */
|| cbPacket < cbIpHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
return;
size_t cbUdpPkt = cbPacket - cbIpHdr;
PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr);
/* We are only interested in DHCP packets coming from client to server. */
if ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS
|| RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC)
return;
/*
* Check if the DHCP message is valid and get the type.
*/
if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt))
{
Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad UDP packet\n"));
return;
}
PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
uint8_t MsgType;
if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
{
Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad DHCP packet\n"));
return;
}
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));
if (!(pDhcp->bp_flags & RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST)))
{
/* Patch flags */
uint16_t uFlags = pDhcp->bp_flags | RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
intnetR0SgWritePart(pSG, (uint8_t*)&pDhcp->bp_flags - (uint8_t*)pIpHdr + sizeof(RTNETETHERHDR), sizeof(uFlags), &uFlags);
/* Patch UDP checksum */
uint32_t uChecksum = (uint32_t)~pUdpHdr->uh_sum + RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
while (uChecksum >> 16)
uChecksum = (uChecksum >> 16) + (uChecksum & 0xFFFF);
uChecksum = ~uChecksum;
intnetR0SgWritePart(pSG, (uint8_t*)&pUdpHdr->uh_sum - (uint8_t*)pIpHdr + sizeof(RTNETETHERHDR), sizeof(pUdpHdr->uh_sum), &uChecksum);
}
break;
}
}
/**
* 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.
*/
static bool intnetR0NetworkSendBroadcast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
{
/*
* 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.
*/
if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
&& (fSrc & INTNETTRUNKDIR_WIRE)
&& RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP)
intnetR0NetworkEditArpFromWire(pNetwork, pSG, pEthHdr);
/*
* 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
* the host.
*/
if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
&& !fSrc
&& RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_IPV4)
intnetR0NetworkEditDhcpFromIntNet(pNetwork, pSG, pEthHdr);
/*
* 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.
*/
for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
if (pIf != pIfSender)
intnetR0IfSend(pIf, pIfSender, pSG, NULL);
/*
* Unless the trunk is the origin, broadcast it to both the wire
* and the host as well.
*/
PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
if ( pIfSender
&& pTrunkIf)
intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked);
/*
* Snoop address info from packet orginating from the trunk connection.
*/
else if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
&& !pIfSender)
{
#ifdef INTNET_WITH_DHCP_SNOOPING
uint16_t EtherType = RT_BE2H_U16(pEthHdr->EtherType);
if ( ( EtherType == RTNET_ETHERTYPE_IPV4 /* for DHCP */
&& pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
|| (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4)) )
intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
#else
if (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4))
intnetR0TrunkIfSnoopArp(pNetwork, pSG);
#endif
}
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.
*/
static bool intnetR0NetworkSendMulticast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
{
/** @todo implement multicast */
return intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, pEthHdr);
}
/**
* Sends a unicast frame using the network layer address instead
* of the link layer one.
*
* 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.
*/
static bool intnetR0NetworkSendUnicastWithSharedMac(PINTNETNETWORK pNetwork, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
{
/*
* Extract the network address from the packet.
*/
RTNETADDRU Addr;
INTNETADDRTYPE enmAddrType;
uint8_t cbAddr;
switch (RT_BE2H_U16(pEthHdr->EtherType))
{
case RTNET_ETHERTYPE_IPV4:
if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
{
Log(("intnetshareduni: failed to read ip_dst! cbTotal=%#x\n", pSG->cbTotal));
return false;
}
enmAddrType = kIntNetAddrType_IPv4;
cbAddr = sizeof(Addr.IPv4);
Log6(("intnetshareduni: IPv4 %d.%d.%d.%d\n", Addr.au8[0], Addr.au8[1], Addr.au8[2], Addr.au8[3]));
break;
#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
case RTNET_ETHERTYPE_IPV6
if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
{
Log(("intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n", pSG->cbTotal));
return false;
}
enmAddrType = kIntNetAddrType_IPv6;
cbAddr = sizeof(Addr.IPv6);
break;
#endif
#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
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)))
{
Log(("intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n", pSG->cbTotal));
return false;
}
enmAddrType = kIntNetAddrType_IPX;
cbAddr = sizeof(Addr.IPX);
break;
#endif
/*
* Treat ARP is broadcast (it shouldn't end up here normally,
* so it goes last in the switch).
*/
case RTNET_ETHERTYPE_ARP:
Log6(("intnetshareduni: ARP\n"));
/** @todo revisit this broadcasting of unicast ARP frames! */
return intnetR0NetworkSendBroadcast(pNetwork, NULL, INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked, pEthHdr);
/*
* Unknown packets are sent do all interfaces that are in promiscuous mode.
*/
default:
{
Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
if (!(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC)))
{
for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
if (pIf->fPromiscuous)
{
Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
intnetR0IfSend(pIf, NULL, pSG, NULL);
}
}
return false;
}
}
/*
* Send it to interfaces with matching network addresses.
*/
bool fExactIntNetRecipient = false;
for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
{
bool fIt = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmAddrType], &Addr, cbAddr) >= 0;
if ( fIt
|| ( pIf->fPromiscuous
&& !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))))
{
Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
fExactIntNetRecipient |= fIt;
intnetR0IfSend(pIf, NULL, pSG, fIt ? &pIf->Mac : NULL);
}
}
#ifdef INTNET_WITH_DHCP_SNOOPING
/*
* Perform DHCP snooping.
*/
if ( enmAddrType == kIntNetAddrType_IPv4
&& pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
#endif /* INTNET_WITH_DHCP_SNOOPING */
return fExactIntNetRecipient;
}
/**
* Sends a unicast 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.
*/
static bool intnetR0NetworkSendUnicast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PCRTNETETHERHDR pEthHdr)
{
/*
* Only send to the interfaces with matching a MAC address.
*/
bool fExactIntNetRecipient = false;
for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
{
bool fIt = false;
if ( ( !pIf->fMacSet
|| (fIt = !memcmp(&pIf->Mac, &pEthHdr->DstMac, sizeof(pIf->Mac))) )
|| ( pIf->fPromiscuous
&& !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))
&& pIf != pIfSender /* promiscuous mode: omit the sender */))
{
Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
fExactIntNetRecipient |= fIt;
intnetR0IfSend(pIf, pIfSender, pSG, NULL);
}
}
/*
* Send it to the trunk?
* If we didn't find the recipient on the internal network the
* frame will hit the wire.
*/
uint32_t fDst = 0;
PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
if ( pIfSender
&& pTrunkIf
&& pTrunkIf->pIfPort)
{
Assert(!fSrc);
/* promiscuous checks first as they are cheaper than pfnIsHostMac. */
if ( pTrunkIf->fPromiscuousWire
&& !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC | INTNET_OPEN_FLAGS_IGNORE_PROMISC_TRUNK_WIRE | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC_TRUNK_WIRE)) )
fDst |= INTNETTRUNKDIR_WIRE;
if ( !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC | INTNET_OPEN_FLAGS_IGNORE_PROMISC_TRUNK_HOST | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC_TRUNK_HOST))
&& pTrunkIf->pIfPort->pfnIsPromiscuous(pTrunkIf->pIfPort) )
fDst |= INTNETTRUNKDIR_HOST;
if ( fDst != (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
&& !fExactIntNetRecipient /* if you have duplicate mac addresses, you're screwed. */ )
{
if (pTrunkIf->pIfPort->pfnIsHostMac(pTrunkIf->pIfPort, &pEthHdr->DstMac))
fDst |= INTNETTRUNKDIR_HOST;
else
fDst |= INTNETTRUNKDIR_WIRE;
}
if (fDst)
intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, fDst, pSG, fTrunkLocked);
}
/* log it */
if ( !fExactIntNetRecipient
&& !fDst
&& ( (pEthHdr->DstMac.au8[0] == 0x08 && pEthHdr->DstMac.au8[1] == 0x00 && pEthHdr->DstMac.au8[2] == 0x27)
|| (pEthHdr->SrcMac.au8[0] == 0x08 && pEthHdr->SrcMac.au8[1] == 0x00 && pEthHdr->SrcMac.au8[2] == 0x27)))
Log2(("Dst=%.6Rhxs ??\n", &pEthHdr->DstMac));
return fExactIntNetRecipient;
}
/**
* Sends a frame.
*
* 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.
*/
static bool intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked)
{
bool fRc = false;
/*
* Assert reality.
*/
AssertPtr(pNetwork);
AssertPtrNull(pIfSender);
Assert(pIfSender ? fSrc == 0 : fSrc != 0);
Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
AssertPtr(pSG);
Assert(pSG->cSegsUsed >= 1);
Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
if (pSG->cbTotal < sizeof(RTNETETHERHDR))
return fRc;
/*
* Send statistics.
*/
if (pIfSender)
{
STAM_REL_COUNTER_INC(&pIfSender->pIntBuf->cStatSends);
STAM_REL_COUNTER_ADD(&pIfSender->pIntBuf->cbStatSend, pSG->cbTotal);
}
/*
* Get the ethernet header (might theoretically involve multiple segments).
*/
RTNETETHERHDR EthHdr;
if (pSG->aSegs[0].cb >= sizeof(EthHdr))
EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
else if (!intnetR0SgReadPart(pSG, 0, sizeof(EthHdr), &EthHdr))
return false;
if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
|| (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
|| (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
|| (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
|| EthHdr.DstMac.au8[0] == 0xff
|| EthHdr.SrcMac.au8[0] == 0xff)
Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
&EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
/*
* Inspect the header updating the mac address of the sender in the process.
*/
if ( pIfSender
&& memcmp(&EthHdr.SrcMac, &pIfSender->Mac, sizeof(pIfSender->Mac)))
{
/** @todo stats */
Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->Mac, &EthHdr.SrcMac));
pIfSender->Mac = EthHdr.SrcMac;
pIfSender->fMacSet = true;
}
/*
* Distribute the frame.
*/
if ( EthHdr.DstMac.au16[0] == 0xffff /* broadcast address. */
&& EthHdr.DstMac.au16[1] == 0xffff
&& EthHdr.DstMac.au16[2] == 0xffff)
fRc = intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
else if (RT_UNLIKELY(EthHdr.DstMac.au8[0] & 1)) /* multicast address */
fRc = intnetR0NetworkSendMulticast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
else if ( !(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
|| !(fSrc & INTNETTRUNKDIR_WIRE))
fRc = intnetR0NetworkSendUnicast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
else
fRc = intnetR0NetworkSendUnicastWithSharedMac(pNetwork, pSG, fTrunkLocked, &EthHdr);
return fRc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSend(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, const void *pvFrame, unsigned cbFrame)
{
Log5(("INTNETR0IfSend: pIntNet=%p hIf=%RX32 pvFrame=%p cbFrame=%u\n", pIntNet, hIf, pvFrame, cbFrame));
/*
* Validate input and translate the handle.
*/
AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
if (pvFrame && cbFrame)
{
AssertReturn(cbFrame < 0x8000, VERR_INVALID_PARAMETER);
AssertPtrReturn(pvFrame, VERR_INVALID_PARAMETER);
AssertPtrReturn((uint8_t *)pvFrame + cbFrame - 1, VERR_INVALID_PARAMETER);
/* This is the better place to crash, probe the buffer. */
ASMProbeReadBuffer(pvFrame, cbFrame);
}
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* 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.
*/
PINTNETNETWORK pNetwork = pIf->pNetwork;
int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
if (RT_FAILURE(rc))
{
intnetR0IfRelease(pIf, pSession);
return rc;
}
PINTNETTRUNKIF pTrunkIf = intnetR0TrunkIfRetain(pNetwork->pTrunkIF);
if (pTrunkIf)
{
RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
if (!intnetR0TrunkIfOutLock(pTrunkIf))
{
intnetR0TrunkIfRelease(pTrunkIf);
intnetR0IfRelease(pIf, pSession);
return VERR_SEM_DESTROYED;
}
rc = RTSemFastMutexRequest(pNetwork->FastMutex);
if (RT_FAILURE(rc))
{
intnetR0TrunkIfOutUnlock(pTrunkIf);
intnetR0TrunkIfRelease(pTrunkIf);
intnetR0IfRelease(pIf, pSession);
return rc;
}
}
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 argument.
*/
if (pvFrame && cbFrame)
{
intnetR0SgInitTemp(&Sg, (void *)pvFrame, cbFrame);
if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
intnetR0IfSnoopAddr(pIf, (uint8_t *)pvFrame, cbFrame, (uint16_t *)&Sg.fFlags);
intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
}
/*
* Process the send buffer.
*/
while (pIf->pIntBuf->Send.offRead != pIf->pIntBuf->Send.offWrite)
{
/* Send the frame if the type is sane. */
PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pIf->pIntBuf + pIf->pIntBuf->Send.offRead);
if (pHdr->u16Type == INTNETHDR_TYPE_FRAME)
{
void *pvCurFrame = INTNETHdrGetFramePtr(pHdr, pIf->pIntBuf);
if (pvCurFrame)
{
intnetR0SgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, (uint16_t *)&Sg.fFlags);
intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
}
}
/* else: ignore the frame */
/* Skip to the next frame. */
INTNETRingSkipFrame(pIf->pIntBuf, &pIf->pIntBuf->Send);
}
/*
* Release the semaphore(s) and release references.
*/
rc = RTSemFastMutexRelease(pNetwork->FastMutex);
if (pTrunkIf)
{
intnetR0TrunkIfOutUnlock(pTrunkIf);
intnetR0TrunkIfRelease(pTrunkIf);
}
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSendReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfSend(pIntNet, pReq->hIf, pSession, NULL, 0);
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfGetRing3Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, R3PTRTYPE(PINTNETBUF) *ppRing3Buf)
{
LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));
/*
* Validate input.
*/
AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertPtrReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
*ppRing3Buf = 0;
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* ASSUMES that only the process that created an interface can use it.
* ASSUMES that we created the ring-3 mapping when selecting or
* allocating the buffer.
*/
int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
if (RT_SUCCESS(rc))
{
*ppRing3Buf = pIf->pIntBufR3;
rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
}
intnetR0IfRelease(pIf, pSession);
LogFlow(("INTNETR0IfGetRing3Buffer: returns %Rrc *ppRing3Buf=%p\n", rc, *ppRing3Buf));
return rc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfGetRing3BufferReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFGETRING3BUFFERREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfGetRing3Buffer(pIntNet, pReq->hIf, pSession, &pReq->pRing3Buf);
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfGetRing0Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF *ppRing0Buf)
{
LogFlow(("INTNETR0IfGetRing0Buffer: pIntNet=%p hIf=%RX32 ppRing0Buf=%p\n", pIntNet, hIf, ppRing0Buf));
/*
* Validate input.
*/
AssertPtrReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
*ppRing0Buf = NULL;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* Grab the lock and get the data.
* ASSUMES that the handle isn't closed while we're here.
*/
int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
if (RT_SUCCESS(rc))
{
*ppRing0Buf = pIf->pIntBuf;
rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
}
intnetR0IfRelease(pIf, pSession);
LogFlow(("INTNETR0IfGetRing0Buffer: returns %Rrc *ppRing0Buf=%p\n", rc, *ppRing0Buf));
return rc;
}
#if 0
/**
* 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.)
* @param cPages
*/
INTNETR0DECL(int) INTNETR0IfGetPhysBuffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
{
/*
* Validate input.
*/
AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* Grab the lock and get the data.
* ASSUMES that the handle isn't closed while we're here.
*/
int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
if (RT_SUCCESS(rc))
{
/** @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.. */
rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
}
intnetR0IfRelease(pIf, pSession);
return VERR_NOT_IMPLEMENTED;
}
#endif
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSetPromiscuousMode(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
{
LogFlow(("INTNETR0IfSetPromiscuousMode: pIntNet=%p hIf=%RX32 fPromiscuous=%d\n", pIntNet, hIf, fPromiscuous));
/*
* Validate & translate input.
*/
AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
{
Log(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
/*
* Grab the network semaphore and make the change.
*/
int rc;
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
{
rc = RTSemFastMutexRequest(pNetwork->FastMutex);
if (RT_SUCCESS(rc))
{
if (pIf->fPromiscuous != fPromiscuous)
{
Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
hIf, !fPromiscuous, !!fPromiscuous));
ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
}
rc = RTSemFastMutexRelease(pNetwork->FastMutex);
}
}
else
rc = VERR_WRONG_ORDER;
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSetPromiscuousModeReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfSetPromiscuousMode(pIntNet, pReq->hIf, pSession, pReq->fPromiscuous);
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSetMacAddress(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCRTMAC pMac)
{
LogFlow(("INTNETR0IfSetMacAddress: pIntNet=%p hIf=%RX32 pMac=%p:{%.6Rhxs}\n", pIntNet, hIf, pMac, pMac));
/*
* Validate & translate input.
*/
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
{
Log(("INTNETR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
/*
* Grab the network semaphore and make the change.
*/
int rc;
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
{
rc = RTSemFastMutexRequest(pNetwork->FastMutex);
if (RT_SUCCESS(rc))
{
if (memcmp(&pIf->Mac, pMac, sizeof(pIf->Mac)))
{
Log(("INTNETR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
hIf, &pIf->Mac, pMac));
pIf->Mac = *pMac;
pIf->fMacSet = true;
}
rc = RTSemFastMutexRelease(pNetwork->FastMutex);
}
}
else
rc = VERR_WRONG_ORDER;
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSetMacAddressReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfSetMacAddress(pIntNet, pReq->hIf, pSession, &pReq->Mac);
}
/**
* 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.
*/
static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
{
/* quick santiy check */
AssertPtr(pNetwork);
AssertPtr(pIf);
/*
* If we've got a trunk, lock it now in case we need to call out, and
* then lock the network.
*/
PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
if (pTrunkIf && !intnetR0TrunkIfOutLock(pTrunkIf))
return VERR_SEM_DESTROYED;
int rc = RTSemFastMutexRequest(pNetwork->FastMutex); AssertRC(rc);
if (RT_SUCCESS(rc))
{
bool fNetworkLocked = true;
/*
* Make the change if necessary.
*/
if (pIf->fActive != fActive)
{
pIf->fActive = fActive;
uint32_t const cActiveIFs = pNetwork->cActiveIFs;
Assert((int32_t)cActiveIFs + (fActive ? 1 : -1) >= 0);
pNetwork->cActiveIFs += fActive ? 1 : -1;
if ( pTrunkIf
&& ( !pNetwork->cActiveIFs
|| !cActiveIFs))
{
/*
* We'll have to change the trunk status, so, leave
* the network semaphore so we don't create any deadlocks.
*/
int rc2 = RTSemFastMutexRelease(pNetwork->FastMutex); AssertRC(rc2);
fNetworkLocked = false;
if (pTrunkIf->pIfPort)
pTrunkIf->pIfPort->pfnSetActive(pTrunkIf->pIfPort, fActive);
}
}
if (fNetworkLocked)
RTSemFastMutexRelease(pNetwork->FastMutex);
}
if (pTrunkIf)
intnetR0TrunkIfOutUnlock(pTrunkIf);
return rc;
}
/**
* 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.
*/
static int intnetR0IfSetActive(PINTNETIF pIf, bool fActive)
{
/* quick sanity check */
AssertPtrReturn(pIf, VERR_INVALID_POINTER);
/*
* Hand it to the network since it might involve the trunk
* and things are tricky there wrt to locking order.
*/
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (!pNetwork)
return VERR_WRONG_ORDER;
return intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSetActive(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
{
LogFlow(("INTNETR0IfSetActive: pIntNet=%p hIf=%RX32 fActive=%RTbool\n", pIntNet, hIf, fActive));
/*
* Validate & translate input.
*/
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
{
Log(("INTNETR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
/*
* Hand it to the network since it might involve the trunk
* and things are tricky there wrt to locking order.
*/
int rc;
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
else
rc = VERR_WRONG_ORDER;
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfSetActiveReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfSetActive(pIntNet, pReq->hIf, pSession, pReq->fActive);
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0IfWait(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
{
Log4(("INTNETR0IfWait: pIntNet=%p hIf=%RX32 cMillies=%u\n", pIntNet, hIf, cMillies));
/*
* Get and validate essential handles.
*/
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
{
Log(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
const INTNETIFHANDLE hIfSelf = pIf->hIf;
const RTSEMEVENT Event = pIf->Event;
if ( hIfSelf != hIf /* paranoia */
&& Event != NIL_RTSEMEVENT)
{
Log(("INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
return VERR_SEM_DESTROYED;
}
/*
* 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.
*/
ASMAtomicIncU32(&pIf->cSleepers);
int rc = RTSemEventWaitNoResume(Event, cMillies);
if (pIf->Event == Event)
{
ASMAtomicDecU32(&pIf->cSleepers);
if (!pIf->fDestroying)
{
if (intnetR0IfRelease(pIf, pSession))
rc = VERR_SEM_DESTROYED;
}
else
rc = VERR_SEM_DESTROYED;
}
else
rc = VERR_SEM_DESTROYED;
Log4(("INTNETR0IfWait: returns %Rrc\n", rc));
return 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.
*/
INTNETR0DECL(int) INTNETR0IfWaitReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfWait(pIntNet, pReq->hIf, pSession, pReq->cMillies);
}
/**
* Close an interface.
*
* @returns VBox status code.
* @param pIntNet The instance handle.
* @param hIf The interface handle.
* @param pSession The caller's session.
*/
INTNETR0DECL(int) INTNETR0IfClose(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
{
LogFlow(("INTNETR0IfClose: pIntNet=%p hIf=%RX32\n", pIntNet, hIf));
/*
* Validate and free the handle.
*/
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/* mark the handle as freed so intnetR0IfDestruct won't free it again. */
ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
/*
* 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).
*/
RTSemEventSignal(pIf->Event);
void *pvObj = pIf->pvObj;
intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
int rc = SUPR0ObjRelease(pvObj, pSession);
LogFlow(("INTNETR0IfClose: returns %Rrc\n", rc));
return 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.
*/
INTNETR0DECL(int) INTNETR0IfCloseReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0IfClose(pIntNet, pReq->hIf, pSession);
}
/**
* 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.
*/
static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
{
PINTNETIF pIf = (PINTNETIF)pvUser1;
PINTNET pIntNet = (PINTNET)pvUser2;
Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
RTSemFastMutexRequest(pIntNet->FastMutex);
/*
* Mark the interface as being destroyed so the waiter
* can behave appropriately (theoretical case).
*/
ASMAtomicWriteBool(&pIf->fDestroying, true);
/*
* Delete the interface handle so the object no longer can be used.
* (Can happen if the client didn't close its session.)
*/
INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
if (hIf != INTNET_HANDLE_INVALID)
{
void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession); NOREF(pvObj2);
AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
}
/*
* If we've got a network deactivate and unlink ourselves from it.
* Because of cleanup order we might be an orphan now.
*/
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
{
intnetR0IfSetActive(pIf, false);
if (pNetwork->pIFs == pIf)
pNetwork->pIFs = pIf->pNext;
else
{
PINTNETIF pPrev = pNetwork->pIFs;
while (pPrev)
{
if (pPrev->pNext == pIf)
{
pPrev->pNext = pIf->pNext;
break;
}
pPrev = pPrev->pNext;
}
Assert(pPrev);
}
pIf->pNext = NULL;
/*
* Release our reference to the network.
*/
RTSemFastMutexRelease(pIntNet->FastMutex);
SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
pIf->pNetwork = NULL;
}
else
RTSemFastMutexRelease(pIntNet->FastMutex);
/*
* Wakeup anyone waiting on this interface.
*
* We *must* make sure they have woken up properly and realized
* that the interface is no longer valid.
*/
if (pIf->Event != NIL_RTSEMEVENT)
{
RTSEMEVENT Event = pIf->Event;
unsigned cMaxWait = 0x1000;
while (pIf->cSleepers && cMaxWait-- > 0)
{
RTSemEventSignal(Event);
RTThreadYield();
}
if (pIf->cSleepers)
{
RTThreadSleep(1);
cMaxWait = pIf->cSleepers;
while (pIf->cSleepers && cMaxWait-- > 0)
{
RTSemEventSignal(Event);
RTThreadSleep(10);
}
}
RTSemEventDestroy(Event);
pIf->Event = NIL_RTSEMEVENT;
}
/*
* Unmap user buffer.
*/
if (pIf->pIntBuf != pIf->pIntBufDefault)
{
/** @todo user buffer */
}
/*
* Unmap and Free the default buffer.
*/
if (pIf->pIntBufDefault)
{
SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
pIf->pIntBufDefault = NULL;
pIf->pIntBufDefaultR3 = 0;
pIf->pIntBuf = NULL;
pIf->pIntBufR3 = 0;
}
/*
* The interface.
*/
pIf->pvObj = NULL;
RTMemFree(pIf);
}
/**
* 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
* can close it.
*
* @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.
*/
static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv, bool *pfCloseNetwork, PINTNETIFHANDLE phIf)
{
LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
pNetwork, pSession, cbSend, cbRecv, phIf));
/*
* Assert input.
*/
AssertPtr(pNetwork);
AssertPtr(phIf);
AssertPtr(pfCloseNetwork);
*pfCloseNetwork = false;
/*
* Allocate and initialize the interface structure.
*/
PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
if (!pIf)
return VERR_NO_MEMORY;
//pIf->pNext = NULL;
memset(&pIf->Mac, 0xff, sizeof(pIf->Mac)); /* broadcast */
//pIf->fMacSet = false;
//pIf->fPromiscuous = false;
//pIf->fActive = false;
//pIf->fDestroying = false;
//pIf->pIntBuf = 0;
//pIf->pIntBufR3 = NIL_RTR3PTR;
//pIf->pIntBufDefault = 0;
//pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
//pIf->cYields = 0;
pIf->Event = NIL_RTSEMEVENT;
//pIf->cSleepers = 0;
pIf->hIf = INTNET_HANDLE_INVALID;
pIf->pNetwork = pNetwork;
pIf->pSession = pSession;
//pIf->pvObj = NULL;
//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_IPv4].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv4);
pIf->aAddrCache[kIntNetAddrType_IPv4].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv4);
//pIf->aAddrCache[kIntNetAddrType_IPv6].pbEntries = NULL;
//pIf->aAddrCache[kIntNetAddrType_IPv6].cEntries = 0;
//pIf->aAddrCache[kIntNetAddrType_IPv6].cEntriesAlloc = 0;
pIf->aAddrCache[kIntNetAddrType_IPv6].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv6);
pIf->aAddrCache[kIntNetAddrType_IPv6].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv6);
//pIf->aAddrCache[kIntNetAddrType_IPX].pbEntries = NULL;
//pIf->aAddrCache[kIntNetAddrType_IPX].cEntries = 0;
//pIf->aAddrCache[kIntNetAddrType_IPX].cEntriesAlloc = 0;
pIf->aAddrCache[kIntNetAddrType_IPX].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPX);
pIf->aAddrCache[kIntNetAddrType_IPX].cbEntry = RT_ALIGN_32(intnetR0AddrSize(kIntNetAddrType_IPv4), 16);
int rc = RTSemEventCreate((PRTSEMEVENT)&pIf->Event);
if (RT_SUCCESS(rc))
{
/*
* Create the default buffer.
*/
/** @todo adjust with minimums and apply defaults here. */
cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR)) + cbRecv + cbSend;
rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
if (RT_SUCCESS(rc))
{
ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
pIf->pIntBuf = pIf->pIntBufDefault;
pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
pIf->pIntBuf->cbBuf = cbBuf;
pIf->pIntBuf->cbRecv = cbRecv;
pIf->pIntBuf->cbSend = cbSend;
/* receive ring buffer. */
pIf->pIntBuf->Recv.offStart = RT_ALIGN_32(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR));
pIf->pIntBuf->Recv.offRead = pIf->pIntBuf->Recv.offStart;
pIf->pIntBuf->Recv.offWrite = pIf->pIntBuf->Recv.offStart;
pIf->pIntBuf->Recv.offEnd = pIf->pIntBuf->Recv.offStart + cbRecv;
/* send ring buffer. */
pIf->pIntBuf->Send.offStart = pIf->pIntBuf->Recv.offEnd;
pIf->pIntBuf->Send.offRead = pIf->pIntBuf->Send.offStart;
pIf->pIntBuf->Send.offWrite = pIf->pIntBuf->Send.offStart;
pIf->pIntBuf->Send.offEnd = pIf->pIntBuf->Send.offStart + cbSend;
/*
* Link the interface to the network.
*/
rc = RTSemFastMutexRequest(pNetwork->FastMutex);
if (RT_SUCCESS(rc))
{
pIf->pNext = pNetwork->pIFs;
pNetwork->pIFs = pIf;
RTSemFastMutexRelease(pNetwork->FastMutex);
/*
* Register the interface with the session.
*/
pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, intnetR0IfDestruct, pIf, pNetwork->pIntNet);
if (pIf->pvObj)
{
rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
if (RT_SUCCESS(rc))
{
*phIf = pIf->hIf;
Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
*phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
return VINF_SUCCESS;
}
SUPR0ObjRelease(pIf->pvObj, pSession);
LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
return rc;
}
RTSemFastMutexDestroy(pNetwork->FastMutex);
pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
}
SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
pIf->pIntBufDefault = NULL;
pIf->pIntBuf = NULL;
}
RTSemEventDestroy(pIf->Event);
pIf->Event = NIL_RTSEMEVENT;
}
RTMemFree(pIf);
LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
*pfCloseNetwork = true;
return rc;
}
#ifdef RT_WITH_W64_UNWIND_HACK
# if defined(RT_OS_WINDOWS) && defined(RT_ARCH_AMD64)
# define INTNET_DECL_CALLBACK(type) DECLASM(DECLHIDDEN(type))
# define INTNET_CALLBACK(_n) intnetNtWrap##_n
/* wrapper callback declarations */
INTNET_DECL_CALLBACK(bool) INTNET_CALLBACK(intnetR0TrunkIfPortSetSGPhys)(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable);
INTNET_DECL_CALLBACK(bool) INTNET_CALLBACK(intnetR0TrunkIfPortRecv)(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc);
INTNET_DECL_CALLBACK(void) INTNET_CALLBACK(intnetR0TrunkIfPortSGRetain)(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG);
INTNET_DECL_CALLBACK(void) INTNET_CALLBACK(intnetR0TrunkIfPortSGRelease)(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG);
# else
# error "UNSUPPORTED (RT_WITH_W64_UNWIND_HACK)"
# endif
#else
# define INTNET_DECL_CALLBACK(_t) static DECLCALLBACK(_t)
# define INTNET_CALLBACK(_n) _n
#endif
/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
INTNET_DECL_CALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
}
/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
INTNET_DECL_CALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
PINTNETNETWORK pNetwork = pThis->pNetwork;
/* assert some sanity */
AssertPtrReturn(pNetwork, false);
AssertReturn(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX, false);
AssertPtr(pSG);
Assert(fSrc);
/*
* Lock the network and send the frame to it.
*/
int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
AssertRCReturn(rc, false);
bool fRc;
if (RT_LIKELY(pNetwork->cActiveIFs > 0))
fRc = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, false /* fTrunkLocked */);
else
fRc = false; /* don't drop it */
rc = RTSemFastMutexRelease(pNetwork->FastMutex);
AssertRC(rc);
return fRc;
}
/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
INTNET_DECL_CALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
PINTNETNETWORK pNetwork = pThis->pNetwork;
/* assert some sanity */
AssertPtrReturnVoid(pNetwork);
AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
AssertPtr(pSG);
Assert(pSG->cUsers > 0);
/* do it. */
++pSG->cUsers;
}
/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
INTNET_DECL_CALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
PINTNETNETWORK pNetwork = pThis->pNetwork;
/* assert some sanity */
AssertPtrReturnVoid(pNetwork);
AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
AssertPtr(pSG);
Assert(pSG->cUsers > 0);
/*
* Free it?
*/
if (!--pSG->cUsers)
{
/** @todo later */
}
}
/**
* Retain the trunk interface.
*
* @returns pThis if retained.
*
* @param pThis The trunk.
*
* @remarks Any locks.
*/
static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis)
{
if (pThis && pThis->pIfPort)
{
pThis->pIfPort->pfnRetain(pThis->pIfPort);
return pThis;
}
return NULL;
}
/**
* Release the trunk interface.
*
* @param pThis The trunk.
*/
static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis)
{
if (pThis && pThis->pIfPort)
pThis->pIfPort->pfnRelease(pThis->pIfPort);
}
/**
* Takes the out-bound trunk lock.
*
* This will ensure that pIfPort is valid.
*
* @returns success indicator.
* @param pThis The trunk.
*
* @remarks No locks other than the create/destroy one.
*/
static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis)
{
AssertPtrReturn(pThis, false);
int rc = RTSemFastMutexRequest(pThis->FastMutex);
if (RT_SUCCESS(rc))
{
if (RT_LIKELY(pThis->pIfPort))
return true;
RTSemFastMutexRelease(pThis->FastMutex);
}
else
AssertMsg(rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc));
return false;
}
/**
* Releases the out-bound trunk lock.
*
* @param pThis The trunk.
*/
static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis)
{
if (pThis)
{
int rc = RTSemFastMutexRelease(pThis->FastMutex);
AssertRC(rc);
}
}
/**
* Activates the trunk interface.
*
* @param pThis The trunk.
* @param fActive What to do with it.
*
* @remarks Caller may only own the create/destroy lock.
*/
static void intnetR0TrunkIfActivate(PINTNETTRUNKIF pThis, bool fActive)
{
if (intnetR0TrunkIfOutLock(pThis))
{
pThis->pIfPort->pfnSetActive(pThis->pIfPort, fActive);
intnetR0TrunkIfOutUnlock(pThis);
}
}
/**
* Shutdown the trunk interface.
*
* @param pThis The trunk.
* @param pNetworks The network.
*
* @remarks The caller must *NOT* hold the network lock. The global
* create/destroy lock is fine though.
*/
static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
{
/* assert sanity */
if (!pThis)
return;
AssertPtr(pThis);
Assert(pThis->pNetwork == pNetwork);
AssertPtrNull(pThis->pIfPort);
/*
* The interface has already been deactivated, we just to wait for
* it to become idle before we can disconnect and release it.
*/
PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
if (pIfPort)
{
intnetR0TrunkIfOutLock(pThis);
/* unset it */
pThis->pIfPort = NULL;
/* wait in portions so we can complain ever now an then. */
uint64_t StartTS = RTTimeSystemNanoTS();
int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
if (RT_FAILURE(rc))
{
LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
Assert(rc == VERR_TIMEOUT);
while ( RT_FAILURE(rc)
&& RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
if (rc == VERR_TIMEOUT)
{
LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
while ( rc == VERR_TIMEOUT
&& RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
if (RT_FAILURE(rc))
{
LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
AssertRC(rc);
}
}
}
/* disconnect & release it. */
pIfPort->pfnDisconnectAndRelease(pIfPort);
}
/*
* Free up the resources.
*/
RTSEMFASTMUTEX FastMutex = pThis->FastMutex;
pThis->FastMutex = NIL_RTSEMFASTMUTEX;
pThis->pNetwork = NULL;
RTSemFastMutexRelease(FastMutex);
RTSemFastMutexDestroy(FastMutex);
RTMemFree(pThis);
}
/**
* Creates the trunk connection (if any).
*
* @returns VBox status code.
*
* @param pNetwork The newly created network.
* @param pSession The session handle.
*/
static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
{
const char *pszName;
switch (pNetwork->enmTrunkType)
{
/*
* The 'None' case, simple.
*/
case kIntNetTrunkType_None:
case kIntNetTrunkType_WhateverNone:
return VINF_SUCCESS;
/* Can't happen, but makes GCC happy. */
default:
return VERR_NOT_IMPLEMENTED;
/*
* Translate enum to component factory name.
*/
case kIntNetTrunkType_NetFlt:
pszName = "VBoxNetFlt";
break;
case kIntNetTrunkType_NetAdp:
pszName = "VBoxNetAdp";
break;
case kIntNetTrunkType_SrvNat:
pszName = "VBoxSrvNat";
break;
}
/*
* Allocate the trunk interface.
*/
PINTNETTRUNKIF pTrunkIF = (PINTNETTRUNKIF)RTMemAllocZ(sizeof(*pTrunkIF));
if (!pTrunkIF)
return VERR_NO_MEMORY;
pTrunkIF->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
pTrunkIF->SwitchPort.pfnSetSGPhys = INTNET_CALLBACK(intnetR0TrunkIfPortSetSGPhys);
pTrunkIF->SwitchPort.pfnRecv = INTNET_CALLBACK(intnetR0TrunkIfPortRecv);
pTrunkIF->SwitchPort.pfnSGRetain = INTNET_CALLBACK(intnetR0TrunkIfPortSGRetain);
pTrunkIF->SwitchPort.pfnSGRelease = INTNET_CALLBACK(intnetR0TrunkIfPortSGRelease);
pTrunkIF->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
//pTrunkIF->pIfPort = NULL;
pTrunkIF->pNetwork = pNetwork;
//pTrunkIF->fPhysSG = false;
//pTrunkIF->fPromiscuousWire = false;
pTrunkIF->CachedMac.au8[0] = 0xfe;
pTrunkIF->CachedMac.au8[1] = 0xff;
pTrunkIF->CachedMac.au8[2] = 0xff;
pTrunkIF->CachedMac.au8[3] = 0xff;
pTrunkIF->CachedMac.au8[4] = 0xff;
pTrunkIF->CachedMac.au8[5] = 0xff;
int rc = RTSemFastMutexCreate(&pTrunkIF->FastMutex);
if (RT_SUCCESS(rc))
{
#ifdef IN_RING0 /* (testcase is ring-3) */
/*
* Query the factory we want, then use it create and connect the trunk.
*/
PINTNETTRUNKFACTORY pTrunkFactory = NULL;
rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
if (RT_SUCCESS(rc))
{
rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory, pNetwork->szTrunk, &pTrunkIF->SwitchPort, &pTrunkIF->pIfPort, !!(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE));
pTrunkFactory->pfnRelease(pTrunkFactory);
if (RT_SUCCESS(rc))
{
Assert(pTrunkIF->pIfPort);
pNetwork->pTrunkIF = pTrunkIF;
Log(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s%s Network=%s\n",
pszName, pNetwork->szTrunk, pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE ? " shared-mac" : "", pNetwork->szName));
return VINF_SUCCESS;
}
}
#endif /* IN_RING0 */
RTSemFastMutexDestroy(pTrunkIF->FastMutex);
}
RTMemFree(pTrunkIF);
LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
rc, pszName, pNetwork->szTrunk, pNetwork->szName));
return rc;
}
/**
* Close a network which was opened/created using intnetR0OpenNetwork()/intnetR0CreateNetwork().
*
* @param pNetwork The network to close.
* @param pSession The session handle.
*/
static int intnetR0NetworkClose(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
{
LogFlow(("intnetR0NetworkClose: pNetwork=%p pSession=%p\n", pNetwork, pSession));
AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
AssertPtrReturn(pNetwork, VERR_INVALID_PARAMETER);
int rc = SUPR0ObjRelease(pNetwork->pvObj, pSession);
LogFlow(("intnetR0NetworkClose: return %Rrc\n", rc));
return 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.
*/
static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
{
PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
PINTNET pIntNet = (PINTNET)pvUser2;
Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
Assert(pNetwork->pIntNet == pIntNet);
/* take the create/destroy sem. */
RTSemFastMutexRequest(pIntNet->FastMutex);
/*
* Deactivate the trunk connection first (if any).
*/
if (pNetwork->pTrunkIF)
intnetR0TrunkIfActivate(pNetwork->pTrunkIF, false /* fActive */);
/*
* Unlink the network.
* Note that it needn't be in the list if we failed during creation.
*/
PINTNETNETWORK pPrev = pIntNet->pNetworks;
if (pPrev == pNetwork)
pIntNet->pNetworks = pNetwork->pNext;
else
{
for (; pPrev; pPrev = pPrev->pNext)
if (pPrev->pNext == pNetwork)
{
pPrev->pNext = pNetwork->pNext;
break;
}
}
pNetwork->pNext = NULL;
pNetwork->pvObj = NULL;
/*
* 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.
*/
RTSemFastMutexRequest(pNetwork->FastMutex);
PINTNETIF pCur = pNetwork->pIFs;
while (pCur)
{
PINTNETIF pNext = pCur->pNext;
pCur->pNext = NULL;
pCur->pNetwork = NULL;
pCur = pNext;
}
/* Grab and zap the trunk pointer before leaving the mutex. */
PINTNETTRUNKIF pTrunkIF = pNetwork->pTrunkIF;
pNetwork->pTrunkIF = NULL;
RTSemFastMutexRelease(pNetwork->FastMutex);
/*
* If there is a trunk, delete it.
* Note that this may tak a while if we're unlucky...
*/
if (pTrunkIF)
intnetR0TrunkIfDestroy(pTrunkIF, pNetwork);
/*
* Free resources.
*/
RTSemFastMutexDestroy(pNetwork->FastMutex);
pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
RTMemFree(pNetwork);
/* release the create/destroy sem. (can be done before trunk destruction.) */
RTSemFastMutexRelease(pIntNet->FastMutex);
}
/**
* 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.
*/
static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
{
LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
/* just pro forma validation, the caller is internal. */
AssertPtr(pIntNet);
AssertPtr(pSession);
AssertPtr(pszNetwork);
Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
AssertPtr(pszTrunk);
Assert(!(fFlags & ~(INTNET_OPEN_FLAGS_MASK)));
AssertPtr(ppNetwork);
*ppNetwork = NULL;
/*
* Search networks by name.
*/
PINTNETNETWORK pCur;
uint8_t cchName = strlen(pszNetwork);
Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
pCur = pIntNet->pNetworks;
while (pCur)
{
if ( pCur->cchName == cchName
&& !memcmp(pCur->szName, pszNetwork, cchName))
{
/*
* Found the network, now check that we have the same ideas
* about the trunk setup and security.
*/
int rc;
if ( enmTrunkType == kIntNetTrunkType_WhateverNone
|| ( pCur->enmTrunkType == enmTrunkType
&& !strcmp(pCur->szTrunk, pszTrunk)))
{
if (!((pCur->fFlags ^ fFlags) & INTNET_OPEN_FLAGS_COMPATIBILITY_XOR_MASK))
{
/*
* Increment the reference and check that the session
* can access this network.
*/
rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
if (RT_SUCCESS(rc))
{
if (!(pCur->fFlags & INTNET_OPEN_FLAGS_PUBLIC))
rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
if (RT_SUCCESS(rc))
{
pCur->fFlags |= fFlags & INTNET_OPEN_FLAGS_SECURITY_OR_MASK;
*ppNetwork = pCur;
}
else
SUPR0ObjRelease(pCur->pvObj, pSession);
}
else if (rc == VERR_WRONG_ORDER)
rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
}
else
rc = VERR_INTNET_INCOMPATIBLE_FLAGS;
}
else
rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
return rc;
}
pCur = pCur->pNext;
}
LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
return VERR_NOT_FOUND;
}
/**
* Creates a new network.
*
* 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.
*/
static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
{
LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
/* just pro forma validation, the caller is internal. */
AssertPtr(pIntNet);
AssertPtr(pSession);
AssertPtr(pszNetwork);
Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
AssertPtr(pszTrunk);
Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
AssertPtr(ppNetwork);
*ppNetwork = NULL;
/*
* Allocate and initialize.
*/
size_t cb = sizeof(INTNETNETWORK);
if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
cb += INTNETNETWORK_TMP_SIZE + 64;
PINTNETNETWORK pNew = (PINTNETNETWORK)RTMemAllocZ(cb);
if (!pNew)
return VERR_NO_MEMORY;
int rc = RTSemFastMutexCreate(&pNew->FastMutex);
if (RT_SUCCESS(rc))
{
//pNew->pIFs = NULL;
pNew->pIntNet = pIntNet;
//pNew->cActiveIFs = 0;
pNew->fFlags = fFlags;
size_t cchName = strlen(pszNetwork);
pNew->cchName = cchName;
Assert(cchName && cchName < sizeof(pNew->szName)); /* caller's responsibility. */
memcpy(pNew->szName, pszNetwork, cchName); /* '\0' by alloc. */
pNew->enmTrunkType = enmTrunkType;
Assert(strlen(pszTrunk) < sizeof(pNew->szTrunk)); /* caller's responsibility. */
strcpy(pNew->szTrunk, pszTrunk);
if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
pNew->pbTmp = RT_ALIGN_PT(pNew + 1, 64, uint8_t *);
//else
// pNew->pbTmp = NULL;
/*
* Register the object in the current session and link it into the network list.
*/
pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNew, pIntNet);
if (pNew->pvObj)
{
pNew->pNext = pIntNet->pNetworks;
pIntNet->pNetworks = pNew;
/*
* 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.
*/
rc = SUPR0ObjVerifyAccess(pNew->pvObj, pSession, pNew->szName);
if (RT_SUCCESS(rc))
{
/*
* Connect the trunk.
*/
rc = intnetR0NetworkCreateTrunkIf(pNew, pSession);
if (RT_SUCCESS(rc))
{
*ppNetwork = pNew;
LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNew));
return VINF_SUCCESS;
}
}
/*
* We unlink it here so it cannot be opened when the caller leaves
* INTNET::FastMutex before dereferencing it.
*/
Assert(pIntNet->pNetworks == pNew);
pIntNet->pNetworks = pNew->pNext;
pNew->pNext = NULL;
*ppNetwork = pNew;
LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
return rc;
}
rc = VERR_NO_MEMORY;
RTSemFastMutexDestroy(pNew->FastMutex);
pNew->FastMutex = NIL_RTSEMFASTMUTEX;
}
RTMemFree(pNew);
LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
return 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.
*/
INTNETR0DECL(int) INTNETR0Open(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork,
INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
unsigned cbSend, unsigned cbRecv, PINTNETIFHANDLE phIf)
{
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));
/*
* Validate input.
*/
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
size_t cchNetwork = pszNetworkEnd - pszNetwork;
AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
if (pszTrunk)
{
AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
const char *pszTrunkEnd = (const char *)memchr(pszTrunk, '\0', INTNET_MAX_TRUNK_NAME);
AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
}
else
pszTrunk = "";
AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
switch (enmTrunkType)
{
case kIntNetTrunkType_None:
case kIntNetTrunkType_WhateverNone:
AssertReturn(!*pszTrunk, VERR_INVALID_PARAMETER);
break;
case kIntNetTrunkType_NetFlt:
AssertReturn(pszTrunk, VERR_INVALID_PARAMETER);
break;
default:
return VERR_NOT_IMPLEMENTED;
}
AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
/*
* Acquire the mutex to serialize open/create.
*/
int rc = RTSemFastMutexRequest(pIntNet->FastMutex);
if (RT_FAILURE(rc))
return rc;
/*
* 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.
*/
PINTNETNETWORK pNetwork = NULL;
rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
{
bool fCloseNetwork = true;
if (rc == VERR_NOT_FOUND)
rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
if (RT_SUCCESS(rc))
rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, &fCloseNetwork, phIf);
RTSemFastMutexRelease(pIntNet->FastMutex);
if (RT_FAILURE(rc) && pNetwork && fCloseNetwork)
intnetR0NetworkClose(pNetwork, pSession);
}
else
RTSemFastMutexRelease(pIntNet->FastMutex);
LogFlow(("INTNETR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
return rc;
}
/**
* 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.
*/
INTNETR0DECL(int) INTNETR0OpenReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return INTNETR0Open(pIntNet, pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
}
/**
* Destroys an instance of the Ring-0 internal networking service.
*
* @param pIntNet Pointer to the instance data.
*/
INTNETR0DECL(void) INTNETR0Destroy(PINTNET pIntNet)
{
LogFlow(("INTNETR0Destroy: pIntNet=%p\n", pIntNet));
/*
* Allow NULL pointers.
*/
if (!pIntNet)
return;
AssertPtrReturnVoid(pIntNet);
/*
* There is not supposed to be any networks hanging around at this time.
*/
Assert(pIntNet->pNetworks == NULL);
if (pIntNet->FastMutex != NIL_RTSEMFASTMUTEX)
{
RTSemFastMutexDestroy(pIntNet->FastMutex);
pIntNet->FastMutex = NIL_RTSEMFASTMUTEX;
}
if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
{
/** @todo does it make sense to have a deleter here? */
RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
pIntNet->hHtIfs = NIL_RTHANDLETABLE;
}
RTMemFree(pIntNet);
}
/**
* Create an instance of the Ring-0 internal networking service.
*
* @returns VBox status code.
* @param ppIntNet Where to store the instance pointer.
*/
INTNETR0DECL(int) INTNETR0Create(PINTNET *ppIntNet)
{
LogFlow(("INTNETR0Create: ppIntNet=%p\n", ppIntNet));
int rc = VERR_NO_MEMORY;
PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
if (pIntNet)
{
//pIntNet->pNetworks = NULL;
rc = RTSemFastMutexCreate(&pIntNet->FastMutex);
if (RT_SUCCESS(rc))
{
rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
if (RT_SUCCESS(rc))
{
*ppIntNet = pIntNet;
LogFlow(("INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n", pIntNet));
return VINF_SUCCESS;
}
RTSemFastMutexDestroy(pIntNet->FastMutex);
}
RTMemFree(pIntNet);
}
*ppIntNet = NULL;
LogFlow(("INTNETR0Create: returns %Rrc\n", rc));
return rc;
}