SrvIntNetR0.cpp revision a91aecdafa378380347ffab1e48b428b7ec37747
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/* $Id$ */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** @file
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync * Internal networking - The ring 0 service.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Copyright (C) 2006-2010 Oracle Corporation
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
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
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*******************************************************************************
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync* Header Files *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync*******************************************************************************/
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#define LOG_GROUP LOG_GROUP_SRV_INTNET
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/intnet.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/intnetinline.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/pdmnetinline.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/sup.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/pdm.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <VBox/log.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/asm.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/assert.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/handletable.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/mp.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/mem.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/net.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/semaphore.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/spinlock.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/string.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/thread.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#include <iprt/time.h>
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*******************************************************************************
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync* Defined Constants And Macros *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync*******************************************************************************/
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** @def INTNET_WITH_DHCP_SNOOPING
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#define INTNET_WITH_DHCP_SNOOPING
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** The maximum number of interface in a network. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#define INTNET_MAX_IFS (1023 + 1 + 16)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** The number of entries to grow the destination tables with. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#if 0
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync# define INTNET_GROW_DSTTAB_SIZE 16
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#else
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync# define INTNET_GROW_DSTTAB_SIZE 1
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync#endif
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** The wakeup bit in the INTNETIF::cBusy and INTNETRUNKIF::cBusy counters. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#define INTNET_BUSY_WAKEUP_MASK RT_BIT_32(30)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync/*******************************************************************************
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync* Structures and Typedefs *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync*******************************************************************************/
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * MAC address lookup table entry.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef struct INTNETMACTABENTRY
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The MAC address of this entry. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync RTMAC MacAddr;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Is it promiscuous. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync bool fPromiscuous;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Is it active.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * We ignore the entry if this is clear and may end up sending packets addressed
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * to this interface onto the trunk. The reasoning for this is that this could
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * be the interface of a VM that just has been teleported to a different host. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync bool fActive;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Pointer to the network interface. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync struct INTNETIF *pIf;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync} INTNETMACTABENTRY;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** Pointer to a MAC address lookup table entry. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef INTNETMACTABENTRY *PINTNETMACTABENTRY;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * MAC address lookup table.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @todo Having this in a separate structure didn't work out as well as it
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * should. Consider merging it into INTNETNETWORK.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef struct INTNETMACTAB
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The current number of entries. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint32_t cEntries;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The number of entries we've allocated space for. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint32_t cEntriesAllocated;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Table entries. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync PINTNETMACTABENTRY paEntries;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The host MAC address (reported). */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync RTMAC HostMac;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The host promisucous setting (reported). */
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync bool fHostPromiscuous;
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync /** Whether the host is active. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync bool fHostActive;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Whether the wire is promiscuous (config). */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync bool fWirePromiscuous;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** Whether the wire is active. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync bool fWireActive;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Pointer to the the trunk interface. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync struct INTNETTRUNKIF *pTrunk;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync} INTNETMACTAB;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** Pointer to a MAC address . */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef INTNETMACTAB *PINTNETMACTAB;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Destination table.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsynctypedef struct INTNETDSTTAB
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync{
445661c86e95894713da707c6c9787b7507dfce6vboxsync /** The trunk destinations. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync uint32_t fTrunkDst;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** Pointer to the trunk interface (referenced) if fTrunkDst is non-zero. */
445661c86e95894713da707c6c9787b7507dfce6vboxsync struct INTNETTRUNKIF *pTrunk;
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync /** The number of destination interfaces. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync uint32_t cIfs;
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync /** The interfaces (referenced). Variable sized array. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync struct
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync {
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The destination interface. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync struct INTNETIF *pIf;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** Whether to replace the destination MAC address.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * This is used when sharing MAC address with the host on the wire(less). */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync bool fReplaceDstMac;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync } aIfs[1];
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync} INTNETDSTTAB;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Pointer to a destination table. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsynctypedef INTNETDSTTAB *PINTNETDSTTAB;
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync/** Pointer to a const destination table. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsynctypedef INTNETDSTTAB const *PCINTNETDSTTAB;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/** Network layer address type. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsynctypedef enum INTNETADDRTYPE
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync{
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** The invalid 0 entry. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync kIntNetAddrType_Invalid = 0,
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync /** IP version 4. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync kIntNetAddrType_IPv4,
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync /** IP version 6. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync kIntNetAddrType_IPv6,
c889bbab784ba8552102ce776b6c67b982017861vboxsync /** IPX. */
c889bbab784ba8552102ce776b6c67b982017861vboxsync kIntNetAddrType_IPX,
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync /** The end of the valid values. */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync kIntNetAddrType_End,
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** The usual 32-bit hack. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync kIntNetAddrType_32BitHack = 0x7fffffff
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync} INTNETADDRTYPE;
c5d2523548cc57504b829f53f1362b848a84542cvboxsync/** Pointer to a network layer address type. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsynctypedef INTNETADDRTYPE *PINTNETADDRTYPE;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Address and type.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef struct INTNETADDR
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The address type. */
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync INTNETADDRTYPE enmType;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The address. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync RTNETADDRU Addr;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync} INTNETADDR;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** Pointer to an address. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsynctypedef INTNETADDR *PINTNETADDR;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/** Pointer to a const address. */
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsynctypedef INTNETADDR const *PCINTNETADDR;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync/**
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * Address cache for a specific network layer.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef struct INTNETADDRCACHE
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Pointer to the table of addresses. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync uint8_t *pbEntries;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /** The number of valid address entries. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync uint8_t cEntries;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The number of allocated address entries. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint8_t cEntriesAlloc;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The address size. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint8_t cbAddress;
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync /** The size of an entry. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint8_t cbEntry;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync} INTNETADDRCACHE;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** Pointer to an address cache. */
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsynctypedef INTNETADDRCACHE *PINTNETADDRCACHE;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Pointer to a const address cache. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * A network interface.
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Unless explicitly stated, all members are protect by the network semaphore.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef struct INTNETIF
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** The MAC address.
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * This is shadowed by INTNETMACTABENTRY::MacAddr. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync RTMAC MacAddr;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** Set if the INTNET::MacAddr member has been explicitly set. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync bool fMacSet;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** Set if the interface is in promiscuous mode.
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * This is shadowed by INTNETMACTABENTRY::fPromiscuous. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync bool fPromiscuous;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Whether the interface is active or not.
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * This is shadowed by INTNETMACTABENTRY::fActive. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync bool fActive;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** Whether someone is currently in the destructor. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync bool volatile fDestroying;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Number of yields done to try make the interface read pending data.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * We will stop yielding when this reaches a threshold assuming that the VM is
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * paused or that it simply isn't worth all the delay. It is cleared when a
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * successful send has been done. */
ee6881e608d98c1ced98963aed047fc7644df794vboxsync uint32_t cYields;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Pointer to the current exchange buffer (ring-0). */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync PINTNETBUF pIntBuf;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Pointer to ring-3 mapping of the current exchange buffer. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync R3PTRTYPE(PINTNETBUF) pIntBufR3;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Pointer to the default exchange buffer for the interface. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync PINTNETBUF pIntBufDefault;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Pointer to ring-3 mapping of the default exchange buffer. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Event semaphore which a receiver/consumer thread will sleep on while
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync * waiting for data to arrive. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync RTSEMEVENT volatile hRecvEvent;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** Number of threads sleeping on the event semaphore. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync uint32_t cSleepers;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** The interface handle.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * should return with the appropriate error condition. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync INTNETIFHANDLE volatile hIf;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** Pointer to the network this interface is connected to.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * This is protected by the INTNET::hMtxCreateOpenDestroy. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync struct INTNETNETWORK *pNetwork;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** The session this interface is associated with. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync PSUPDRVSESSION pSession;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The SUPR0 object id. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync void *pvObj;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The network layer address cache. (Indexed by type, 0 entry isn't used.)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * This is protected by the address spinlock of the network. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Spinlock protecting the input (producer) side of the receive ring. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync RTSPINLOCK hRecvInSpinlock;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Busy count for tracking destination table references and active sends.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Usually incremented while owning the switch table spinlock. The 30th bit
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * is used to indicate wakeup. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync uint32_t volatile cBusy;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** The preallocated destination table.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * This is NULL when it's in use as a precaution against unserialized
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * transmitting. This is grown when new interfaces are added to the network. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync PINTNETDSTTAB volatile pDstTab;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync} INTNETIF;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Pointer to an internal network interface. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef INTNETIF *PINTNETIF;
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * A trunk interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef struct INTNETTRUNKIF
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The port interface we present to the component. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync INTNETTRUNKSWPORT SwitchPort;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The port interface we get from the component. */
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync PINTNETTRUNKIFPORT pIfPort;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Pointer to the network we're connect to.
1829a716128b3e2d42bcee064a15c553dbd44798vboxsync * This may be NULL if we're orphaned? */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync struct INTNETNETWORK *pNetwork;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The current MAC address for the interface. (reported)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Updated while owning the switch table spinlock. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync RTMAC MacAddr;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Whether to supply physical addresses with the outbound SGs. (reported) */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync bool fPhysSG;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Explicit alignment. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync bool fUnused;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** Busy count for tracking destination table references and active sends.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * Usually incremented while owning the switch table spinlock. The 30th bit
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * is used to indicate wakeup. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync uint32_t volatile cBusy;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Mask of destinations that pfnXmit cope with disabled preemption for. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint32_t fNoPreemptDsts;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The GSO capabilities of the wire destination. (reported) */
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync uint32_t fWireGsoCapabilites;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The GSO capabilities of the host destination. (reported)
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync * This is as bit map where each bit represents the GSO type with the same
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * number. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint32_t fHostGsoCapabilites;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Header buffer for when we're carving GSO frames. */
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync uint8_t abGsoHdrs[256];
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The destination table spinlock, interrupt safe.
5979821e2c16d1fcec5b9a3ef64e13246fc9a93avboxsync * Protects apTaskDstTabs and apIntDstTabs. */
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync RTSPINLOCK hDstTabSpinlock;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** The number of entries in apIntDstTabs. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync uint32_t cIntDstTabs;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** The task time destination tables.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @remarks intnetR0NetworkEnsureTabSpace and others ASSUMES this immediately
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * preceeds apIntDstTabs so that these two tables can be used as one
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * contiguous one. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync PINTNETDSTTAB apTaskDstTabs[2];
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The interrupt / disabled-preemption time destination tables.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * This is a variable sized array. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync PINTNETDSTTAB apIntDstTabs[1];
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync} INTNETTRUNKIF;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Pointer to a trunk interface. */
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsynctypedef INTNETTRUNKIF *PINTNETTRUNKIF;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync * Internal representation of a network.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsynctypedef struct INTNETNETWORK
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync{
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync /** The Next network in the chain.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * This is protected by the INTNET::hMtxCreateOpenDestroy. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync struct INTNETNETWORK *pNext;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** The spinlock protecting MacTab and INTNETTRUNKIF::aAddrCache.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Interrupt safe. */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync RTSPINLOCK hAddrSpinlock;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync /** MAC address table.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * This doubles as interface collection. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync INTNETMACTAB MacTab;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync /** Wait for an interface to stop being busy so it can be removed or have its
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * destination table replaced. We have to wait upon this while owning the
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * network mutex. Will only ever have one waiter because of the big mutex. */
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync RTSEMEVENT hEvtBusyIf;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Pointer to the instance data. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync struct INTNET *pIntNet;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** The SUPR0 object id. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync void *pvObj;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /** Pointer to the temporary buffer that is used when snooping fragmented packets.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * This is allocated after this structure if we're sharing the MAC address with
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundrary. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync uint8_t *pbTmp;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Network creation flags (INTNET_OPEN_FLAGS_*). */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync uint32_t fFlags;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** The number of active interfaces (excluding the trunk). */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync uint32_t cActiveIFs;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** The length of the network name. */
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync uint8_t cchName;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync /** The network name. */
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync char szName[INTNET_MAX_NETWORK_NAME];
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /** The trunk type. */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync INTNETTRUNKTYPE enmTrunkType;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** The trunk name. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync char szTrunk[INTNET_MAX_TRUNK_NAME];
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync} INTNETNETWORK;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Pointer to an internal network. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsynctypedef INTNETNETWORK *PINTNETNETWORK;
234af146205f61c4aa0be736abb06601a89facb8vboxsync
234af146205f61c4aa0be736abb06601a89facb8vboxsync/** The size of the buffer INTNETNETWORK::pbTmp points at. */
234af146205f61c4aa0be736abb06601a89facb8vboxsync#define INTNETNETWORK_TMP_SIZE 2048
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Internal networking instance.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsynctypedef struct INTNET
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Magic number (INTNET_MAGIC). */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync uint32_t volatile u32Magic;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /** Mutex protecting the creation, opening and destruction of both networks and
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * interfaces. (This means all operations affecting the pNetworks list.) */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync RTSEMMUTEX hMtxCreateOpenDestroy;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** List of networks. Protected by INTNET::Spinlock. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync PINTNETNETWORK volatile pNetworks;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /** Handle table for the interfaces. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync RTHANDLETABLE hHtIfs;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync} INTNET;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/** Pointer to an internal network ring-0 instance. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsynctypedef struct INTNET *PINTNET;
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Magic number for the internal network instance data (Hayao Miyazaki). */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync#define INTNET_MAGIC UINT32_C(0x19410105)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/*******************************************************************************
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync* Global Variables *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync*******************************************************************************/
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/** Pointer to the internal network instance data. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncstatic PINTNET volatile g_pIntNet = NULL;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/*******************************************************************************
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync* Internal Functions *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync*******************************************************************************/
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncstatic PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncstatic void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
8e3faac3e7a5ca790b1d82e9d019d6f48b8a61f2vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Worker for intnetR0SgWritePart that deals with the case where the
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * request doesn't fit into the first segment.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @returns true, unless the request or SG invalid.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pSG The SG list to write to.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param off Where to start writing (offset into the SG).
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param cb How much to write.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pvBuf The buffer to containing the bits to write.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncstatic bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync if (RT_UNLIKELY(off + cb > pSG->cbTotal))
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return false;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync /*
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * Skip ahead to the segment where off starts.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync unsigned iSeg = 0;
041d531fb5794a8a4cf6c35886d89ec25cbbdde2vboxsync while (off > pSG->aSegs[iSeg].cb)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync off -= pSG->aSegs[iSeg++].cb;
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync AssertReturn(iSeg < cSegs, false);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync }
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync /*
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * 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((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync else
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync {
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /* copy the portion in the current segment. */
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync cb -= cbCanCopy;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /* copy the portions in the other segments. */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync do
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync {
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync iSeg++;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync AssertReturn(iSeg < cSegs, false);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync cb -= cbCanCopy;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync } while (cb > 0);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync }
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return true;
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync}
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * Writes to a part of an SG.
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync *
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * @returns true on success, false on failure (out of bounds).
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * @param pSG The SG list to write to.
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * @param off Where to start writing (offset into the SG).
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param cb How much to write.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * @param pvBuf The buffer to containing the bits to write.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsyncDECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync{
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync Assert(off + cb > off);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync /* The optimized case. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_LIKELY( pSG->cSegsUsed == 1
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync || pSG->aSegs[0].cb >= off + cb))
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync Assert(pSG->cbTotal == pSG->aSegs[0].cb);
c5d2523548cc57504b829f53f1362b848a84542cvboxsync memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync return true;
c5d2523548cc57504b829f53f1362b848a84542cvboxsync }
c5d2523548cc57504b829f53f1362b848a84542cvboxsync return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
c5d2523548cc57504b829f53f1362b848a84542cvboxsync}
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
a9ed90fc7da3c371bc3b96c8535655b87836fcadvboxsync/**
8929a16e87a515b7071399479548158b8c5fbdd2vboxsync * Reads a byte from a SG list.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
ee6e48c229ef52aee5e968d956ebd066073811abvboxsync * @returns The byte on success. 0xff on failure.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pSG The SG list to read.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param off The offset (into the SG) off the byte.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
ee6e48c229ef52aee5e968d956ebd066073811abvboxsyncDECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_LIKELY(pSG->aSegs[0].cb > off))
e4527e0a08e2d635a679ae2947d42195f30a2ce2vboxsync return ((uint8_t const *)pSG->aSegs[0].pv)[off];
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync off -= pSG->aSegs[0].cb;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (pSG->aSegs[iSeg].cb > off)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync off -= pSG->aSegs[iSeg].cb;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync }
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return false;
7f18b4c79440e724c5e08f007674ea7883ce6c01vboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
db3d025f28c59aececbbda4174fa513496c89b2bvboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Worker for intnetR0SgReadPart that deals with the case where the
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * requested data isn't in the first segment.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns true, unless the SG is invalid.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pSG The SG list to read.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param off Where to start reading (offset into the SG).
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param cb How much to read.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @param pvBuf The buffer to read into.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsyncstatic bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (RT_UNLIKELY(off + cb > pSG->cbTotal))
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return false;
7f18b4c79440e724c5e08f007674ea7883ce6c01vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync /*
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Skip ahead to the segment where off starts.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync unsigned iSeg = 0;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync while (off > pSG->aSegs[iSeg].cb)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync off -= pSG->aSegs[iSeg++].cb;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync AssertReturn(iSeg < cSegs, false);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync }
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync /*
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Copy the data, hoping that it's all from one segment...
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (cbCanCopy >= cb)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync else
7f18b4c79440e724c5e08f007674ea7883ce6c01vboxsync {
7f18b4c79440e724c5e08f007674ea7883ce6c01vboxsync /* copy the portion in the current segment. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync cb -= cbCanCopy;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync /* copy the portions in the other segments. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync do
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync {
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync pvBuf = (uint8_t *)pvBuf + cbCanCopy;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync iSeg++;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync AssertReturn(iSeg < cSegs, false);
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f18b4c79440e724c5e08f007674ea7883ce6c01vboxsync cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
7f18b4c79440e724c5e08f007674ea7883ce6c01vboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync cb -= cbCanCopy;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync } while (cb > 0);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync }
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync return true;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * Reads a part of an SG into a buffer.
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync *
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * @returns true on success, false on failure (out of bounds).
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pSG The SG list to read.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param off Where to start reading (offset into the SG).
a2d29442b07952c0d2b4d2b703bf4c2d1eb072eavboxsync * @param cb How much to read.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pvBuf The buffer to read into.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync */
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsyncDECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync{
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync Assert(off + cb > off);
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync /* The optimized case. */
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync if (RT_LIKELY( pSG->cSegsUsed == 1
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync || pSG->aSegs[0].cb >= off + cb))
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync {
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync Assert(pSG->cbTotal == pSG->aSegs[0].cb);
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync return true;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync }
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Wait for a busy counter to reach zero.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pNetwork The network.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pcBusy The busy counter.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncstatic void intnetR0BusyWait(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync if (ASMAtomicReadU32(pcBusy) == 0)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync return;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /*
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * We have to be a bit cautious here so we don't destroy the network or the
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * semaphore before intnetR0BusyDec has signalled us.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /* Reset the semaphore and flip the wakeup bit. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync RTSemEventWait(pNetwork->hEvtBusyIf, 0); /* clear it */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync uint32_t cCurBusy = ASMAtomicReadU32(pcBusy);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync do
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync {
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync if (cCurBusy == 0)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync return;
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync AssertMsg(!(cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync } while (!ASMAtomicCmpXchgExU32(pcBusy, cCurBusy | INTNET_BUSY_WAKEUP_MASK, cCurBusy, &cCurBusy));
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync /* Wait for the count to reach zero. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync do
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync {
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync int rc2 = RTSemEventWait(pNetwork->hEvtBusyIf, 30000); NOREF(rc2);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync //AssertMsg(RT_SUCCESS(rc2), ("rc=%Rrc *pcBusy=%#x (%#x)\n", rc2, ASMAtomicReadU32(pcBusy), cCurBusy ));
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync cCurBusy = ASMAtomicReadU32(pcBusy);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync AssertMsg((cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync } while ( cCurBusy != INTNET_BUSY_WAKEUP_MASK
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync || ASMAtomicCmpXchgU32(pcBusy, 0, INTNET_BUSY_WAKEUP_MASK));
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * Decrements the busy counter and maybe wakes up any threads waiting for it to
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync * reach zero.
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pNetwork The network.
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync * @param pcBusy The busy counter.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsyncDECLINLINE(void) intnetR0BusyDec(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync uint32_t cNewBusy = ASMAtomicDecU32(pcBusy);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync if (RT_UNLIKELY( cNewBusy == INTNET_BUSY_WAKEUP_MASK
c5d2523548cc57504b829f53f1362b848a84542cvboxsync && pNetwork))
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync RTSemEventSignal(pNetwork->hEvtBusyIf);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync/**
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * Increments the busy count of the specified interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * The caller must own the MAC address table spinlock.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * @param pIf The interface.
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync */
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsyncDECLINLINE(void) intnetR0BusyDecIf(PINTNETIF pIf)
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync{
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync intnetR0BusyDec(pIf->pNetwork, &pIf->cBusy);
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Increments the busy count of the specified interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * The caller must own the MAC address table spinlock or an explicity reference.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pTrunk The trunk.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncDECLINLINE(void) intnetR0BusyDecTrunk(PINTNETTRUNKIF pTrunk)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync intnetR0BusyDec(pTrunk->pNetwork, &pTrunk->cBusy);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Increments the busy count of the specified interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * The caller must own the MAC address table spinlock or an explicity reference.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pIf The interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncDECLINLINE(void) intnetR0BusyIncIf(PINTNETIF pIf)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync uint32_t cNewBusy = ASMAtomicIncU32(&pIf->cBusy);
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync NOREF(cNewBusy);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Increments the busy count of the specified interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * The caller must own the MAC address table spinlock or an explicity reference.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pTrunk The trunk.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncDECLINLINE(void) intnetR0BusyIncTrunk(PINTNETTRUNKIF pTrunk)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync uint32_t cNewBusy = ASMAtomicIncU32(&pTrunk->cBusy);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync NOREF(cNewBusy);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * Retain an interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @returns VBox status code, can assume success in most situations.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pIf The interface instance.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pSession The current session.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncDECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync int rc = SUPR0ObjAddRefEx(pIf->pvObj, pSession, true /* fNoBlocking */);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync AssertRCReturn(rc, rc);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return VINF_SUCCESS;
c889bbab784ba8552102ce776b6c67b982017861vboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync/**
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * Release an interface previously retained by intnetR0IfRetain or
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * by handle lookup/freeing.
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns true if destroyed, false if not.
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * @param pIf The interface instance.
ad1e35f9fb71d147c2126449a25adf0f8e155aaavboxsync * @param pSession The current session.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsyncDECLINLINE(bool) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync AssertRC(rc);
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return rc == VINF_OBJECT_DESTROYED;
c889bbab784ba8552102ce776b6c67b982017861vboxsync}
c889bbab784ba8552102ce776b6c67b982017861vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/**
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * RTHandleCreateEx callback that retains an object in the
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * handle table before returning it.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync *
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * (Avoids racing the freeing of the handle.)
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync *
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @returns VBox status code.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @param hHandleTable The handle table (ignored).
c889bbab784ba8552102ce776b6c67b982017861vboxsync * @param pvObj The object (INTNETIF).
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pvCtx The context (SUPDRVSESSION).
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pvUser The user context (ignored).
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncstatic DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync NOREF(pvUser);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync NOREF(hHandleTable);
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync PINTNETIF pIf = (PINTNETIF)pvObj;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return VINF_SUCCESS;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync/**
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync * Checks if the interface has a usable MAC address or not.
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync *
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * @returns true if MacAddr is usable, false if not.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * @param pIf The interface.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncDECL_FORCE_INLINE(bool) intnetR0IfHasMacAddr(PINTNETIF pIf)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync return pIf->fMacSet || !(pIf->MacAddr.au8[0] & 1);
4d32c71a938b979c6aeee321ae325d5793a5ccdbvboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync
81fcf0038d2d6c76ab2c8b02103dc18c24efe0a1vboxsync/**
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * Locates the MAC address table entry for the given interface.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * The caller holds the MAC address table spinlock, obviously.
00cc5d93f7446d9394a0f6b7ad790b9fb9d6005cvboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @returns Pointer to the entry on if found, NULL if not.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @param pNetwork The network.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pIf The interface.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync */
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsyncDECLINLINE(PINTNETMACTABENTRY) intnetR0NetworkFindMacAddrEntry(PINTNETNETWORK pNetwork, PINTNETIF pIf)
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync{
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync uint32_t iIf = pNetwork->MacTab.cEntries;
c889bbab784ba8552102ce776b6c67b982017861vboxsync while (iIf-- > 0)
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync {
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync return &pNetwork->MacTab.paEntries[iIf];
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync }
c889bbab784ba8552102ce776b6c67b982017861vboxsync return NULL;
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync}
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync/**
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * Checks if the IPv4 address is a broadcast address.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @returns true/false.
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync * @param Addr The address, network endian.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync */
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsyncDECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
ab02aeaf2a0312fe9267a292e3911728e4531332vboxsync /* Just check for 255.255.255.255 atm. */
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync return Addr.u == UINT32_MAX;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync}
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsync/**
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync * Checks if the IPv4 address is a good interface address.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @returns true/false.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * @param Addr The address, network endian.
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsyncDECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync{
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /* Usual suspects. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync || Addr.au8[0] == 0) /* Current network, can be used as source address. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return false;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync /* Unusual suspects. */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync || (Addr.au8[0] & 0xf0) == 224 /* Multicast */
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync ))
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return false;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync return true;
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync}
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Gets the address size of a network layer type.
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync *
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync * @returns size in bytes.
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync * @param enmType The type.
31cda52093d58f5c604589fa74949c5fddcbde70vboxsync */
5eda82e218d35ae0691febd531e1bfc0324cc4a6vboxsyncDECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync{
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync switch (enmType)
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync {
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync case kIntNetAddrType_IPv4: return 4;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync case kIntNetAddrType_IPv6: return 16;
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync case kIntNetAddrType_IPX: return 4 + 6;
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync default: AssertFailedReturn(0);
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync }
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync}
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync
d60d5da33bb93fc7a8717802f21b13aa37914799vboxsync/**
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync * Compares two address to see if they are equal, assuming naturally align structures.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @returns true if equal, false if not.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param pAddr1 The first address.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param pAddr2 The second address.
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync * @param cbAddr The address size.
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsyncDECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync{
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync switch (cbAddr)
6f058039e7b13efc3278b9bc84cf0fd135614b67vboxsync {
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync case 4: /* IPv4 */
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync return pAddr1->au32[0] == pAddr2->au32[0];
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync case 16: /* IPv6 */
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync return pAddr1->au64[0] == pAddr2->au64[0]
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync && pAddr1->au64[1] == pAddr2->au64[1];
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync case 10: /* IPX */
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync return pAddr1->au64[0] == pAddr2->au64[0]
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync && pAddr1->au16[4] == pAddr2->au16[4];
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync default:
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync AssertFailedReturn(false);
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync }
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync}
d9a86bd327291cd44d5f6da6e08bec728aae68bbvboxsync
82c6fd518728c98e8bae58f3c5e55c5efa160878vboxsync
e5bfc5c34142a7550be3564a8e01a037b1db5b31vboxsync/**
7f4b4ec1cadbf891d2756ce77dc4a4ec220a03bcvboxsync * Worker for intnetR0IfAddrCacheLookup that performs the lookup
ad836b4d6fb2ccaf969666246c2c389a8d10ca41vboxsync * in the remaining cache entries after the caller has check the
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * most likely ones.
ad836b4d6fb2ccaf969666246c2c389a8d10ca41vboxsync *
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync * @returns -1 if not found, the index of the cache entry if found.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * @param pCache The cache.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * @param pAddr The address.
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync * @param cbAddr The address size (optimization).
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync */
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsyncstatic int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync{
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync unsigned i = pCache->cEntries - 2;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync while (i >= 1)
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync {
d27bf03c13c7a5707386600ef9b0bbb82fb3420dvboxsync if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync return i;
ad836b4d6fb2ccaf969666246c2c389a8d10ca41vboxsync pbEntry -= pCache->cbEntry;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync i--;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync }
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync return -1;
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync}
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync
9669409ada28ab1b50aa2e7d6a64d2779a47f415vboxsync/**
ad836b4d6fb2ccaf969666246c2c389a8d10ca41vboxsync * Lookup an address in a cache without any expectations.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync *
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @returns -1 if not found, the index of the cache entry if found.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pCache The cache.
833f83ce101b6e9168f519decc0dc7a1079d35f7vboxsync * @param pAddr The address.
c889bbab784ba8552102ce776b6c67b982017861vboxsync * @param cbAddr The address size (optimization).
*/
DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
{
Assert(pCache->cbAddress == cbAddr);
/*
* The optimized case is when there is one cache entry and
* it doesn't match.
*/
unsigned i = pCache->cEntries;
if ( i > 0
&& intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
return 0;
if (i <= 1)
return -1;
/*
* Check the last entry.
*/
i--;
if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
return i;
if (i <= 1)
return -1;
return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
}
/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
{
/** @todo implement this. */
return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
}
/**
* Worker for intnetR0IfAddrCacheLookupUnlikely that performs
* the lookup in the remaining cache entries after the caller
* has check the most likely ones.
*
* The routine is expecting not to find the address.
*
* @returns -1 if not found, the index of the cache entry if found.
* @param pCache The cache.
* @param pAddr The address.
* @param cbAddr The address size (optimization).
*/
static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
{
/*
* Perform a full table lookup.
*/
unsigned i = pCache->cEntries - 2;
uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
while (i >= 1)
{
if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
return i;
pbEntry -= pCache->cbEntry;
i--;
}
return -1;
}
/**
* Lookup an address in a cache expecting not to find it.
*
* @returns -1 if not found, the index of the cache entry if found.
* @param pCache The cache.
* @param pAddr The address.
* @param cbAddr The address size (optimization).
*/
DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
{
Assert(pCache->cbAddress == cbAddr);
/*
* The optimized case is when there is one cache entry and
* it doesn't match.
*/
unsigned i = pCache->cEntries;
if (RT_UNLIKELY( i > 0
&& intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
return 0;
if (RT_LIKELY(i <= 1))
return -1;
/*
* Then check the last entry and return if there are just two cache entries.
*/
i--;
if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
return i;
if (i <= 1)
return -1;
return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
}
/**
* Deletes a specific cache entry.
*
* Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
*
* @param pIf The interface (for logging).
* @param pCache The cache.
* @param iEntry The entry to delete.
* @param pszMsg Log message.
*/
static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
{
AssertReturnVoid(iEntry < pCache->cEntries);
AssertReturnVoid(iEntry >= 0);
#ifdef LOG_ENABLED
INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
PCRTNETADDRU pAddr = (PCRTNETADDRU)(pCache->pbEntries + iEntry * pCache->cbEntry);
switch (enmAddrType)
{
case kIntNetAddrType_IPv4:
Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
pIf->hIf, &pIf->MacAddr, iEntry, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
break;
default:
Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
pIf->hIf, &pIf->MacAddr, enmAddrType, iEntry, pCache->cbAddress, pAddr, pszMsg));
break;
}
#endif
pCache->cEntries--;
if (iEntry < pCache->cEntries)
memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
(pCache->cEntries - iEntry) * pCache->cbEntry);
}
/**
* Deletes an address from the cache, assuming it isn't actually in the cache.
*
* May or may not own the spinlock when calling this.
*
* @param pIf The interface (for logging).
* @param pCache The cache.
* @param pAddr The address.
* @param cbAddr The address size (optimization).
*/
DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
{
int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
if (RT_UNLIKELY(i >= 0))
intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
}
/**
* Deletes the address from all the interface caches.
*
* This is used to remove stale entries that has been reassigned to
* other machines on the network.
*
* @param pNetwork The network.
* @param pAddr The address.
* @param enmType The address type.
* @param cbAddr The address size (optimization).
* @param pszMsg Log message.
*/
DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
uint8_t const cbAddr, const char *pszMsg)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf--)
{
PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
if (RT_UNLIKELY(i >= 0))
intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
/**
* Deletes the address from all the interface caches except the specified one.
*
* This is used to remove stale entries that has been reassigned to
* other machines on the network.
*
* @param pNetwork The network.
* @param pAddr The address.
* @param enmType The address type.
* @param cbAddr The address size (optimization).
* @param pszMsg Log message.
*/
DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf--)
{
PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
if (pIf != pIfSender)
{
int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
if (RT_UNLIKELY(i >= 0))
intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
/**
* 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. The caller
* must release the interface by calling intnetR0BusyDecIf.
* @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)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf--)
{
PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
if (i >= 0)
{
intnetR0BusyIncIf(pIf);
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return pIf;
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return NULL;
}
/**
* Adds an address to the cache, the caller is responsible for making sure it's
* not already in the cache.
*
* The caller must not
*
* @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)
{
PINTNETNETWORK pNetwork = pIf->pNetwork;
AssertReturnVoid(pNetwork);
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (RT_UNLIKELY(!pCache->cEntriesAlloc))
{
/* This shouldn't happen*/
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return;
}
/* When the table is full, drop the older entry (FIFO). Do proper ageing? */
if (pCache->cEntries >= pCache->cEntriesAlloc)
{
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--;
Assert(pCache->cEntries < pCache->cEntriesAlloc);
}
/*
* 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->MacAddr, 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->MacAddr, enmAddrType, pCache->cEntries, pCache->cbAddress, pAddr, pszMsg));
break;
}
#endif
pCache->cEntries++;
Assert(pCache->cEntries <= pCache->cEntriesAlloc);
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
/**
* 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.
*
* Must not own any spinlocks when calling this function.
*
* @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);
}
/**
* Destroys the specified address cache.
* @param pCache The address cache.
*/
static void intnetR0IfAddrCacheDestroy(PINTNETADDRCACHE pCache)
{
void *pvFree = pCache->pbEntries;
pCache->pbEntries = NULL;
pCache->cEntries = 0;
pCache->cEntriesAlloc = 0;
RTMemFree(pvFree);
}
/**
* Initialize the address cache for the specified address type.
*
* The cache storage is preallocated and fixed size so that we can handle
* inserts from problematic contexts.
*
* @returns VINF_SUCCESS or VERR_NO_MEMORY.
* @param pCache The cache to initialize.
* @param enmAddrType The address type.
* @param fEnabled Whether the address cache is enabled or not.
*/
static int intnetR0IfAddrCacheInit(PINTNETADDRCACHE pCache, INTNETADDRTYPE enmAddrType, bool fEnabled)
{
pCache->cEntries = 0;
pCache->cbAddress = intnetR0AddrSize(enmAddrType);
pCache->cbEntry = RT_ALIGN(pCache->cbAddress, 4);
if (fEnabled)
{
pCache->cEntriesAlloc = 32;
pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cEntriesAlloc * pCache->cbEntry);
if (!pCache->pbEntries)
return VERR_NO_MEMORY;
}
else
{
pCache->cEntriesAlloc = 0;
pCache->pbEntries = NULL;
}
return VINF_SUCCESS;
}
/**
* Is it a multicast or broadcast MAC address?
*
* @returns true if multicast, false if not.
* @param pMacAddr The address to inspect.
*/
DECL_FORCE_INLINE(bool) intnetR0IsMacAddrMulticast(PCRTMAC pMacAddr)
{
return !!(pMacAddr->au8[0] & 0x01);
}
/**
* Is it a dummy MAC address?
*
* We use dummy MAC addresses for interfaces which we don't know the MAC
* address of because they haven't sent anything (learning) or explicitly set
* it.
*
* @returns true if dummy, false if not.
* @param pMacAddr The address to inspect.
*/
DECL_FORCE_INLINE(bool) intnetR0IsMacAddrDummy(PCRTMAC pMacAddr)
{
/* The dummy address are broadcast addresses, don't bother check it all. */
return pMacAddr->au16[0] == 0xffff;
}
/**
* Compares two MAC addresses.
*
* @returns true if equal, false if not.
* @param pDstAddr1 Address 1.
* @param pDstAddr2 Address 2.
*/
DECL_FORCE_INLINE(bool) intnetR0AreMacAddrsEqual(PCRTMAC pDstAddr1, PCRTMAC pDstAddr2)
{
return pDstAddr1->au16[2] == pDstAddr2->au16[2]
&& pDstAddr1->au16[1] == pDstAddr2->au16[1]
&& pDstAddr1->au16[0] == pDstAddr2->au16[0];
}
/**
* Switch a unicast frame based on the network layer address (OSI level 3) and
* return a destination table.
*
* @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
* INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
* @param pNetwork The network to switch on.
* @param pDstMacAddr The destination MAC address.
* @param enmL3AddrType The level-3 destination address type.
* @param pL3Addr The level-3 destination address.
* @param cbL3Addr The size of the level-3 destination address.
* @param fSrc The frame source (INTNETTRUNKDIR_WIRE).
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSwitchLevel3(PINTNETNETWORK pNetwork, PCRTMAC pDstMacAddr,
INTNETADDRTYPE enmL3AddrType, PCRTNETADDRU pL3Addr, uint8_t cbL3Addr,
uint32_t fSrc, PINTNETDSTTAB pDstTab)
{
Assert(fSrc == INTNETTRUNKDIR_WIRE);
/*
* Grab the spinlock first and do the switching.
*/
PINTNETMACTAB pTab = &pNetwork->MacTab;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pDstTab->fTrunkDst = 0;
pDstTab->pTrunk = 0;
pDstTab->cIfs = 0;
/* Find exactly matching or promiscuous interfaces. */
uint32_t cExactHits = 0;
uint32_t iIfMac = pTab->cEntries;
while (iIfMac-- > 0)
{
if (pTab->paEntries[iIfMac].fActive)
{
PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
bool fExact = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmL3AddrType], pL3Addr, cbL3Addr) >= 0;
if (fExact || pTab->paEntries[iIfMac].fPromiscuous)
{
cExactHits += fExact;
uint32_t iIfDst = pDstTab->cIfs++;
pDstTab->aIfs[iIfDst].pIf = pIf;
pDstTab->aIfs[iIfDst].fReplaceDstMac = fExact;
intnetR0BusyIncIf(pIf);
}
}
}
/* Does it match the host, or is the host promiscuous? */
if (pTab->fHostActive)
{
bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstMacAddr);
if ( fExact
|| intnetR0IsMacAddrDummy(&pTab->HostMac)
|| pTab->fHostPromiscuous)
{
cExactHits += fExact;
pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
}
}
/* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
if (pTab->fWireActive && (!cExactHits || pTab->fWirePromiscuous))
pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
pDstTab->fTrunkDst &= ~fSrc;
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pTab->pTrunk;
pDstTab->pTrunk = pTrunk;
intnetR0BusyIncTrunk(pTrunk);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return pDstTab->cIfs
? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
: (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
}
/**
* Switch a unicast MAC address and return a destination table.
*
* @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
* INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
* @param pNetwork The network to switch on.
* @param fSrc The frame source.
* @param pIfSender The sender interface, NULL if trunk. Used to
* prevent sending an echo to the sender.
* @param pDstAddr The destination address of the frame.
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
PCRTMAC pDstAddr, PINTNETDSTTAB pDstTab)
{
AssertPtr(pDstTab);
Assert(!intnetR0IsMacAddrMulticast(pDstAddr));
/*
* Grab the spinlock first and do the switching.
*/
PINTNETMACTAB pTab = &pNetwork->MacTab;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pDstTab->fTrunkDst = 0;
pDstTab->pTrunk = 0;
pDstTab->cIfs = 0;
/* Find exactly matching or promiscuous interfaces. */
uint32_t cExactHits = 0;
uint32_t iIfMac = pTab->cEntries;
while (iIfMac-- > 0)
{
if (pTab->paEntries[iIfMac].fActive)
{
bool fExact = intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr);
if ( fExact
|| intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr)
|| pTab->paEntries[iIfMac].fPromiscuous)
{
cExactHits += fExact;
PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
if (RT_LIKELY(pIf != pIfSender)) /* paranoia */
{
uint32_t iIfDst = pDstTab->cIfs++;
pDstTab->aIfs[iIfDst].pIf = pIf;
pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
intnetR0BusyIncIf(pIf);
}
}
}
}
/* Does it match the host, or is the host promiscuous? */
if ( fSrc != INTNETTRUNKDIR_HOST
&& pTab->fHostActive)
{
bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstAddr);
if ( fExact
|| intnetR0IsMacAddrDummy(&pTab->HostMac)
|| pTab->fHostPromiscuous)
{
cExactHits += fExact;
pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
}
}
/* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
if ( fSrc != INTNETTRUNKDIR_WIRE
&& pTab->fWireActive
&& (!cExactHits || pTab->fWirePromiscuous)
)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
/* Grab the trunk if we're sending to it. */
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pTab->pTrunk;
pDstTab->pTrunk = pTrunk;
intnetR0BusyIncTrunk(pTrunk);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return pDstTab->cIfs
? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
: (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
}
/**
* Create a destination table for a broadcast frame.
*
* @returns INTNETSWDECISION_BROADCAST.
* @param pNetwork The network to switch on.
* @param fSrc The frame source.
* @param pIfSender The sender interface, NULL if trunk. Used to
* prevent sending an echo to the sender.
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSwitchBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
PINTNETDSTTAB pDstTab)
{
AssertPtr(pDstTab);
/*
* Grab the spinlock first and record all active interfaces.
*/
PINTNETMACTAB pTab = &pNetwork->MacTab;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pDstTab->fTrunkDst = 0;
pDstTab->pTrunk = 0;
pDstTab->cIfs = 0;
/* Regular interfaces. */
uint32_t iIfMac = pTab->cEntries;
while (iIfMac-- > 0)
{
if (pTab->paEntries[iIfMac].fActive)
{
PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
if (pIf != pIfSender)
{
uint32_t iIfDst = pDstTab->cIfs++;
pDstTab->aIfs[iIfDst].pIf = pIf;
pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
intnetR0BusyIncIf(pIf);
}
}
}
/* The trunk interface. */
if (pTab->fHostActive)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
if (pTab->fWireActive)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
pDstTab->fTrunkDst &= ~fSrc;
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pTab->pTrunk;
pDstTab->pTrunk = pTrunk;
intnetR0BusyIncTrunk(pTrunk);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return INTNETSWDECISION_BROADCAST;
}
/**
* Create a destination table with the trunk and any promiscuous interfaces.
*
* This is only used in a fallback case of the level-3 switching, so we can
* assume the wire as source and skip the sender interface filtering.
*
* @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
* INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
* @param pNetwork The network to switch on.
* @param fSrc The frame source.
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSwitchTrunkAndPromisc(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
{
Assert(fSrc == INTNETTRUNKDIR_WIRE);
/*
* Grab the spinlock first and do the switching.
*/
PINTNETMACTAB pTab = &pNetwork->MacTab;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pDstTab->fTrunkDst = 0;
pDstTab->pTrunk = 0;
pDstTab->cIfs = 0;
/* Find promiscuous interfaces. */
uint32_t iIfMac = pTab->cEntries;
while (iIfMac-- > 0)
{
if ( pTab->paEntries[iIfMac].fActive
&& pTab->paEntries[iIfMac].fPromiscuous)
{
PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
uint32_t iIfDst = pDstTab->cIfs++;
pDstTab->aIfs[iIfDst].pIf = pIf;
pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
intnetR0BusyIncIf(pIf);
}
}
/* The trunk interface. */
if (pTab->fHostActive)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
if (pTab->fWireActive)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
pDstTab->fTrunkDst &= ~fSrc;
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pTab->pTrunk;
pDstTab->pTrunk = pTrunk;
intnetR0BusyIncTrunk(pTrunk);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return !pDstTab->cIfs
? (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK)
: (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST);
}
/**
* Create a destination table for a trunk frame.
*
* @returns INTNETSWDECISION_BROADCAST.
* @param pNetwork The network to switch on.
* @param fSrc The frame source.
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSwitchTrunk(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
{
AssertPtr(pDstTab);
/*
* Grab the spinlock first and record all active interfaces.
*/
PINTNETMACTAB pTab= &pNetwork->MacTab;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pDstTab->fTrunkDst = 0;
pDstTab->pTrunk = 0;
pDstTab->cIfs = 0;
/* The trunk interface. */
if (pTab->fHostActive)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
if (pTab->fWireActive)
pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
pDstTab->fTrunkDst &= ~fSrc;
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pTab->pTrunk;
pDstTab->pTrunk = pTrunk;
intnetR0BusyIncTrunk(pTrunk);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return pDstTab->fTrunkDst ? INTNETSWDECISION_TRUNK : INTNETSWDECISION_DROP;
}
/**
* Wrapper around RTMemAlloc for allocating a destination table.
*
* @returns VINF_SUCCESS or VERR_NO_MEMORY.
* @param cEntries The size given as an entry count.
* @param ppDstTab Where to store the pointer (always).
*/
DECLINLINE(int) intnetR0AllocDstTab(uint32_t cEntries, PINTNETDSTTAB *ppDstTab)
{
PINTNETDSTTAB pDstTab;
*ppDstTab = pDstTab = (PINTNETDSTTAB)RTMemAlloc(RT_OFFSETOF(INTNETDSTTAB, aIfs[cEntries]));
if (RT_UNLIKELY(!pDstTab))
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
/**
* Ensures that there is space for another interface in the MAC address lookup
* table as well as all the destination tables.
*
* The caller must own the create/open/destroy mutex.
*
* @returns VINF_SUCCESS, VERR_NO_MEMORY or VERR_OUT_OF_RANGE.
* @param pNetwork The network to operate on.
*/
static int intnetR0NetworkEnsureTabSpace(PINTNETNETWORK pNetwork)
{
/*
* The cEntries and cEntriesAllocated members are only updated while
* owning the big mutex, so we only need the spinlock when doing the
* actual table replacing.
*/
PINTNETMACTAB pTab = &pNetwork->MacTab;
int rc = VINF_SUCCESS;
AssertReturn(pTab->cEntries <= pTab->cEntriesAllocated, VERR_INTERNAL_ERROR_2);
if (pTab->cEntries + 1 > pTab->cEntriesAllocated)
{
uint32_t const cAllocated = pTab->cEntriesAllocated + INTNET_GROW_DSTTAB_SIZE;
if (cAllocated <= INTNET_MAX_IFS)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
/*
* Resize the destination tables first, this can be kind of tedious.
*/
for (uint32_t i = 0; i < pTab->cEntries; i++)
{
PINTNETIF pIf = pTab->paEntries[i].pIf; AssertPtr(pIf);
PINTNETDSTTAB pNew;
rc = intnetR0AllocDstTab(cAllocated, &pNew);
if (RT_FAILURE(rc))
break;
for (;;)
{
PINTNETDSTTAB pOld = pIf->pDstTab;
if ( pOld
&& ASMAtomicCmpXchgPtr((void * volatile *)&pIf->pDstTab, pNew, pOld))
{
RTMemFree(pOld);
break;
}
intnetR0BusyWait(pNetwork, &pIf->cBusy);
}
}
/*
* The trunk.
*/
if ( RT_SUCCESS(rc)
&& pNetwork->MacTab.pTrunk)
{
AssertCompileAdjacentMembers(INTNETTRUNKIF, apTaskDstTabs, apIntDstTabs);
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
PINTNETDSTTAB * const ppEndDstTab = &pTrunk->apIntDstTabs[pTrunk->cIntDstTabs];
for (PINTNETDSTTAB *ppDstTab = &pTrunk->apTaskDstTabs[0];
ppDstTab != ppEndDstTab && RT_SUCCESS(rc);
ppDstTab++)
{
PINTNETDSTTAB pNew;
rc = intnetR0AllocDstTab(cAllocated, &pNew);
if (RT_FAILURE(rc))
break;
for (;;)
{
RTSpinlockAcquireNoInts(pTrunk->hDstTabSpinlock, &Tmp);
void *pvOld = *ppDstTab;
if (pvOld)
*ppDstTab = pNew;
RTSpinlockReleaseNoInts(pTrunk->hDstTabSpinlock, &Tmp);
if (pvOld)
{
RTMemFree(pvOld);
break;
}
intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
}
}
}
/*
* The MAC Address table itself.
*/
if (RT_SUCCESS(rc))
{
PINTNETMACTABENTRY paNew = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * cAllocated);
if (paNew)
{
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
PINTNETMACTABENTRY paOld = pTab->paEntries;
uint32_t i = pTab->cEntries;
while (i-- > 0)
{
paNew[i] = paOld[i];
paOld[i].fActive = false;
paOld[i].pIf = NULL;
}
pTab->paEntries = paNew;
pTab->cEntriesAllocated = cAllocated;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
RTMemFree(paOld);
}
else
rc = VERR_NO_MEMORY;
}
}
else
rc = VERR_OUT_OF_RANGE;
}
return rc;
}
#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.
* @param fGso Set if this is a GSO frame, clear if regular.
*/
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, true /*fCheckSum*/))
{
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))
{
PINTNETIF pMatchingIf = NULL;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
{
PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
if ( intnetR0IfHasMacAddr(pCur)
&& !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
{
intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
if (!pMatchingIf)
{
pMatchingIf = pCur;
intnetR0BusyIncIf(pMatchingIf);
}
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (pMatchingIf)
{
intnetR0IfAddrCacheAdd(pMatchingIf, &pMatchingIf->aAddrCache[kIntNetAddrType_IPv4],
(PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
intnetR0BusyDecIf(pMatchingIf);
}
}
return;
/*
* Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache.
*/
case RTNET_DHCP_MT_RELEASE:
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
{
PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
if ( intnetR0IfHasMacAddr(pCur)
&& !memcmp(&pCur->MacAddr, &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");
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
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);
uint32_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), true /*fChecksum*/))
{
Log(("intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
return;
}
uint32_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.
*/
uint32_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 ( !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_sha)
&& ( 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.
* @param fGso Set if this is a GSO frame, clear if regular.
*/
static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket, bool fGso)
{
/*
* 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, !fGso /*fChecksum*/))
{
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. */
&& !fGso) /* GSO is not applicable to DHCP traffic. */
{
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, !fGso /*fChecksum*/))
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
&& !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_tha)
&& ( 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->MacAddr, 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 fGso Set if this is a GSO frame, clear if regular.
* @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, bool fGso, 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, fGso);
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, fGso, 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;
}
}
/**
* Writes a frame packet to the ring 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(PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
{
PINTNETHDR pHdr = NULL; /* shut up gcc*/
void *pvDst = NULL; /* ditto */
int rc;
if (pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
rc = IntNetRingAllocateFrame(pRingBuf, pSG->cbTotal, &pHdr, &pvDst);
else
rc = IntNetRingAllocateGsoFrame(pRingBuf, pSG->cbTotal, &pSG->GsoCtx, &pHdr, &pvDst);
if (RT_SUCCESS(rc))
{
IntNetSgRead(pSG, pvDst);
if (pNewDstMac)
((PRTNETETHERHDR)pvDst)->DstMac = *pNewDstMac;
IntNetRingCommitFrame(pRingBuf, pHdr);
return VINF_SUCCESS;
}
return rc;
}
/**
* 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)
{
/*
* Grab the receive/producer lock and copy over the frame.
*/
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pIf->hRecvInSpinlock, &Tmp);
int rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
RTSpinlockReleaseNoInts(pIf->hRecvInSpinlock, &Tmp);
if (RT_SUCCESS(rc))
{
pIf->cYields = 0;
RTSemEventSignal(pIf->hRecvEvent);
return;
}
Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
/*
* Scheduling hack, for unicore machines primarily.
*/
if ( pIf->fActive
&& pIf->cYields < 4 /* just twice */
&& pIfSender /* but not if it's from the trunk */
&& RTThreadPreemptIsEnabled(NIL_RTTHREAD)
)
{
unsigned cYields = 2;
while (--cYields > 0)
{
RTSemEventSignal(pIf->hRecvEvent);
RTThreadYield();
RTSpinlockAcquireNoInts(pIf->hRecvInSpinlock, &Tmp);
rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
RTSpinlockReleaseNoInts(pIf->hRecvInSpinlock, &Tmp);
if (RT_SUCCESS(rc))
{
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
RTSemEventSignal(pIf->hRecvEvent);
return;
}
pIf->cYields++;
}
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
}
/* ok, the frame is lost. */
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
RTSemEventSignal(pIf->hRecvEvent);
}
/**
* Fallback path that does the GSO segmenting before passing the frame on to the
* trunk interface.
*
* The caller holds the trunk lock.
*
* @param pThis The trunk.
* @param pSG Pointer to the gather list.
* @param fDst The destination flags.
*/
static int intnetR0TrunkIfSendGsoFallback(PINTNETTRUNKIF pThis, PINTNETSG pSG, uint32_t fDst)
{
/*
* Since we're only using this for GSO frame comming from the internal
* network interfaces and never the trunk, we can assume there is only
* one segment. This simplifies the code quite a bit.
*/
Assert(PDMNetGsoIsValid(&pSG->GsoCtx, sizeof(pSG->GsoCtx), pSG->cbTotal));
AssertReturn(pSG->cSegsUsed == 1, VERR_INTERNAL_ERROR_4);
union
{
uint8_t abBuf[sizeof(INTNETSG) + sizeof(INTNETSEG)];
INTNETSG SG;
} u;
/*
* Carve out the frame segments with the header and frame in different
* scatter / gather segments.
*/
uint32_t const cSegs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
{
uint32_t cbSegPayload;
uint32_t offSegPayload = PDMNetGsoCarveSegment(&pSG->GsoCtx, (uint8_t *)pSG->aSegs[0].pv, pSG->cbTotal, iSeg, cSegs,
pThis->abGsoHdrs, &cbSegPayload);
IntNetSgInitTempSegs(&u.SG, pSG->GsoCtx.cbHdrs + cbSegPayload, 2, 2);
u.SG.aSegs[0].Phys = NIL_RTHCPHYS;
u.SG.aSegs[0].pv = pThis->abGsoHdrs;
u.SG.aSegs[0].cb = pSG->GsoCtx.cbHdrs;
u.SG.aSegs[1].Phys = NIL_RTHCPHYS;
u.SG.aSegs[1].pv = (uint8_t *)pSG->aSegs[0].pv + offSegPayload;
u.SG.aSegs[1].cb = (uint32_t)cbSegPayload;
int rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, &u.SG, fDst);
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Checks if any of the given trunk destinations can handle this kind of GSO SG.
*
* @returns true if it can, false if it cannot.
* @param pThis The trunk.
* @param pSG The scatter / gather buffer.
* @param fDst The destination mask.
*/
DECLINLINE(bool) intnetR0TrunkIfCanHandleGsoFrame(PINTNETTRUNKIF pThis, PINTNETSG pSG, uint32_t fDst)
{
uint8_t u8Type = pSG->GsoCtx.u8Type;
AssertReturn(u8Type < 32, false); /* paranoia */
uint32_t fMask = RT_BIT_32(u8Type);
if (fDst == INTNETTRUNKDIR_HOST)
return !!(pThis->fHostGsoCapabilites & fMask);
if (fDst == INTNETTRUNKDIR_WIRE)
return !!(pThis->fWireGsoCapabilites & fMask);
Assert(fDst == (INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST));
return !!(pThis->fHostGsoCapabilites & pThis->fWireGsoCapabilites & fMask);
}
/**
* Sends a frame down the trunk.
*
* @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.
*/
static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
uint32_t fDst, PINTNETSG pSG)
{
/*
* 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);
fDst &= ~INTNETTRUNKDIR_HOST;
}
/*
* Edit the source address so that it it's the same as the host.
*/
/* ASSUME frame from IntNetR0IfSend! */
AssertReturnVoid(pSG->cSegsUsed == 1);
AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
AssertReturnVoid(pIfSender);
PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
pEthHdr->SrcMac = pThis->MacAddr;
/*
* 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->MacAddr, sizeof(RTMAC)))
{
Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->MacAddr));
pArp->ar_sha = pThis->MacAddr;
}
if (!memcmp(&pArp->ar_tha, &pIfSender->MacAddr, sizeof(RTMAC))) /* just in case... */
{
Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->MacAddr));
pArp->ar_tha = pThis->MacAddr;
}
}
//else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP)
//{ /// @todo move the editing into a different function
//}
}
/*
* Send the frame, handling the GSO fallback .
* .
* Note! The trunk implementation will re-check that the trunk is active .
* before sending, so we don't have to duplicate that effort here.
*/
int rc;
if ( pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID
|| intnetR0TrunkIfCanHandleGsoFrame(pThis, pSG, fDst) )
rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pSG, fDst);
else
rc = intnetR0TrunkIfSendGsoFallback(pThis, pSG, fDst);
/** @todo failure statistics? */
Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst)); NOREF(rc);
}
/**
* 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->MacTab.pTrunk->MacAddr, 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->MacAddr));
pArpIPv4->ar_tha = pIf->MacAddr;
if (!memcmp(&pEthHdr->DstMac, &pNetwork->MacTab.pTrunk->MacAddr, sizeof(RTMAC)))
{
Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->MacAddr));
pEthHdr->DstMac = pIf->MacAddr;
if ((void *)pEthHdr != pSG->aSegs[0].pv)
intnetR0SgWritePart(pSG, RT_OFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->MacAddr);
}
intnetR0BusyDecIf(pIf);
/* 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);
uint32_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), true /*fCheckSum*/))
{
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, true /*fCheckSum*/))
{
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, (uintptr_t)&pDhcp->bp_flags - (uintptr_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, (uintptr_t)&pUdpHdr->uh_sum - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR), sizeof(pUdpHdr->uh_sum), &uChecksum);
}
break;
}
}
/**
* Checks if the callers context is okay for sending to the specified
* destinations.
*
* @returns true if it's okay, false if it isn't.
* @param pNetwork The network.
* @param pIfSender The interface sending or NULL if it's the trunk.
* @param pDstTab The destination table.
*/
DECLINLINE(bool) intnetR0NetworkIsContextOk(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCINTNETDSTTAB pDstTab)
{
/* Sending to the trunk is the problematic path. If the trunk is the
sender we won't be sending to it, so no problem..
Note! fTrunkDst may be set event if if the trunk is the sender. */
if (!pIfSender)
return true;
uint32_t const fTrunkDst = pDstTab->fTrunkDst;
if (!fTrunkDst)
return true;
/* ASSUMES: that the trunk won't change its report while we're checking. */
PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
if ((fTrunkDst & pTrunk->fNoPreemptDsts) == fTrunkDst)
return true;
/* ASSUMES: That a preemption test detects HWACCM contexts. (Will work on
non-preemptive systems as well.) */
if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
return true;
return false;
}
/**
* Checks if the callers context is okay for doing a broadcast given the
* specified source.
*
* @returns true if it's okay, false if it isn't.
* @param pNetwork The network.
* @param fSrc The source of the packet. (0 (intnet),
* INTNETTRUNKDIR_HOST or INTNETTRUNKDIR_WIRE).
*/
DECLINLINE(bool) intnetR0NetworkIsContextOkForBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc)
{
/* Sending to the trunk is the problematic path. If the trunk is the
sender we won't be sending to it, so no problem. */
if (fSrc)
return true;
/* ASSUMES: That a preemption test detects HWACCM contexts. (Will work on
non-preemptive systems as well.) */
if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
return true;
/* PARANOIA: Grab the spinlock to make sure the trunk structure cannot be
freed while we're touching it. */
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
bool fRc = !pTrunk
|| pTrunk->fNoPreemptDsts == (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
|| ( (!pNetwork->MacTab.fHostActive || (pTrunk->fNoPreemptDsts & INTNETTRUNKDIR_HOST) )
&& (!pNetwork->MacTab.fWireActive || (pTrunk->fNoPreemptDsts & INTNETTRUNKDIR_WIRE) ) );
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
return fRc;
}
/**
* Check context, edit, snoop and switch a broadcast frame when sharing MAC
* address on the wire.
*
* The caller must hold at least one interface on the network busy to prevent it
* from destructing beath us.
*
* @param pNetwork The network the frame is being sent to.
* @param fSrc The source of the packet. (0 (intnet),
* INTNETTRUNKDIR_HOST or INTNETTRUNKDIR_WIRE).
* @param pIfSender The sender interface, NULL if trunk. Used to
* prevent sending an echo to the sender.
* @param pSG Pointer to the gather list.
* @param pEthHdr Pointer to the ethernet header.
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchBroadcast(PINTNETNETWORK pNetwork,
uint32_t fSrc, PINTNETIF pIfSender,
PINTNETSG pSG, PRTNETETHERHDR pEthHdr,
PINTNETDSTTAB pDstTab)
{
/*
* Before doing any work here, we need to figure out if we can handle it
* in the current context. The restrictions are solely on the trunk.
*
* Note! Since at least one interface is busy, there won't be any changes
* to the parameters here (unless the trunk changes its capability
* report, which it shouldn't).
*/
if (!intnetR0NetworkIsContextOkForBroadcast(pNetwork, fSrc))
return INTNETSWDECISION_BAD_CONTEXT;
/*
* 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 ( (fSrc & INTNETTRUNKDIR_WIRE)
&& RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP
&& pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
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. GSO is not applicable to DHCP traffic.
*/
if ( !fSrc
&& RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_IPV4
&& pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
intnetR0NetworkEditDhcpFromIntNet(pNetwork, pSG, pEthHdr);
/*
* Snoop address info from packet orginating from the trunk connection.
*/
if (fSrc)
{
#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->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID )
|| (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4) )
intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
#else
if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
intnetR0TrunkIfSnoopArp(pNetwork, pSG);
#endif
}
/*
* Create the broadcast destination table.
*/
return intnetR0NetworkSwitchBroadcast(pNetwork, fSrc, pIfSender, pDstTab);
}
/**
* Check context, snoop and switch a unicast frame using the network layer
* address of the link layer one (when sharing MAC address on the wire).
*
* This function is only used for frames coming from the wire (trunk).
*
* @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 pEthHdr Pointer to the ethernet header.
* @param pDstTab The destination output table.
*/
static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchUnicast(PINTNETNETWORK pNetwork, PINTNETSG pSG,
PRTNETETHERHDR pEthHdr, PINTNETDSTTAB pDstTab)
{
/*
* 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 intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
}
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 intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
}
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 intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
}
enmAddrType = kIntNetAddrType_IPX;
cbAddr = sizeof(Addr.IPX);
break;
#endif
/*
* Treat ARP as 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 intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, INTNETTRUNKDIR_WIRE, NULL, pSG, pEthHdr, pDstTab);
/*
* Unknown packets are sent to the trunk and any promiscuous interfaces.
*/
default:
{
Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
return intnetR0NetworkSwitchTrunkAndPromisc(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
}
}
/*
* Do level-3 switching.
*/
INTNETSWDECISION enmSwDecision = intnetR0NetworkSwitchLevel3(pNetwork, &pEthHdr->DstMac,
enmAddrType, &Addr, cbAddr,
INTNETTRUNKDIR_WIRE, pDstTab);
#ifdef INTNET_WITH_DHCP_SNOOPING
/*
* Perform DHCP snooping. GSO is not applicable to DHCP traffic
*/
if ( enmAddrType == kIntNetAddrType_IPv4
&& pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
&& pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
#endif /* INTNET_WITH_DHCP_SNOOPING */
return enmSwDecision;
}
/**
* Release all the interfaces in the destination table when we realize that
* we're in a context where we cannot get the job done.
*
* @param pNetwork The network.
* @param pDstTab The destination table.
*/
static void intnetR0NetworkReleaseDstTab(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab)
{
/* The trunk interface. */
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
intnetR0BusyDec(pNetwork, &pTrunk->cBusy);
pDstTab->pTrunk = NULL;
pDstTab->fTrunkDst = 0;
}
/* Regular interfaces. */
uint32_t iIf = pDstTab->cIfs;
while (iIf-- > 0)
{
PINTNETIF pIf = pDstTab->aIfs[iIf].pIf;
intnetR0BusyDecIf(pIf);
pDstTab->aIfs[iIf].pIf = NULL;
}
pDstTab->cIfs = 0;
}
/**
* Deliver the frame to the interfaces specified in the destination table.
*
* @param pNetwork The network.
* @param pDstTab The destination table.
* @param pSG The frame to send.
* @param pIfSender The sender interface. NULL if it origined via
* the trunk.
*/
static void intnetR0NetworkDeliver(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab, PINTNETSG pSG, PINTNETIF pIfSender)
{
/*
* Trunk first so we don't wast any more time before hitting the wire.
*
* Note! The switching functions will include the trunk even when the frame
* source is the trunk. This is because we need it to figure out
* whether the other half of the trunk should see the frame or not
* and let the caller know.
*
* So, we'll ignore trunk sends here if the frame origin is
* INTNETTRUNKSWPORT::pfnRecv.
*/
if (pDstTab->fTrunkDst)
{
PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
if (pIfSender)
intnetR0TrunkIfSend(pTrunk, pNetwork, pIfSender, pDstTab->fTrunkDst, pSG);
intnetR0BusyDec(pNetwork, &pTrunk->cBusy);
pDstTab->pTrunk = NULL;
pDstTab->fTrunkDst = 0;
}
/*
* Do the interfaces.
*/
uint32_t iIf = pDstTab->cIfs;
while (iIf-- > 0)
{
PINTNETIF pIf = pDstTab->aIfs[iIf].pIf;
intnetR0IfSend(pIf, pIfSender, pSG,
pDstTab->aIfs[iIf].fReplaceDstMac ? &pIf->MacAddr: NULL);
intnetR0BusyDecIf(pIf);
pDstTab->aIfs[iIf].pIf = NULL;
}
pDstTab->cIfs = 0;
}
/**
* 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 The switching decision.
* @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 pDstTab The destination table to use.
*/
static INTNETSWDECISION intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
PINTNETSG pSG, PINTNETDSTTAB pDstTab)
{
/*
* 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 INTNETSWDECISION_INVALID;
/*
* 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 INTNETSWDECISION_INVALID;
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));
/*
* Learn the MAC address of the sender. No re-learning as the interface
* user will normally tell us the right MAC address.
*
* Note! We don't notify the trunk about these mainly because of the
* problematic contexts we might be called in.
*/
if (RT_UNLIKELY( pIfSender
&& !pIfSender->fMacSet
&& memcmp(&EthHdr.SrcMac, &pIfSender->MacAddr, sizeof(pIfSender->MacAddr))
&& !intnetR0IsMacAddrMulticast(&EthHdr.SrcMac)
))
{
Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->MacAddr, &EthHdr.SrcMac));
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
PINTNETMACTABENTRY pIfEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIfSender);
if (pIfEntry)
pIfEntry->MacAddr = EthHdr.SrcMac;
pIfSender->MacAddr = EthHdr.SrcMac;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
/*
* Deal with MAC address sharing as that may required editing of the
* packets before we dispatch them anywhere.
*/
INTNETSWDECISION enmSwDecision;
if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
{
if (intnetR0IsMacAddrMulticast(&EthHdr.DstMac))
enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, fSrc, pIfSender, pSG, &EthHdr, pDstTab);
else if (fSrc & INTNETTRUNKDIR_WIRE)
enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchUnicast(pNetwork, pSG, &EthHdr, pDstTab);
else
enmSwDecision = intnetR0NetworkSwitchUnicast(pNetwork, fSrc, pIfSender, &EthHdr.DstMac, pDstTab);
}
else if (intnetR0IsMacAddrMulticast(&EthHdr.DstMac))
enmSwDecision = intnetR0NetworkSwitchBroadcast(pNetwork, fSrc, pIfSender, pDstTab);
else
enmSwDecision = intnetR0NetworkSwitchUnicast(pNetwork, fSrc, pIfSender, &EthHdr.DstMac, pDstTab);
/*
* Deliver to the destinations if we can.
*/
if (enmSwDecision != INTNETSWDECISION_BAD_CONTEXT)
{
if (intnetR0NetworkIsContextOk(pNetwork, pIfSender, pDstTab))
intnetR0NetworkDeliver(pNetwork, pDstTab, pSG, pIfSender);
else
{
intnetR0NetworkReleaseDstTab(pNetwork, pDstTab);
enmSwDecision = INTNETSWDECISION_BAD_CONTEXT;
}
}
return enmSwDecision;
}
/**
* 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.
*
* The caller is responsible for making sure that there are no concurrent calls
* to this method (with the same handle).
*
* @returns VBox status code.
* @param hIf The interface handle.
* @param pSession The caller's session.
*/
INTNETR0DECL(int) IntNetR0IfSend(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
{
Log5(("IntNetR0IfSend: hIf=%RX32\n", hIf));
/*
* Validate input and translate the handle.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/*
* Make sure we've got a network.
*/
int rc = VINF_SUCCESS;
intnetR0BusyIncIf(pIf);
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (RT_LIKELY(pNetwork))
{
/*
* Grab the destination table.
*/
PINTNETDSTTAB pDstTab = (PINTNETDSTTAB)ASMAtomicXchgPtr((void * volatile *)&pIf->pDstTab, NULL);
if (RT_LIKELY(pDstTab))
{
/*
* Process the send buffer.
*/
INTNETSWDECISION enmSwDecision = INTNETSWDECISION_BROADCAST;
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. */
PINTNETHDR pHdr;
while ((pHdr = IntNetRingGetNextFrameToRead(&pIf->pIntBuf->Send)) != NULL)
{
uint16_t const u16Type = pHdr->u16Type;
if (u16Type == INTNETHDR_TYPE_FRAME)
{
/* Send regular frame. */
void *pvCurFrame = IntNetHdrGetFramePtr(pHdr, pIf->pIntBuf);
IntNetSgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, false /*fGso*/, (uint16_t *)&Sg.fFlags);
enmSwDecision = intnetR0NetworkSend(pNetwork, pIf, 0 /*fSrc*/, &Sg, pDstTab);
}
else if (u16Type == INTNETHDR_TYPE_GSO)
{
/* Send GSO frame if sane. */
PPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pIf->pIntBuf);
uint32_t cbFrame = pHdr->cbFrame - sizeof(*pGso);
if (RT_LIKELY(PDMNetGsoIsValid(pGso, pHdr->cbFrame, cbFrame)))
{
void *pvCurFrame = pGso + 1;
IntNetSgInitTempGso(&Sg, pvCurFrame, cbFrame, pGso);
if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, cbFrame, true /*fGso*/, (uint16_t *)&Sg.fFlags);
enmSwDecision = intnetR0NetworkSend(pNetwork, pIf, 0 /*fSrc*/, &Sg, pDstTab);
}
else
{
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatBadFrames); /* ignore */
enmSwDecision = INTNETSWDECISION_DROP;
}
}
/* Unless it's a padding frame, we're getting babble from the producer. */
else
{
if (u16Type != INTNETHDR_TYPE_PADDING)
STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatBadFrames); /* ignore */
enmSwDecision = INTNETSWDECISION_DROP;
}
if (enmSwDecision == INTNETSWDECISION_BAD_CONTEXT)
{
rc = VERR_TRY_AGAIN;
break;
}
/* Skip to the next frame. */
IntNetRingSkipFrame(&pIf->pIntBuf->Send);
}
/*
* Put back the destination table.
*/
Assert(!pIf->pDstTab);
ASMAtomicWritePtr((void * volatile *)&pIf->pDstTab, pDstTab);
}
else
rc = VERR_INTERNAL_ERROR_4;
}
else
rc = VERR_INTERNAL_ERROR_3;
/*
* Release the interface.
*/
intnetR0BusyDecIf(pIf);
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* VMMR0 request wrapper for IntNetR0IfSend.
*
* @returns see IntNetR0IfSend.
* @param pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfSendReq(PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfSend(pReq->hIf, pSession);
}
/**
* Maps the default buffer into ring 3.
*
* @returns VBox status code.
* @param hIf The interface handle.
* @param pSession The caller's session.
* @param ppRing3Buf Where to store the address of the ring-3 mapping
* (optional).
* @param ppRing0Buf Where to store the address of the ring-0 mapping
* (optional).
*/
INTNETR0DECL(int) IntNetR0IfGetBufferPtrs(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession,
R3PTRTYPE(PINTNETBUF) *ppRing3Buf, R0PTRTYPE(PINTNETBUF) *ppRing0Buf)
{
LogFlow(("IntNetR0IfGetBufferPtrs: hIf=%RX32 ppRing3Buf=%p ppRing0Buf=%p\n", hIf, ppRing3Buf, ppRing0Buf));
/*
* Validate input.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
AssertPtrNullReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
AssertPtrNullReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
if (ppRing3Buf)
*ppRing3Buf = 0;
if (ppRing0Buf)
*ppRing0Buf = 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 = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
if (RT_SUCCESS(rc))
{
if (ppRing3Buf)
*ppRing3Buf = pIf->pIntBufR3;
if (ppRing0Buf)
*ppRing0Buf = (R0PTRTYPE(PINTNETBUF))pIf->pIntBuf; /* tstIntNetR0 mess */
rc = RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
}
intnetR0IfRelease(pIf, pSession);
LogFlow(("IntNetR0IfGetBufferPtrs: returns %Rrc *ppRing3Buf=%p *ppRing0Buf=%p\n",
rc, ppRing3Buf ? *ppRing3Buf : NULL, ppRing0Buf ? *ppRing0Buf : NULL));
return rc;
}
/**
* VMMR0 request wrapper for IntNetR0IfGetBufferPtrs.
*
* @returns see IntNetR0IfGetRing3Buffer.
* @param pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfGetBufferPtrsReq(PSUPDRVSESSION pSession, PINTNETIFGETBUFFERPTRSREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfGetBufferPtrs(pReq->hIf, pSession, &pReq->pRing3Buf, &pReq->pRing0Buf);
}
#if 0
/**
* Gets the physical addresses of the default interface buffer.
*
* @returns VBox status code.
* @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(INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
{
/*
* Validate input.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
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 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(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
{
LogFlow(("IntNetR0IfSetPromiscuousMode: hIf=%RX32 fPromiscuous=%d\n", hIf, fPromiscuous));
/*
* Validate & translate input.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
{
Log(("IntNetR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
/*
* Get the network, take the address spinlock, and make the change.
* Paranoia^2: Mark ourselves busy to prevent anything from being destroyed.
*/
int rc = VINF_SUCCESS;
intnetR0BusyIncIf(pIf);
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (pIf->fPromiscuous != fPromiscuous)
{
Log(("IntNetR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
hIf, !fPromiscuous, !!fPromiscuous));
ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
if (RT_LIKELY(pEntry))
pEntry->fPromiscuous = fPromiscuous;
pIf->fPromiscuous = fPromiscuous;
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
else
rc = VERR_WRONG_ORDER;
intnetR0BusyDecIf(pIf);
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* VMMR0 request wrapper for IntNetR0IfSetPromiscuousMode.
*
* @returns see IntNetR0IfSetPromiscuousMode.
* @param pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfSetPromiscuousModeReq(PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfSetPromiscuousMode(pReq->hIf, pSession, pReq->fPromiscuous);
}
/**
* Sets the MAC address of an interface.
*
* @returns VBox status code.
* @param hIf The interface handle.
* @param pSession The caller's session.
* @param pMAC The new MAC address.
*/
INTNETR0DECL(int) IntNetR0IfSetMacAddress(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCRTMAC pMac)
{
LogFlow(("IntNetR0IfSetMacAddress: hIf=%RX32 pMac=%p:{%.6Rhxs}\n", hIf, pMac, pMac));
/*
* Validate & translate input.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
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;
}
/*
* Get the network, take the address spinlock, and make the change.
* Paranoia^2: Mark ourselves busy to prevent anything from being destroyed.
*/
int rc = VINF_SUCCESS;
intnetR0BusyIncIf(pIf);
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
PINTNETTRUNKIF pTrunk = NULL;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (memcmp(&pIf->MacAddr, pMac, sizeof(pIf->MacAddr)))
{
Log(("IntNetR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
hIf, &pIf->MacAddr, pMac));
/* Update the two copies. */
PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
if (RT_LIKELY(pEntry))
pEntry->MacAddr = *pMac;
pIf->MacAddr = *pMac;
pIf->fMacSet = true;
/* Grab a busy reference to the trunk so we release the lock before notifying it. */
pTrunk = pNetwork->MacTab.pTrunk;
if (pTrunk)
intnetR0BusyIncTrunk(pTrunk);
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (pTrunk)
{
Log(("IntNetR0IfSetMacAddress: pfnNotifyMacAddress hIf=%RX32\n", hIf));
PINTNETTRUNKIFPORT pIfPort = pTrunk->pIfPort;
if (pIfPort)
pIfPort->pfnNotifyMacAddress(pIfPort, hIf, pMac);
intnetR0BusyDecTrunk(pTrunk);
}
}
else
rc = VERR_WRONG_ORDER;
intnetR0BusyDecIf(pIf);
intnetR0IfRelease(pIf, pSession);
return rc;
}
/**
* VMMR0 request wrapper for IntNetR0IfSetMacAddress.
*
* @returns see IntNetR0IfSetMacAddress.
* @param pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfSetMacAddressReq(PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfSetMacAddress(pReq->hIf, pSession, &pReq->Mac);
}
/**
* Worker for intnetR0IfSetActive and intnetR0IfDestruct.
*
* 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);
/*
* The address spinlock of the network protects the variables, while the
* big lock protects the calling of pfnSetState. Grab both lock at once
* to save us the extra hazzle.
*/
PINTNET pIntNet = pNetwork->pIntNet;
int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
AssertRCReturn(rc, rc);
PINTNETTRUNKIF pTrunk = NULL;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
/*
* Do the update.
*/
if (pIf->fActive != fActive)
{
PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
if (RT_LIKELY(pEntry))
{
pEntry->fActive = fActive;
pIf->fActive = fActive;
if (fActive)
{
pNetwork->cActiveIFs++;
if (pNetwork->cActiveIFs == 1)
{
pTrunk = pNetwork->MacTab.pTrunk;
if (pTrunk)
{
pNetwork->MacTab.fHostActive = true;
pNetwork->MacTab.fWireActive = true;
}
}
}
else
{
pNetwork->cActiveIFs--;
if (pNetwork->cActiveIFs == 0)
{
pTrunk = pNetwork->MacTab.pTrunk;
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWireActive = false;
}
}
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
/*
* Tell the trunk if necessary.
*/
if (pTrunk && pTrunk->pIfPort)
pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, fActive ? INTNETTRUNKIFSTATE_ACTIVE : INTNETTRUNKIFSTATE_INACTIVE);
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
return VINF_SUCCESS;
}
/**
* Sets the active property of an interface.
*
* @returns VBox status code.
* @param hIf The interface handle.
* @param pSession The caller's session.
* @param fActive The new state.
*/
INTNETR0DECL(int) IntNetR0IfSetActive(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
{
LogFlow(("IntNetR0IfSetActive: hIf=%RX32 fActive=%RTbool\n", hIf, fActive));
/*
* Validate & translate input.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
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.
*
* Note! We mark the interface busy so the network cannot be removed while
* we're working on it - paranoia strikes again.
*/
intnetR0BusyIncIf(pIf);
int rc;
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
else
rc = VERR_WRONG_ORDER;
intnetR0BusyDecIf(pIf);
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(PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfSetActive(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 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(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
{
Log4(("IntNetR0IfWait: hIf=%RX32 cMillies=%u\n", hIf, cMillies));
/*
* Get and validate essential handles.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
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 hRecvEvent = pIf->hRecvEvent;
if ( hIfSelf != hIf /* paranoia */
&& hRecvEvent != 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(hRecvEvent, cMillies);
if (pIf->hRecvEvent == hRecvEvent)
{
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 pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfWaitReq(PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfWait(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(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
{
LogFlow(("IntNetR0IfClose: hIf=%RX32\n", hIf));
/*
* Validate and free the handle.
*
* We grab the big mutex before we free the handle to avoid any handle
* confusion on the trunk.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
{
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
return VERR_INVALID_HANDLE;
}
/* Mark the handle as freed so intnetR0IfDestruct won't free it again. */
ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
/* Notify the trunk that the interface has been disconnected. */
PINTNETNETWORK pNetwork = pIf->pNetwork;
PINTNETTRUNKIF pTrunk = pNetwork ? pNetwork->MacTab.pTrunk : NULL;
if (pTrunk && pTrunk->pIfPort)
pTrunk->pIfPort->pfnDisconnectInterface(pTrunk->pIfPort, hIf);
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
/*
* Signal the event semaphore to wake up any threads in IntNetR0IfWait
* and give them a moment to get out and release the interface.
*/
uint32_t i = pIf->cSleepers;
while (i-- > 0)
{
RTSemEventSignal(pIf->hRecvEvent);
RTThreadYield();
}
RTSemEventSignal(pIf->hRecvEvent);
/*
* Release the references to the interface object (handle + free lookup).
*/
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 pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfCloseReq(PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfClose(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));
/*
* We grab the INTNET create/open/destroy semaphore to make sure nobody is
* adding or removing interface while we're in here. For paranoid reasons
* we also mark the interface as destroyed here so any waiting threads can
* take evasive action (theoretical case).
*/
RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
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 detach ourselves from it. Because
* of cleanup order we might have been orphaned by the network destructor.
*/
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
{
/* set inactive. */
intnetR0NetworkSetIfActive(pNetwork, pIf, false /*fActive*/);
/* remove ourselves from the switch table. */
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
{
if (iIf + 1 < pNetwork->MacTab.cEntries)
memmove(&pNetwork->MacTab.paEntries[iIf],
&pNetwork->MacTab.paEntries[iIf + 1],
(pNetwork->MacTab.cEntries - iIf - 1) * sizeof(pNetwork->MacTab.paEntries[0]));
pNetwork->MacTab.cEntries--;
break;
}
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
/* If we freed the handle just now, notify the trunk about the
interface being destroyed. */
if (hIf != INTNET_HANDLE_INVALID && pTrunk && pTrunk->pIfPort)
pTrunk->pIfPort->pfnDisconnectInterface(pTrunk->pIfPort, hIf);
/* Wait for the interface to quiesce while we still can. */
intnetR0BusyWait(pNetwork, &pIf->cBusy);
/* Release our reference to the network. */
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pIf->pNetwork = NULL;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
}
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
/*
* 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->hRecvEvent != NIL_RTSEMEVENT)
{
RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
unsigned cMaxWait = 0x1000;
while (pIf->cSleepers && cMaxWait-- > 0)
{
RTSemEventSignal(hRecvEvent);
RTThreadYield();
}
if (pIf->cSleepers)
{
RTThreadSleep(1);
cMaxWait = pIf->cSleepers;
while (pIf->cSleepers && cMaxWait-- > 0)
{
RTSemEventSignal(hRecvEvent);
RTThreadSleep(10);
}
}
RTSemEventDestroy(hRecvEvent);
pIf->hRecvEvent = 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;
}
/*
* Free remaining resources
*/
RTSpinlockDestroy(pIf->hRecvInSpinlock);
pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
RTMemFree(pIf->pDstTab);
pIf->pDstTab = NULL;
for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
intnetR0IfAddrCacheDestroy(&pIf->aAddrCache[i]);
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, referenced. The reference is consumed on
* success.
* @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,
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);
/*
* Make sure that all destination tables as well as the have space of
*/
int rc = intnetR0NetworkEnsureTabSpace(pNetwork);
if (RT_FAILURE(rc))
return rc;
/*
* Allocate the interface and initalize it.
*/
PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
if (!pIf)
return VERR_NO_MEMORY;
memset(&pIf->MacAddr, 0xff, sizeof(pIf->MacAddr)); /* broadcast */
//pIf->fMacSet = false;
//pIf->fPromiscuous = false;
//pIf->fActive = false;
//pIf->fDestroying = false;
//pIf->cYields = 0;
//pIf->pIntBuf = 0;
//pIf->pIntBufR3 = NIL_RTR3PTR;
//pIf->pIntBufDefault = 0;
//pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
pIf->hRecvEvent = NIL_RTSEMEVENT;
//pIf->cSleepers = 0;
pIf->hIf = INTNET_HANDLE_INVALID;
pIf->pNetwork = pNetwork;
pIf->pSession = pSession;
//pIf->pvObj = NULL;
//pIf->aAddrCache = {0};
pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
pIf->cBusy = 0;
//pIf->pDstTab = NULL;
for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End && RT_SUCCESS(rc); i++)
rc = intnetR0IfAddrCacheInit(&pIf->aAddrCache[i], (INTNETADDRTYPE)i,
!!(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE));
if (RT_SUCCESS(rc))
rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, (PINTNETDSTTAB *)&pIf->pDstTab);
if (RT_SUCCESS(rc))
rc = RTSemEventCreate((PRTSEMEVENT)&pIf->hRecvEvent);
if (RT_SUCCESS(rc))
rc = RTSpinlockCreate(&pIf->hRecvInSpinlock);
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), INTNETRINGBUF_ALIGNMENT);
cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), INTNETRINGBUF_ALIGNMENT);
const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), INTNETRINGBUF_ALIGNMENT) + 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;
IntNetBufInit(pIf->pIntBuf, cbBuf, cbRecv, cbSend);
/*
* Register the interface with the session and create a handle for it.
*/
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))
{
/*
* Finally add the interface to the network, consuming the
* network reference of the caller.
*/
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
Assert(iIf + 1 <= pNetwork->MacTab.cEntriesAllocated);
pNetwork->MacTab.paEntries[iIf].MacAddr = pIf->MacAddr;
pNetwork->MacTab.paEntries[iIf].fActive = false;
pNetwork->MacTab.paEntries[iIf].fPromiscuous = false;
pNetwork->MacTab.paEntries[iIf].pIf = pIf;
pNetwork->MacTab.cEntries = iIf + 1;
pIf->pNetwork = pNetwork;
/*
* Grab a busy reference (paranoia) to the trunk before releaseing
* the spinlock and then notify it about the new interface.
*/
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
if (pTrunk)
intnetR0BusyIncTrunk(pTrunk);
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (pTrunk)
{
Log(("intnetR0NetworkCreateIf: pfnConnectInterface hIf=%RX32\n", pIf->hIf));
if (pTrunk->pIfPort)
rc = pTrunk->pIfPort->pfnConnectInterface(pTrunk->pIfPort, pIf->hIf);
intnetR0BusyDecTrunk(pTrunk);
}
if (RT_SUCCESS(rc))
{
/*
* We're good!
*/
*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;
}
/* clean up */
SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
pIf->pIntBufDefault = NULL;
pIf->pIntBuf = NULL;
}
}
RTSpinlockDestroy(pIf->hRecvInSpinlock);
pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
RTSemEventDestroy(pIf->hRecvEvent);
pIf->hRecvEvent = NIL_RTSEMEVENT;
RTMemFree(pIf->pDstTab);
for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
intnetR0IfAddrCacheDestroy(&pIf->aAddrCache[i]);
RTMemFree(pIf);
LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
return rc;
}
/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
static DECLCALLBACK(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::pfnReportMacAddress */
static DECLCALLBACK(void) intnetR0TrunkIfPortReportMacAddress(PINTNETTRUNKSWPORT pSwitchPort, PCRTMAC pMacAddr)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
/*
* Get the network instance and grab the address spinlock before making
* any changes.
*/
intnetR0BusyIncTrunk(pThis);
PINTNETNETWORK pNetwork = pThis->pNetwork;
if (pNetwork)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pNetwork->MacTab.HostMac = *pMacAddr;
pThis->MacAddr = *pMacAddr;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
else
pThis->MacAddr = *pMacAddr;
intnetR0BusyDecTrunk(pThis);
}
/** @copydoc INTNETTRUNKSWPORT::pfnReportPromiscuousMode */
static DECLCALLBACK(void) intnetR0TrunkIfPortReportPromiscuousMode(PINTNETTRUNKSWPORT pSwitchPort, bool fPromiscuous)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
/*
* Get the network instance and grab the address spinlock before making
* any changes.
*/
intnetR0BusyIncTrunk(pThis);
PINTNETNETWORK pNetwork = pThis->pNetwork;
if (pNetwork)
{
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
pNetwork->MacTab.fHostPromiscuous = fPromiscuous;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
}
intnetR0BusyDecTrunk(pThis);
}
/** @copydoc INTNETTRUNKSWPORT::pfnReportGsoCapabilities */
static DECLCALLBACK(void) intnetR0TrunkIfPortReportGsoCapabilities(PINTNETTRUNKSWPORT pSwitchPort,
uint32_t fGsoCapabilities, uint32_t fDst)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
for (unsigned iBit = PDMNETWORKGSOTYPE_END; iBit < 32; iBit++)
Assert(!(fGsoCapabilities & RT_BIT_32(iBit)));
Assert(!(fDst & ~INTNETTRUNKDIR_VALID_MASK));
Assert(fDst);
if (fDst & INTNETTRUNKDIR_HOST)
pThis->fHostGsoCapabilites = fGsoCapabilities;
if (fDst & INTNETTRUNKDIR_WIRE)
pThis->fWireGsoCapabilites = fGsoCapabilities;
}
/** @copydoc INTNETTRUNKSWPORT::pfnReportNoPreemptDsts */
static DECLCALLBACK(void) intnetR0TrunkIfPortReportNoPreemptDsts(PINTNETTRUNKSWPORT pSwitchPort, uint32_t fNoPreemptDsts)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
Assert(!(fNoPreemptDsts & ~INTNETTRUNKDIR_VALID_MASK));
pThis->fNoPreemptDsts = fNoPreemptDsts;
}
/** @copydoc INTNETTRUNKSWPORT::pfnPreRecv */
static DECLCALLBACK(INTNETSWDECISION) intnetR0TrunkIfPortPreRecv(PINTNETTRUNKSWPORT pSwitchPort,
void const *pvSrc, size_t cbSrc, uint32_t fSrc)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
PINTNETNETWORK pNetwork = pThis->pNetwork;
/* assert some sanity */
AssertPtrReturn(pNetwork, INTNETSWDECISION_TRUNK);
AssertReturn(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT, INTNETSWDECISION_TRUNK);
AssertPtr(pvSrc);
AssertPtr(cbSrc >= 6);
Assert(fSrc);
/** @todo implement the switch table. */
return INTNETSWDECISION_BROADCAST;
}
/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
/* assert some sanity */
AssertPtr(pSG);
Assert(fSrc);
/*
* Mark the trunk as busy, make sure we've got a network and that there are
* some active interfaces around.
*/
bool fRc = false /* don't drop it */;
intnetR0BusyIncTrunk(pThis);
PINTNETNETWORK pNetwork = pThis->pNetwork;
if (RT_LIKELY( pNetwork
&& pNetwork->cActiveIFs > 0 ))
{
/*
* Grab or allocate a destination table.
*/
bool const fIntCtx = RTThreadPreemptIsEnabled(NIL_RTTHREAD) || RTThreadIsInInterrupt(NIL_RTTHREAD);
unsigned iDstTab = 0;
PINTNETDSTTAB pDstTab = NULL;
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pThis->hDstTabSpinlock, &Tmp);
if (fIntCtx)
{
/* Interrupt or restricted context. */
iDstTab = RTMpCpuIdToSetIndex(RTMpCpuId());
iDstTab %= pThis->cIntDstTabs;
pDstTab = pThis->apIntDstTabs[iDstTab];
if (RT_LIKELY(pDstTab))
pThis->apIntDstTabs[iDstTab] = NULL;
else
{
iDstTab = pThis->cIntDstTabs;
while (iDstTab-- > 0)
{
pDstTab = pThis->apIntDstTabs[iDstTab];
if (pDstTab)
{
pThis->apIntDstTabs[iDstTab] = NULL;
break;
}
}
}
RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
Assert(!pDstTab || iDstTab < pThis->cIntDstTabs);
}
else
{
/* Task context, fallback is to allocate a table. */
AssertCompile(RT_ELEMENTS(pThis->apTaskDstTabs) == 2); /* for loop rollout */
pDstTab = pThis->apIntDstTabs[iDstTab = 0];
if (!pDstTab)
pDstTab = pThis->apIntDstTabs[iDstTab = 1];
if (pDstTab)
{
pThis->apIntDstTabs[iDstTab] = NULL;
RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
Assert(iDstTab < RT_ELEMENTS(pThis->apTaskDstTabs));
}
else
{
RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pDstTab);
iDstTab = 65535;
}
}
if (RT_LIKELY(pDstTab))
{
/*
* Finally, get down to business of sending the frame.
*/
INTNETSWDECISION enmSwDecision = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, pDstTab);
AssertMsg(enmSwDecision != INTNETSWDECISION_BAD_CONTEXT, ("fSrc=%#x fTrunkDst=%#x hdr=%.14Rhxs\n", fSrc, pDstTab->fTrunkDst, pSG->aSegs[0].pv));
if (enmSwDecision == INTNETSWDECISION_INTNET)
fRc = true; /* drop it */
/*
* Free the destination table.
*/
if (iDstTab == 65535)
RTMemFree(pDstTab);
else
{
RTSpinlockAcquireNoInts(pThis->hDstTabSpinlock, &Tmp);
if (fIntCtx && !pThis->apIntDstTabs[iDstTab])
pThis->apIntDstTabs[iDstTab] = pDstTab;
else if (!fIntCtx && !pThis->apTaskDstTabs[iDstTab])
pThis->apTaskDstTabs[iDstTab] = pDstTab;
else
{
/* this shouldn't happen! */
PINTNETDSTTAB *papDstTabs = fIntCtx ? &pThis->apIntDstTabs[0] : &pThis->apTaskDstTabs[0];
iDstTab = fIntCtx ? pThis->cIntDstTabs : RT_ELEMENTS(pThis->apTaskDstTabs);
while (iDstTab-- > 0)
if (!papDstTabs[iDstTab])
{
papDstTabs[iDstTab] = pDstTab;
break;
}
}
RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
Assert(iDstTab < RT_MAX(RT_ELEMENTS(pThis->apTaskDstTabs), pThis->cIntDstTabs));
}
}
}
intnetR0BusyDecTrunk(pThis);
return fRc;
}
/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
PINTNETNETWORK pNetwork = pThis->pNetwork;
/* assert some sanity */
AssertPtrReturnVoid(pNetwork);
AssertReturnVoid(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT);
AssertPtr(pSG);
Assert(pSG->cUsers > 0 && pSG->cUsers < 256);
/* do it. */
++pSG->cUsers;
}
/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
PINTNETNETWORK pNetwork = pThis->pNetwork;
/* assert some sanity */
AssertPtrReturnVoid(pNetwork);
AssertReturnVoid(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT);
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);
}
/**
* Shutdown the trunk interface.
*
* @param pThis The trunk.
* @param pNetworks The network.
*
* @remarks The caller must hold the global lock.
*/
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)
{
/* 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.
*/
pThis->pNetwork = NULL;
RTSpinlockDestroy(pThis->hDstTabSpinlock);
for (unsigned i = 0; i < RT_ELEMENTS(pThis->apTaskDstTabs); i++)
{
Assert(pThis->apTaskDstTabs[i]);
RTMemFree(pThis->apTaskDstTabs[i]);
pThis->apTaskDstTabs[i] = NULL;
}
for (unsigned i = 0; i < pThis->cIntDstTabs; i++)
{
Assert(pThis->apIntDstTabs[i]);
RTMemFree(pThis->apIntDstTabs[i]);
pThis->apIntDstTabs[i] = NULL;
}
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:
#if defined(RT_OS_DARWIN) && !defined(VBOXNETADP_DO_NOT_USE_NETFLT)
pszName = "VBoxNetFlt";
#else /* VBOXNETADP_DO_NOT_USE_NETFLT */
pszName = "VBoxNetAdp";
#endif /* VBOXNETADP_DO_NOT_USE_NETFLT */
break;
case kIntNetTrunkType_SrvNat:
pszName = "VBoxSrvNat";
break;
}
/*
* Allocate the trunk interface and associated destination tables.
*
* We take a very optimistic view on the parallelism of the host
* network stack and NIC driver. So, we allocate one table for each
* possible CPU to deal with interrupt time requests and one for task
* time calls.
*/
RTCPUID cCpus = RTMpGetCount(); Assert(cCpus > 0);
PINTNETTRUNKIF pTrunk = (PINTNETTRUNKIF)RTMemAllocZ(RT_OFFSETOF(INTNETTRUNKIF, apIntDstTabs[cCpus]));
if (!pTrunk)
return VERR_NO_MEMORY;
Assert(pNetwork->MacTab.cEntriesAllocated > 0);
int rc = VINF_SUCCESS;
pTrunk->cIntDstTabs = cCpus;
for (unsigned i = 0; i < cCpus && RT_SUCCESS(rc); i++)
rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pTrunk->apIntDstTabs[i]);
for (unsigned i = 0; i < RT_ELEMENTS(pTrunk->apTaskDstTabs) && RT_SUCCESS(rc); i++)
rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pTrunk->apTaskDstTabs[i]);
if (RT_SUCCESS(rc))
{
pTrunk->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
pTrunk->SwitchPort.pfnPreRecv = intnetR0TrunkIfPortPreRecv;
pTrunk->SwitchPort.pfnRecv = intnetR0TrunkIfPortRecv;
pTrunk->SwitchPort.pfnSGRetain = intnetR0TrunkIfPortSGRetain;
pTrunk->SwitchPort.pfnSGRelease = intnetR0TrunkIfPortSGRelease;
pTrunk->SwitchPort.pfnSetSGPhys = intnetR0TrunkIfPortSetSGPhys;
pTrunk->SwitchPort.pfnReportMacAddress = intnetR0TrunkIfPortReportMacAddress;
pTrunk->SwitchPort.pfnReportPromiscuousMode = intnetR0TrunkIfPortReportPromiscuousMode;
pTrunk->SwitchPort.pfnReportGsoCapabilities = intnetR0TrunkIfPortReportGsoCapabilities;
pTrunk->SwitchPort.pfnReportNoPreemptDsts = intnetR0TrunkIfPortReportNoPreemptDsts;
pTrunk->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
//pTrunk->pIfPort = NULL;
pTrunk->pNetwork = pNetwork;
pTrunk->MacAddr.au8[0] = 0xff;
pTrunk->MacAddr.au8[1] = 0xff;
pTrunk->MacAddr.au8[2] = 0xff;
pTrunk->MacAddr.au8[3] = 0xff;
pTrunk->MacAddr.au8[4] = 0xff;
pTrunk->MacAddr.au8[5] = 0xff;
//pTrunk->fPhysSG = false;
//pTrunk->fUnused = false;
//pTrunk->cBusy = 0;
//pTrunk->fNoPreemptDsts = 0;
//pTrunk->fWireGsoCapabilites = 0;
//pTrunk->fHostGsoCapabilites = 0;
//pTrunk->abGsoHdrs = {0};
pTrunk->hDstTabSpinlock = NIL_RTSPINLOCK;
//pTrunk->apTaskDstTabs = above;
//pTrunk->cIntDstTabs = above;
//pTrunk->apIntDstTabs = above;
/*
* Create the lock (we've NIL'ed the members above to simplify cleanup).
*/
rc = RTSpinlockCreate(&pTrunk->hDstTabSpinlock);
if (RT_SUCCESS(rc))
{
/*
* There are a couple of bits in MacTab as well pertaining to the
* trunk. We have to set this before it's reported.
*
* Note! We don't need to lock the MacTab here - creation time.
*/
pNetwork->MacTab.pTrunk = pTrunk;
pNetwork->MacTab.HostMac = pTrunk->MacAddr;
pNetwork->MacTab.fHostPromiscuous = false;
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWirePromiscuous = false; /** @todo !!(fFlags & INTNET_OPEN_FLAGS_PROMISC_TRUNK_WIRE); */
pNetwork->MacTab.fWireActive = false;
#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,
&pTrunk->SwitchPort,
pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE
? INTNETTRUNKFACTORY_FLAG_NO_PROMISC
: 0,
&pTrunk->pIfPort);
pTrunkFactory->pfnRelease(pTrunkFactory);
if (RT_SUCCESS(rc))
{
Assert(pTrunk->pIfPort);
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;
}
}
#else /* IN_RING3 */
rc = VERR_NOT_SUPPORTED;
#endif /* IN_RING3 */
pNetwork->MacTab.pTrunk = NULL;
}
/* bail out and clean up. */
RTSpinlockDestroy(pTrunk->hDstTabSpinlock);
}
for (unsigned i = 0; i < RT_ELEMENTS(pTrunk->apTaskDstTabs); i++)
RTMemFree(pTrunk->apTaskDstTabs[i]);
for (unsigned i = 0; i < pTrunk->cIntDstTabs; i++)
RTMemFree(pTrunk->apIntDstTabs[i]);
RTMemFree(pTrunk);
LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
rc, pszName, pNetwork->szTrunk, pNetwork->szName));
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 big create/open/destroy sem. */
RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
/*
* Tell the trunk, if present, that we're about to disconnect it and wish
* no further calls from it.
*/
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
if (pTrunk)
pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, INTNETTRUNKIFSTATE_DISCONNECTING);
/*
* Deactivate and orphan any remaining interfaces and wait for them to idle.
*
* Note! Normally there are no more interfaces at this point, however, when
* supdrvCloseSession / supdrvCleanupSession release the objects the
* order is undefined. So, it's quite possible that the network will
* be dereference and destroyed before the interfaces.
*/
RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
{
pNetwork->MacTab.paEntries[iIf].fActive = false;
pNetwork->MacTab.paEntries[iIf].pIf->fActive = false;
}
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWireActive = false;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
/* Wait for all the interfaces to quiesce. (Interfaces cannot be
removed / added since we're holding the big lock.) */
if (pTrunk)
intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
intnetR0BusyWait(pNetwork, &pNetwork->MacTab.paEntries[iIf].pIf->cBusy);
/* Orphan the interfaces (not trunk). Don't bother with calling
pfnDisconnectInterface here since the networking is going away. */
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
while ((iIf = pNetwork->MacTab.cEntries) > 0)
{
PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf - 1].pIf;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
intnetR0BusyWait(pNetwork, &pIf->cBusy);
RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
if ( iIf == pNetwork->MacTab.cEntries /* paranoia */
&& pIf->cBusy)
{
pIf->pNetwork = NULL;
pNetwork->MacTab.cEntries--;
}
}
/*
* Zap the trunk pointer while we still own the spinlock, destroy the
* trunk after we've left it. Note that this might take a while...
*/
pNetwork->MacTab.pTrunk = NULL;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
if (pTrunk)
intnetR0TrunkIfDestroy(pTrunk, pNetwork);
/*
* 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;
/*
* Free resources.
*/
RTSemEventDestroy(pNetwork->hEvtBusyIf);
pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
RTSpinlockDestroy(pNetwork->hAddrSpinlock);
pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
RTMemFree(pNetwork->MacTab.paEntries);
pNetwork->MacTab.paEntries = NULL;
RTMemFree(pNetwork);
/* Release the create/destroy sem. */
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
}
/**
* Opens an existing network.
*
* The call must own the INTNET::hMtxCreateOpenDestroy.
*
* @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 = (uint8_t)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::hMtxCreateOpenDestroy 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::hMtxCreateOpenDestroy.
*/
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 pNetwork = (PINTNETNETWORK)RTMemAllocZ(cb);
if (!pNetwork)
return VERR_NO_MEMORY;
//pNetwork->pNext = NULL;
//pNetwork->pIfs = NULL;
pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
pNetwork->MacTab.cEntries = 0;
pNetwork->MacTab.cEntriesAllocated = INTNET_GROW_DSTTAB_SIZE;
pNetwork->MacTab.paEntries = NULL;
pNetwork->MacTab.fHostPromiscuous = false;
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWirePromiscuous = false;
pNetwork->MacTab.fWireActive = false;
pNetwork->MacTab.pTrunk = NULL;
pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
pNetwork->pIntNet = pIntNet;
//pNetwork->pvObj = NULL;
if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
pNetwork->pbTmp = RT_ALIGN_PT(pNetwork + 1, 64, uint8_t *);
//else
// pNetwork->pbTmp = NULL;
pNetwork->fFlags = fFlags;
//pNetwork->cActiveIFs = 0;
size_t cchName = strlen(pszNetwork);
pNetwork->cchName = (uint8_t)cchName;
Assert(cchName && cchName < sizeof(pNetwork->szName)); /* caller's responsibility. */
memcpy(pNetwork->szName, pszNetwork, cchName); /* '\0' at courtesy of alloc. */
pNetwork->enmTrunkType = enmTrunkType;
Assert(strlen(pszTrunk) < sizeof(pNetwork->szTrunk)); /* caller's responsibility. */
strcpy(pNetwork->szTrunk, pszTrunk);
/*
* Create the semaphore, spinlock and allocate the interface table.
*/
int rc = RTSemEventCreate(&pNetwork->hEvtBusyIf);
if (RT_SUCCESS(rc))
rc = RTSpinlockCreate(&pNetwork->hAddrSpinlock);
if (RT_SUCCESS(rc))
{
pNetwork->MacTab.paEntries = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * pNetwork->MacTab.cEntriesAllocated);
if (!pNetwork->MacTab.paEntries)
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
/*
* Register the object in the current session and link it into the network list.
*/
pNetwork->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNetwork, pIntNet);
if (pNetwork->pvObj)
{
pNetwork->pNext = pIntNet->pNetworks;
pIntNet->pNetworks = pNetwork;
/*
* 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(pNetwork->pvObj, pSession, pNetwork->szName);
if (RT_SUCCESS(rc))
{
/*
* Connect the trunk.
*/
rc = intnetR0NetworkCreateTrunkIf(pNetwork, pSession);
if (RT_SUCCESS(rc))
{
*ppNetwork = pNetwork;
LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNetwork));
return VINF_SUCCESS;
}
}
SUPR0ObjRelease(pNetwork->pvObj, pSession);
LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
return rc;
}
/* cleanup */
rc = VERR_NO_MEMORY;
}
RTSemEventDestroy(pNetwork->hEvtBusyIf);
pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
RTSpinlockDestroy(pNetwork->hAddrSpinlock);
pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
RTMemFree(pNetwork->MacTab.paEntries);
pNetwork->MacTab.paEntries = NULL;
RTMemFree(pNetwork);
LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
return rc;
}
/**
* Opens a network interface and connects it to the specified network.
*
* @returns VBox status code.
* @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(PSUPDRVSESSION pSession, const char *pszNetwork,
INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
uint32_t cbSend, uint32_t cbRecv, PINTNETIFHANDLE phIf)
{
LogFlow(("IntNetR0Open: pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, cbSend, cbRecv, phIf));
/*
* Validate input.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
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:
if (*pszTrunk)
return VERR_INVALID_PARAMETER;
break;
case kIntNetTrunkType_NetFlt:
case kIntNetTrunkType_NetAdp:
if (!*pszTrunk)
return 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/close.
*/
int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
if (RT_FAILURE(rc))
return rc;
/*
* Try open / create the network and create an interface on it for the
* caller to use.
*/
PINTNETNETWORK pNetwork = NULL;
rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
if (RT_SUCCESS(rc))
{
rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, phIf);
if (RT_SUCCESS(rc))
rc = VINF_ALREADY_INITIALIZED;
else
SUPR0ObjRelease(pNetwork->pvObj, pSession);
}
else if (rc == VERR_NOT_FOUND)
{
rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
if (RT_SUCCESS(rc))
{
rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, phIf);
if (RT_FAILURE(rc))
SUPR0ObjRelease(pNetwork->pvObj, pSession);
}
}
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
LogFlow(("IntNetR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
return rc;
}
/**
* VMMR0 request wrapper for IntNetR0Open.
*
* @returns see GMMR0MapUnmapChunk.
* @param pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0OpenReq(PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0Open(pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
}
/**
* Count the internal networks.
*
* This is mainly for providing the testcase with some introspection to validate
* behavior when closing interfaces.
*
* @returns The number of networks.
*/
INTNETR0DECL(uint32_t) IntNetR0GetNetworkCount(void)
{
/*
* Grab the instance.
*/
PINTNET pIntNet = g_pIntNet;
if (!pIntNet)
return 0;
AssertPtrReturn(pIntNet, 0);
AssertReturn(pIntNet->u32Magic == INTNET_MAGIC, 0);
/*
* Grab the mutex and count the networks.
*/
int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
if (RT_FAILURE(rc))
return 0;
uint32_t cNetworks = 0;
for (PINTNETNETWORK pCur = pIntNet->pNetworks; pCur; pCur = pCur->pNext)
cNetworks++;
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
return cNetworks;
}
/**
* Destroys an instance of the Ring-0 internal networking service.
*/
INTNETR0DECL(void) IntNetR0Term(void)
{
LogFlow(("IntNetR0Term:\n"));
/*
* Zap the global pointer and validate it.
*/
PINTNET pIntNet = g_pIntNet;
g_pIntNet = NULL;
if (!pIntNet)
return;
AssertPtrReturnVoid(pIntNet);
AssertReturnVoid(pIntNet->u32Magic == INTNET_MAGIC);
/*
* There is not supposed to be any networks hanging around at this time.
*/
AssertReturnVoid(ASMAtomicCmpXchgU32(&pIntNet->u32Magic, ~INTNET_MAGIC, INTNET_MAGIC));
Assert(pIntNet->pNetworks == NULL);
if (pIntNet->hMtxCreateOpenDestroy != NIL_RTSEMMUTEX)
{
RTSemMutexDestroy(pIntNet->hMtxCreateOpenDestroy);
pIntNet->hMtxCreateOpenDestroy = NIL_RTSEMMUTEX;
}
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);
}
/**
* Initalizes the internal network ring-0 service.
*
* @returns VBox status code.
*/
INTNETR0DECL(int) IntNetR0Init(void)
{
LogFlow(("IntNetR0Init:\n"));
int rc = VERR_NO_MEMORY;
PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
if (pIntNet)
{
//pIntNet->pNetworks = NULL;
rc = RTSemMutexCreate(&pIntNet->hMtxCreateOpenDestroy);
if (RT_SUCCESS(rc))
{
rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
if (RT_SUCCESS(rc))
{
pIntNet->u32Magic = INTNET_MAGIC;
g_pIntNet = pIntNet;
LogFlow(("IntNetR0Init: returns VINF_SUCCESS pIntNet=%p\n", pIntNet));
return VINF_SUCCESS;
}
RTSemMutexDestroy(pIntNet->hMtxCreateOpenDestroy);
}
RTMemFree(pIntNet);
}
LogFlow(("IntNetR0Init: returns %Rrc\n", rc));
return rc;
}