SrvIntNetR0.cpp revision f091ce66ee934d599f16056078a9a76d7286b959
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* $Id$ */
fa9e4066f08beec538e775443c5be79dd423fcabahrens/** @file
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Internal networking - The ring 0 service.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
441d80aa4f613b6298fc8bd3151f4be02dbf84fclling
441d80aa4f613b6298fc8bd3151f4be02dbf84fclling/*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Copyright (C) 2006-2011 Oracle Corporation
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This file is part of VirtualBox Open Source Edition (OSE), as
fa9e4066f08beec538e775443c5be79dd423fcabahrens * available from http://www.virtualbox.org. This file is free software;
fa9e4066f08beec538e775443c5be79dd423fcabahrens * you can redistribute it and/or modify it under the terms of the GNU
fa9e4066f08beec538e775443c5be79dd423fcabahrens * General Public License (GPL) as published by the Free Software
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Foundation, in version 2 as it comes in the "COPYING" file of the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/*******************************************************************************
fa9e4066f08beec538e775443c5be79dd423fcabahrens* Header Files *
99653d4ee642c6528e88224f12409a5f23060994eschrock*******************************************************************************/
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define LOG_GROUP LOG_GROUP_SRV_INTNET
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante#include <VBox/intnet.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <VBox/intnetinline.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <VBox/vmm/pdmnetinline.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <VBox/sup.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <VBox/vmm/pdm.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <VBox/log.h>
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/asm.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/assert.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/handletable.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/mp.h>
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl#include <iprt/mem.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/net.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/semaphore.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/spinlock.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/string.h>
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl#include <iprt/thread.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/time.h>
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
fa9e4066f08beec538e775443c5be79dd423fcabahrens/*******************************************************************************
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock* Defined Constants And Macros *
06eeb2ad640ce72d394ac521094bed7681044408ek*******************************************************************************/
fa9e4066f08beec538e775443c5be79dd423fcabahrens/** @def INTNET_WITH_DHCP_SNOOPING
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
b1b8ab34de515a5e83206da22c3d7e563241b021lling#define INTNET_WITH_DHCP_SNOOPING
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/** The maximum number of interface in a network. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw#define INTNET_MAX_IFS (1023 + 1 + 16)
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/** The number of entries to grow the destination tables with. */
990b4856d0eaada6f8140335733a1b1771ed2746lling#if 0
990b4856d0eaada6f8140335733a1b1771ed2746lling# define INTNET_GROW_DSTTAB_SIZE 16
990b4856d0eaada6f8140335733a1b1771ed2746lling#else
990b4856d0eaada6f8140335733a1b1771ed2746lling# define INTNET_GROW_DSTTAB_SIZE 1
990b4856d0eaada6f8140335733a1b1771ed2746lling#endif
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/** The wakeup bit in the INTNETIF::cBusy and INTNETRUNKIF::cBusy counters. */
990b4856d0eaada6f8140335733a1b1771ed2746lling#define INTNET_BUSY_WAKEUP_MASK RT_BIT_32(30)
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/*******************************************************************************
990b4856d0eaada6f8140335733a1b1771ed2746lling* Structures and Typedefs *
990b4856d0eaada6f8140335733a1b1771ed2746lling*******************************************************************************/
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * MAC address lookup table entry.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETMACTABENTRY
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The MAC address of this entry. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTMAC MacAddr;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Is it is effectively promiscuous mode. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fPromiscuousEff;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Is it promiscuous and should it see unrelated trunk traffic. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fPromiscuousSeeTrunk;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Is it active.
990b4856d0eaada6f8140335733a1b1771ed2746lling * We ignore the entry if this is clear and may end up sending packets addressed
990b4856d0eaada6f8140335733a1b1771ed2746lling * to this interface onto the trunk. The reasoning for this is that this could
990b4856d0eaada6f8140335733a1b1771ed2746lling * be the interface of a VM that just has been teleported to a different host. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fActive;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the network interface. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNETIF *pIf;
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETMACTABENTRY;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a MAC address lookup table entry. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETMACTABENTRY *PINTNETMACTABENTRY;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * MAC address lookup table.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * @todo Having this in a separate structure didn't work out as well as it
990b4856d0eaada6f8140335733a1b1771ed2746lling * should. Consider merging it into INTNETNETWORK.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETMACTAB
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The current number of entries. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of entries we've allocated space for. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cEntriesAllocated;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Table entries. */
990b4856d0eaada6f8140335733a1b1771ed2746lling PINTNETMACTABENTRY paEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of interface entries currently in promicuous mode. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cPromiscuousEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of interface entries currently in promicuous mode that
990b4856d0eaada6f8140335733a1b1771ed2746lling * shall not see unrelated trunk traffic. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cPromiscuousNoTrunkEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The host MAC address (reported). */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTMAC HostMac;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The effective host promiscuous setting (reported). */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fHostPromiscuousEff;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The real host promiscuous setting (reported). */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fHostPromiscuousReal;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether the host is active. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fHostActive;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether the wire is promiscuous (config). */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fWirePromiscuousEff;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether the wire is promiscuous (config).
990b4856d0eaada6f8140335733a1b1771ed2746lling * (Shadows INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE in
990b4856d0eaada6f8140335733a1b1771ed2746lling * INTNETNETWORK::fFlags.) */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fWirePromiscuousReal;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether the wire is active. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fWireActive;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the trunk interface. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNETTRUNKIF *pTrunk;
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETMACTAB;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a MAC address . */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETMACTAB *PINTNETMACTAB;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Destination table.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETDSTTAB
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The trunk destinations. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fTrunkDst;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the trunk interface (referenced) if fTrunkDst is non-zero. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNETTRUNKIF *pTrunk;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of destination interfaces. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cIfs;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The interfaces (referenced). Variable sized array. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct
990b4856d0eaada6f8140335733a1b1771ed2746lling {
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The destination interface. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNETIF *pIf;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether to replace the destination MAC address.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is used when sharing MAC address with the host on the wire(less). */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fReplaceDstMac;
990b4856d0eaada6f8140335733a1b1771ed2746lling } aIfs[1];
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETDSTTAB;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a destination table. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETDSTTAB *PINTNETDSTTAB;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a const destination table. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETDSTTAB const *PCINTNETDSTTAB;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Network layer address type. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef enum INTNETADDRTYPE
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The invalid 0 entry. */
990b4856d0eaada6f8140335733a1b1771ed2746lling kIntNetAddrType_Invalid = 0,
990b4856d0eaada6f8140335733a1b1771ed2746lling /** IP version 4. */
990b4856d0eaada6f8140335733a1b1771ed2746lling kIntNetAddrType_IPv4,
990b4856d0eaada6f8140335733a1b1771ed2746lling /** IP version 6. */
990b4856d0eaada6f8140335733a1b1771ed2746lling kIntNetAddrType_IPv6,
990b4856d0eaada6f8140335733a1b1771ed2746lling /** IPX. */
990b4856d0eaada6f8140335733a1b1771ed2746lling kIntNetAddrType_IPX,
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The end of the valid values. */
990b4856d0eaada6f8140335733a1b1771ed2746lling kIntNetAddrType_End,
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The usual 32-bit hack. */
990b4856d0eaada6f8140335733a1b1771ed2746lling kIntNetAddrType_32BitHack = 0x7fffffff
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETADDRTYPE;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a network layer address type. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETADDRTYPE *PINTNETADDRTYPE;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Address and type.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETADDR
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The address type. */
990b4856d0eaada6f8140335733a1b1771ed2746lling INTNETADDRTYPE enmType;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The address. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTNETADDRU Addr;
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETADDR;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to an address. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETADDR *PINTNETADDR;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a const address. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETADDR const *PCINTNETADDR;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Address cache for a specific network layer.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETADDRCACHE
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the table of addresses. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t *pbEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of valid address entries. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t cEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of allocated address entries. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t cEntriesAlloc;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The address size. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t cbAddress;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The size of an entry. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t cbEntry;
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETADDRCACHE;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to an address cache. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETADDRCACHE *PINTNETADDRCACHE;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a const address cache. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * A network interface.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * Unless explicitly stated, all members are protect by the network semaphore.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETIF
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The MAC address.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is shadowed by INTNETMACTABENTRY::MacAddr. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTMAC MacAddr;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Set if the INTNET::MacAddr member has been explicitly set. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fMacSet;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Tracks the desired promiscuous setting of the interface. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fPromiscuousReal;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether the interface is active or not.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is shadowed by INTNETMACTABENTRY::fActive. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fActive;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Whether someone is currently in the destructor or has indicated that
990b4856d0eaada6f8140335733a1b1771ed2746lling * the end is nigh by means of IntNetR0IfAbortWait. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool volatile fDestroying;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The flags specified when opening this interface. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fOpenFlags;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Number of yields done to try make the interface read pending data.
990b4856d0eaada6f8140335733a1b1771ed2746lling * We will stop yielding when this reaches a threshold assuming that the VM is
990b4856d0eaada6f8140335733a1b1771ed2746lling * paused or that it simply isn't worth all the delay. It is cleared when a
990b4856d0eaada6f8140335733a1b1771ed2746lling * successful send has been done. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cYields;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the current exchange buffer (ring-0). */
990b4856d0eaada6f8140335733a1b1771ed2746lling PINTNETBUF pIntBuf;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to ring-3 mapping of the current exchange buffer. */
990b4856d0eaada6f8140335733a1b1771ed2746lling R3PTRTYPE(PINTNETBUF) pIntBufR3;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the default exchange buffer for the interface. */
990b4856d0eaada6f8140335733a1b1771ed2746lling PINTNETBUF pIntBufDefault;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to ring-3 mapping of the default exchange buffer. */
990b4856d0eaada6f8140335733a1b1771ed2746lling R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Event semaphore which a receiver/consumer thread will sleep on while
990b4856d0eaada6f8140335733a1b1771ed2746lling * waiting for data to arrive. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTSEMEVENT volatile hRecvEvent;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Number of threads sleeping on the event semaphore. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cSleepers;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The interface handle.
990b4856d0eaada6f8140335733a1b1771ed2746lling * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
990b4856d0eaada6f8140335733a1b1771ed2746lling * should return with the appropriate error condition. */
990b4856d0eaada6f8140335733a1b1771ed2746lling INTNETIFHANDLE volatile hIf;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the network this interface is connected to.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is protected by the INTNET::hMtxCreateOpenDestroy. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNETNETWORK *pNetwork;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The session this interface is associated with. */
990b4856d0eaada6f8140335733a1b1771ed2746lling PSUPDRVSESSION pSession;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The SUPR0 object id. */
990b4856d0eaada6f8140335733a1b1771ed2746lling void *pvObj;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The network layer address cache. (Indexed by type, 0 entry isn't used.)
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is protected by the address spinlock of the network. */
990b4856d0eaada6f8140335733a1b1771ed2746lling INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Spinlock protecting the input (producer) side of the receive ring. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTSPINLOCK hRecvInSpinlock;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Busy count for tracking destination table references and active sends.
990b4856d0eaada6f8140335733a1b1771ed2746lling * Usually incremented while owning the switch table spinlock. The 30th bit
990b4856d0eaada6f8140335733a1b1771ed2746lling * is used to indicate wakeup. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t volatile cBusy;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The preallocated destination table.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is NULL when it's in use as a precaution against unserialized
990b4856d0eaada6f8140335733a1b1771ed2746lling * transmitting. This is grown when new interfaces are added to the network. */
990b4856d0eaada6f8140335733a1b1771ed2746lling PINTNETDSTTAB volatile pDstTab;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the trunk's per interface data. Can be NULL. */
990b4856d0eaada6f8140335733a1b1771ed2746lling void *pvIfData;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Header buffer for when we're carving GSO frames. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t abGsoHdrs[256];
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw} INTNETIF;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw/** Pointer to an internal network interface. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgwtypedef INTNETIF *PINTNETIF;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw/**
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * A trunk interface.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgwtypedef struct INTNETTRUNKIF
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw{
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** The port interface we present to the component. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw INTNETTRUNKSWPORT SwitchPort;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** The port interface we get from the component. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw PINTNETTRUNKIFPORT pIfPort;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** Pointer to the network we're connect to.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * This may be NULL if we're orphaned? */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw struct INTNETNETWORK *pNetwork;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** The current MAC address for the interface. (reported)
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * Updated while owning the switch table spinlock. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTMAC MacAddr;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** Whether to supply physical addresses with the outbound SGs. (reported) */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fPhysSG;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Explicit alignment. */
990b4856d0eaada6f8140335733a1b1771ed2746lling bool fUnused;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Busy count for tracking destination table references and active sends.
990b4856d0eaada6f8140335733a1b1771ed2746lling * Usually incremented while owning the switch table spinlock. The 30th bit
990b4856d0eaada6f8140335733a1b1771ed2746lling * is used to indicate wakeup. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t volatile cBusy;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Mask of destinations that pfnXmit cope with disabled preemption for. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fNoPreemptDsts;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The GSO capabilities of the wire destination. (reported) */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fWireGsoCapabilites;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The GSO capabilities of the host destination. (reported)
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is as bit map where each bit represents the GSO type with the same
990b4856d0eaada6f8140335733a1b1771ed2746lling * number. */
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock uint32_t fHostGsoCapabilites;
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock /** The destination table spinlock, interrupt safe.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * Protects apTaskDstTabs and apIntDstTabs. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTSPINLOCK hDstTabSpinlock;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of entries in apIntDstTabs. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cIntDstTabs;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The task time destination tables.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @remarks intnetR0NetworkEnsureTabSpace and others ASSUMES this immediately
990b4856d0eaada6f8140335733a1b1771ed2746lling * precedes apIntDstTabs so that these two tables can be used as one
990b4856d0eaada6f8140335733a1b1771ed2746lling * contiguous one. */
990b4856d0eaada6f8140335733a1b1771ed2746lling PINTNETDSTTAB apTaskDstTabs[2];
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The interrupt / disabled-preemption time destination tables.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is a variable sized array. */
990b4856d0eaada6f8140335733a1b1771ed2746lling PINTNETDSTTAB apIntDstTabs[1];
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETTRUNKIF;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a trunk interface. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETTRUNKIF *PINTNETTRUNKIF;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
990b4856d0eaada6f8140335733a1b1771ed2746lling#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Internal representation of a network.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef struct INTNETNETWORK
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The Next network in the chain.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is protected by the INTNET::hMtxCreateOpenDestroy. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNETNETWORK *pNext;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The spinlock protecting MacTab and INTNETTRUNKIF::aAddrCache.
990b4856d0eaada6f8140335733a1b1771ed2746lling * Interrupt safe. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTSPINLOCK hAddrSpinlock;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** MAC address table.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This doubles as interface collection. */
990b4856d0eaada6f8140335733a1b1771ed2746lling INTNETMACTAB MacTab;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Wait for an interface to stop being busy so it can be removed or have its
990b4856d0eaada6f8140335733a1b1771ed2746lling * destination table replaced. We have to wait upon this while owning the
990b4856d0eaada6f8140335733a1b1771ed2746lling * network mutex. Will only ever have one waiter because of the big mutex. */
990b4856d0eaada6f8140335733a1b1771ed2746lling RTSEMEVENT hEvtBusyIf;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the instance data. */
990b4856d0eaada6f8140335733a1b1771ed2746lling struct INTNET *pIntNet;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The SUPR0 object id. */
990b4856d0eaada6f8140335733a1b1771ed2746lling void *pvObj;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Pointer to the temporary buffer that is used when snooping fragmented packets.
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is allocated after this structure if we're sharing the MAC address with
990b4856d0eaada6f8140335733a1b1771ed2746lling * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundary. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t *pbTmp;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Network creation flags (INTNET_OPEN_FLAGS_*). */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fFlags;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** Any restrictive policies required as a minimum by some interface.
990b4856d0eaada6f8140335733a1b1771ed2746lling * (INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES) */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fMinFlags;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The number of active interfaces (excluding the trunk). */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cActiveIFs;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The length of the network name. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint8_t cchName;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The network name. */
990b4856d0eaada6f8140335733a1b1771ed2746lling char szName[INTNET_MAX_NETWORK_NAME];
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The trunk type. */
990b4856d0eaada6f8140335733a1b1771ed2746lling INTNETTRUNKTYPE enmTrunkType;
990b4856d0eaada6f8140335733a1b1771ed2746lling /** The trunk name. */
990b4856d0eaada6f8140335733a1b1771ed2746lling char szTrunk[INTNET_MAX_TRUNK_NAME];
990b4856d0eaada6f8140335733a1b1771ed2746lling} INTNETNETWORK;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to an internal network. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef INTNETNETWORK *PINTNETNETWORK;
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to a const internal network. */
990b4856d0eaada6f8140335733a1b1771ed2746llingtypedef const INTNETNETWORK *PCINTNETNETWORK;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/** The size of the buffer INTNETNETWORK::pbTmp points at. */
990b4856d0eaada6f8140335733a1b1771ed2746lling#define INTNETNETWORK_TMP_SIZE 2048
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Internal networking instance.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgwtypedef struct INTNET
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw{
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** Magic number (INTNET_MAGIC). */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw uint32_t volatile u32Magic;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** Mutex protecting the creation, opening and destruction of both networks and
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * interfaces. (This means all operations affecting the pNetworks list.) */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTSEMMUTEX hMtxCreateOpenDestroy;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** List of networks. Protected by INTNET::Spinlock. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw PINTNETNETWORK volatile pNetworks;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /** Handle table for the interfaces. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTHANDLETABLE hHtIfs;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw} INTNET;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw/** Pointer to an internal network ring-0 instance. */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgwtypedef struct INTNET *PINTNET;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw/** Magic number for the internal network instance data (Hayao Miyazaki). */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw#define INTNET_MAGIC UINT32_C(0x19410105)
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw/*******************************************************************************
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw* Global Variables *
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw*******************************************************************************/
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Pointer to the internal network instance data. */
990b4856d0eaada6f8140335733a1b1771ed2746llingstatic PINTNET volatile g_pIntNet = NULL;
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock
990b4856d0eaada6f8140335733a1b1771ed2746llingstatic const struct INTNETOPENNETWORKFLAGS
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fRestrictive; /**< The restrictive flag (deny/disabled). */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fRelaxed; /**< The relaxed flag (allow/enabled). */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fFixed; /**< The config-fixed flag. */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t fPair; /**< The pair of restrictive and relaxed flags. */
990b4856d0eaada6f8140335733a1b1771ed2746lling}
990b4856d0eaada6f8140335733a1b1771ed2746lling/** Open network policy flags relating to the network. */
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrockg_afIntNetOpenNetworkNetFlags[] =
990b4856d0eaada6f8140335733a1b1771ed2746lling{
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock { INTNET_OPEN_FLAGS_ACCESS_RESTRICTED, INTNET_OPEN_FLAGS_ACCESS_PUBLIC, INTNET_OPEN_FLAGS_ACCESS_FIXED, INTNET_OPEN_FLAGS_ACCESS_RESTRICTED | INTNET_OPEN_FLAGS_ACCESS_PUBLIC },
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock { INTNET_OPEN_FLAGS_PROMISC_DENY_CLIENTS, INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS, INTNET_OPEN_FLAGS_PROMISC_FIXED, INTNET_OPEN_FLAGS_PROMISC_DENY_CLIENTS | INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS },
990b4856d0eaada6f8140335733a1b1771ed2746lling { INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_HOST, INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST, INTNET_OPEN_FLAGS_PROMISC_FIXED, INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_HOST | INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST },
990b4856d0eaada6f8140335733a1b1771ed2746lling { INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_WIRE, INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE, INTNET_OPEN_FLAGS_PROMISC_FIXED, INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_WIRE | INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE },
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock { INTNET_OPEN_FLAGS_TRUNK_HOST_DISABLED, INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_HOST_DISABLED | INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED },
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock { INTNET_OPEN_FLAGS_TRUNK_HOST_CHASTE_MODE, INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_HOST_CHASTE_MODE | INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE },
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock { INTNET_OPEN_FLAGS_TRUNK_WIRE_DISABLED, INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_WIRE_DISABLED | INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED },
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock { INTNET_OPEN_FLAGS_TRUNK_WIRE_CHASTE_MODE, INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_WIRE_CHASTE_MODE | INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE },
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock},
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock/** Open network policy flags relating to the new interface. */
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrockg_afIntNetOpenNetworkIfFlags[] =
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock{
990b4856d0eaada6f8140335733a1b1771ed2746lling { INTNET_OPEN_FLAGS_IF_PROMISC_DENY, INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW, INTNET_OPEN_FLAGS_IF_FIXED, INTNET_OPEN_FLAGS_IF_PROMISC_DENY | INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW },
990b4856d0eaada6f8140335733a1b1771ed2746lling { INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK, INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK, INTNET_OPEN_FLAGS_IF_FIXED, INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK | INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK },
990b4856d0eaada6f8140335733a1b1771ed2746lling};
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Worker for intnetR0SgWritePart that deals with the case where the
990b4856d0eaada6f8140335733a1b1771ed2746lling * request doesn't fit into the first segment.
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock *
990b4856d0eaada6f8140335733a1b1771ed2746lling * @returns true, unless the request or SG invalid.
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock * @param pSG The SG list to write to.
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock * @param off Where to start writing (offset into the SG).
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock * @param cb How much to write.
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock * @param pvBuf The buffer to containing the bits to write.
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock */
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrockstatic bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock{
990b4856d0eaada6f8140335733a1b1771ed2746lling if (RT_UNLIKELY(off + cb > pSG->cbTotal))
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock return false;
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock
2c32020f848d8e5619a1f441a92f0ee4aca1b297eschrock /*
2c32020f848d8e5619a1f441a92f0ee4aca1b297eschrock * Skip ahead to the segment where off starts.
2c32020f848d8e5619a1f441a92f0ee4aca1b297eschrock */
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock unsigned iSeg = 0;
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock while (off > pSG->aSegs[iSeg].cb)
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock {
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock off -= pSG->aSegs[iSeg++].cb;
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock AssertReturn(iSeg < cSegs, false);
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock }
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock
2f8aaab38e6371ad39ed90a1211ba8921acbb4d5eschrock /*
990b4856d0eaada6f8140335733a1b1771ed2746lling * Copy the data, hoping that it's all from one segment...
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
990b4856d0eaada6f8140335733a1b1771ed2746lling if (cbCanCopy >= cb)
990b4856d0eaada6f8140335733a1b1771ed2746lling memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
990b4856d0eaada6f8140335733a1b1771ed2746lling else
990b4856d0eaada6f8140335733a1b1771ed2746lling {
990b4856d0eaada6f8140335733a1b1771ed2746lling /* copy the portion in the current segment. */
990b4856d0eaada6f8140335733a1b1771ed2746lling memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
990b4856d0eaada6f8140335733a1b1771ed2746lling cb -= cbCanCopy;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /* copy the portions in the other segments. */
990b4856d0eaada6f8140335733a1b1771ed2746lling do
990b4856d0eaada6f8140335733a1b1771ed2746lling {
990b4856d0eaada6f8140335733a1b1771ed2746lling pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
990b4856d0eaada6f8140335733a1b1771ed2746lling iSeg++;
990b4856d0eaada6f8140335733a1b1771ed2746lling AssertReturn(iSeg < cSegs, false);
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
990b4856d0eaada6f8140335733a1b1771ed2746lling memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling cb -= cbCanCopy;
990b4856d0eaada6f8140335733a1b1771ed2746lling } while (cb > 0);
990b4856d0eaada6f8140335733a1b1771ed2746lling }
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling return true;
990b4856d0eaada6f8140335733a1b1771ed2746lling}
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Writes to a part of an SG.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * @returns true on success, false on failure (out of bounds).
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pSG The SG list to write to.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param off Where to start writing (offset into the SG).
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param cb How much to write.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pvBuf The buffer to containing the bits to write.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingDECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling Assert(off + cb > off);
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /* The optimized case. */
990b4856d0eaada6f8140335733a1b1771ed2746lling if (RT_LIKELY( pSG->cSegsUsed == 1
990b4856d0eaada6f8140335733a1b1771ed2746lling || pSG->aSegs[0].cb >= off + cb))
990b4856d0eaada6f8140335733a1b1771ed2746lling {
990b4856d0eaada6f8140335733a1b1771ed2746lling Assert(pSG->cbTotal == pSG->aSegs[0].cb);
990b4856d0eaada6f8140335733a1b1771ed2746lling memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
990b4856d0eaada6f8140335733a1b1771ed2746lling return true;
990b4856d0eaada6f8140335733a1b1771ed2746lling }
990b4856d0eaada6f8140335733a1b1771ed2746lling return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
990b4856d0eaada6f8140335733a1b1771ed2746lling}
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Reads a byte from a SG list.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * @returns The byte on success. 0xff on failure.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pSG The SG list to read.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param off The offset (into the SG) off the byte.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingDECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling if (RT_LIKELY(pSG->aSegs[0].cb > off))
990b4856d0eaada6f8140335733a1b1771ed2746lling return ((uint8_t const *)pSG->aSegs[0].pv)[off];
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling off -= pSG->aSegs[0].cb;
990b4856d0eaada6f8140335733a1b1771ed2746lling unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
990b4856d0eaada6f8140335733a1b1771ed2746lling for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
990b4856d0eaada6f8140335733a1b1771ed2746lling {
990b4856d0eaada6f8140335733a1b1771ed2746lling if (pSG->aSegs[iSeg].cb > off)
990b4856d0eaada6f8140335733a1b1771ed2746lling return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
990b4856d0eaada6f8140335733a1b1771ed2746lling off -= pSG->aSegs[iSeg].cb;
990b4856d0eaada6f8140335733a1b1771ed2746lling }
990b4856d0eaada6f8140335733a1b1771ed2746lling return false;
990b4856d0eaada6f8140335733a1b1771ed2746lling}
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Worker for intnetR0SgReadPart that deals with the case where the
990b4856d0eaada6f8140335733a1b1771ed2746lling * requested data isn't in the first segment.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * @returns true, unless the SG is invalid.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pSG The SG list to read.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param off Where to start reading (offset into the SG).
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param cb How much to read.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pvBuf The buffer to read into.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingstatic bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling if (RT_UNLIKELY(off + cb > pSG->cbTotal))
990b4856d0eaada6f8140335733a1b1771ed2746lling return false;
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling /*
990b4856d0eaada6f8140335733a1b1771ed2746lling * Skip ahead to the segment where off starts.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
990b4856d0eaada6f8140335733a1b1771ed2746lling unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
fa9e4066f08beec538e775443c5be79dd423fcabahrens unsigned iSeg = 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (off > pSG->aSegs[iSeg].cb)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens off -= pSG->aSegs[iSeg++].cb;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw AssertReturn(iSeg < cSegs, false);
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Copy the data, hoping that it's all from one segment...
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock */
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock if (cbCanCopy >= cb)
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock else
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock {
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock /* copy the portion in the current segment. */
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock cb -= cbCanCopy;
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock /* copy the portions in the other segments. */
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock do
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock {
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin pvBuf = (uint8_t *)pvBuf + cbCanCopy;
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin iSeg++;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw AssertReturn(iSeg < cSegs, false);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
99653d4ee642c6528e88224f12409a5f23060994eschrock memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock cb -= cbCanCopy;
fa9e4066f08beec538e775443c5be79dd423fcabahrens } while (cb > 0);
b468a217b67dc26ce21da5d5a2ca09bb6249e4faeschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens return true;
b81d61a68b235e0529ebadc18e14d9d1dd52a258lling}
99653d4ee642c6528e88224f12409a5f23060994eschrock
b81d61a68b235e0529ebadc18e14d9d1dd52a258lling
b81d61a68b235e0529ebadc18e14d9d1dd52a258lling/**
b81d61a68b235e0529ebadc18e14d9d1dd52a258lling * Reads a part of an SG into a buffer.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
99653d4ee642c6528e88224f12409a5f23060994eschrock * @returns true on success, false on failure (out of bounds).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pSG The SG list to read.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param off Where to start reading (offset into the SG).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param cb How much to read.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pvBuf The buffer to read into.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
99653d4ee642c6528e88224f12409a5f23060994eschrockDECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
fa9e4066f08beec538e775443c5be79dd423fcabahrens Assert(off + cb > off);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* The optimized case. */
99653d4ee642c6528e88224f12409a5f23060994eschrock if (RT_LIKELY( pSG->cSegsUsed == 1
99653d4ee642c6528e88224f12409a5f23060994eschrock || pSG->aSegs[0].cb >= off + cb))
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens Assert(pSG->cbTotal == pSG->aSegs[0].cb);
fa9e4066f08beec538e775443c5be79dd423fcabahrens memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
99653d4ee642c6528e88224f12409a5f23060994eschrock return true;
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
5ad820458efd0fdb914baff9c1447c22b819fa23nd}
5ad820458efd0fdb914baff9c1447c22b819fa23nd
5ad820458efd0fdb914baff9c1447c22b819fa23nd
5ad820458efd0fdb914baff9c1447c22b819fa23nd/**
5ad820458efd0fdb914baff9c1447c22b819fa23nd * Wait for a busy counter to reach zero.
5ad820458efd0fdb914baff9c1447c22b819fa23nd *
5ad820458efd0fdb914baff9c1447c22b819fa23nd * @param pNetwork The network.
5ad820458efd0fdb914baff9c1447c22b819fa23nd * @param pcBusy The busy counter.
5ad820458efd0fdb914baff9c1447c22b819fa23nd */
5ad820458efd0fdb914baff9c1447c22b819fa23ndstatic void intnetR0BusyWait(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
5ad820458efd0fdb914baff9c1447c22b819fa23nd{
5ad820458efd0fdb914baff9c1447c22b819fa23nd if (ASMAtomicReadU32(pcBusy) == 0)
5ad820458efd0fdb914baff9c1447c22b819fa23nd return;
5ad820458efd0fdb914baff9c1447c22b819fa23nd
5ad820458efd0fdb914baff9c1447c22b819fa23nd /*
5ad820458efd0fdb914baff9c1447c22b819fa23nd * We have to be a bit cautious here so we don't destroy the network or the
5ad820458efd0fdb914baff9c1447c22b819fa23nd * semaphore before intnetR0BusyDec has signalled us.
5ad820458efd0fdb914baff9c1447c22b819fa23nd */
5ad820458efd0fdb914baff9c1447c22b819fa23nd
5ad820458efd0fdb914baff9c1447c22b819fa23nd /* Reset the semaphore and flip the wakeup bit. */
5ad820458efd0fdb914baff9c1447c22b819fa23nd RTSemEventWait(pNetwork->hEvtBusyIf, 0); /* clear it */
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint32_t cCurBusy = ASMAtomicReadU32(pcBusy);
fa9e4066f08beec538e775443c5be79dd423fcabahrens do
99653d4ee642c6528e88224f12409a5f23060994eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (cCurBusy == 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return;
99653d4ee642c6528e88224f12409a5f23060994eschrock AssertMsg(!(cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
fa9e4066f08beec538e775443c5be79dd423fcabahrens } while (!ASMAtomicCmpXchgExU32(pcBusy, cCurBusy | INTNET_BUSY_WAKEUP_MASK, cCurBusy, &cCurBusy));
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Wait for the count to reach zero. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens do
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens int rc2 = RTSemEventWait(pNetwork->hEvtBusyIf, 30000); NOREF(rc2);
99653d4ee642c6528e88224f12409a5f23060994eschrock //AssertMsg(RT_SUCCESS(rc2), ("rc=%Rrc *pcBusy=%#x (%#x)\n", rc2, ASMAtomicReadU32(pcBusy), cCurBusy ));
fa9e4066f08beec538e775443c5be79dd423fcabahrens cCurBusy = ASMAtomicReadU32(pcBusy);
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertMsg((cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
fa9e4066f08beec538e775443c5be79dd423fcabahrens } while ( cCurBusy != INTNET_BUSY_WAKEUP_MASK
fa9e4066f08beec538e775443c5be79dd423fcabahrens || !ASMAtomicCmpXchgU32(pcBusy, 0, INTNET_BUSY_WAKEUP_MASK));
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Decrements the busy counter and maybe wakes up any threads waiting for it to
99653d4ee642c6528e88224f12409a5f23060994eschrock * reach zero.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pNetwork The network.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pcBusy The busy counter.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrockDECLINLINE(void) intnetR0BusyDec(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
99653d4ee642c6528e88224f12409a5f23060994eschrock uint32_t cNewBusy = ASMAtomicDecU32(pcBusy);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (RT_UNLIKELY( cNewBusy == INTNET_BUSY_WAKEUP_MASK
fa9e4066f08beec538e775443c5be79dd423fcabahrens && pNetwork))
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock RTSemEventSignal(pNetwork->hEvtBusyIf);
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock}
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Increments the busy count of the specified interface.
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * The caller must own the MAC address table spinlock.
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock *
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock * @param pIf The interface.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(void) intnetR0BusyDecIf(PINTNETIF pIf)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens intnetR0BusyDec(pIf->pNetwork, &pIf->cBusy);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Increments the busy count of the specified interface.
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock *
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock * The caller must own the MAC address table spinlock or an explicity reference.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pTrunk The trunk.
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(void) intnetR0BusyDecTrunk(PINTNETTRUNKIF pTrunk)
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock{
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock intnetR0BusyDec(pTrunk->pNetwork, &pTrunk->cBusy);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
99653d4ee642c6528e88224f12409a5f23060994eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock * Increments the busy count of the specified interface.
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock *
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock * The caller must own the MAC address table spinlock or an explicity reference.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIf The interface.
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock */
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrockDECLINLINE(void) intnetR0BusyIncIf(PINTNETIF pIf)
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock{
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock uint32_t cNewBusy = ASMAtomicIncU32(&pIf->cBusy);
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock NOREF(cNewBusy);
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock}
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Increments the busy count of the specified interface.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The caller must own the MAC address table spinlock or an explicity reference.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pTrunk The trunk.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(void) intnetR0BusyIncTrunk(PINTNETTRUNKIF pTrunk)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint32_t cNewBusy = ASMAtomicIncU32(&pTrunk->cBusy);
99653d4ee642c6528e88224f12409a5f23060994eschrock AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
fa9e4066f08beec538e775443c5be79dd423fcabahrens NOREF(cNewBusy);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Retain an interface.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns VBox status code, can assume success in most situations.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIf The interface instance.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pSession The current session.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens int rc = SUPR0ObjAddRefEx(pIf->pvObj, pSession, true /* fNoBlocking */);
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertRCReturn(rc, rc);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return VINF_SUCCESS;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
088e9d477eee66081e407fbc5a33c4da25f66f6aeschrock * Release an interface previously retained by intnetR0IfRetain or
088e9d477eee66081e407fbc5a33c4da25f66f6aeschrock * by handle lookup/freeing.
b1b8ab34de515a5e83206da22c3d7e563241b021lling *
b1b8ab34de515a5e83206da22c3d7e563241b021lling * @returns true if destroyed, false if not.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIf The interface instance.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pSession The current session.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(bool) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertRC(rc);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return rc == VINF_OBJECT_DESTROYED;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * RTHandleCreateEx callback that retains an object in the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * handle table before returning it.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * (Avoids racing the freeing of the handle.)
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns VBox status code.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param hHandleTable The handle table (ignored).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pvObj The object (INTNETIF).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pvCtx The context (SUPDRVSESSION).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pvUser The user context (ignored).
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens NOREF(pvUser);
fa9e4066f08beec538e775443c5be79dd423fcabahrens NOREF(hHandleTable);
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETIF pIf = (PINTNETIF)pvObj;
99653d4ee642c6528e88224f12409a5f23060994eschrock if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
990b4856d0eaada6f8140335733a1b1771ed2746lling return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return VINF_SUCCESS;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
99653d4ee642c6528e88224f12409a5f23060994eschrock
990b4856d0eaada6f8140335733a1b1771ed2746lling
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Checks if the interface has a usable MAC address or not.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
99653d4ee642c6528e88224f12409a5f23060994eschrock * @returns true if MacAddr is usable, false if not.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pIf The interface.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
351420b34707afeafa8d5c3e0c77b7bcffb1edc0llingDECL_FORCE_INLINE(bool) intnetR0IfHasMacAddr(PINTNETIF pIf)
990b4856d0eaada6f8140335733a1b1771ed2746lling{
fa9e4066f08beec538e775443c5be79dd423fcabahrens return pIf->fMacSet || !(pIf->MacAddr.au8[0] & 1);
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling}
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling * Locates the MAC address table entry for the given interface.
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * The caller holds the MAC address table spinlock, obviously.
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns Pointer to the entry on if found, NULL if not.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pNetwork The network.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIf The interface.
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks */
351420b34707afeafa8d5c3e0c77b7bcffb1edc0llingDECLINLINE(PINTNETMACTABENTRY) intnetR0NetworkFindMacAddrEntry(PINTNETNETWORK pNetwork, PINTNETIF pIf)
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock{
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling uint32_t iIf = pNetwork->MacTab.cEntries;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (iIf-- > 0)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return &pNetwork->MacTab.paEntries[iIf];
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens return NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Checks if the IPv4 address is a broadcast address.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @returns true/false.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param Addr The address, network endian.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Just check for 255.255.255.255 atm. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return Addr.u == UINT32_MAX;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Checks if the IPv4 address is a good interface address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns true/false.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param Addr The address, network endian.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrockDECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Usual suspects. */
99653d4ee642c6528e88224f12409a5f23060994eschrock if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens || Addr.au8[0] == 0) /* Current network, can be used as source address. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return false;
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Unusual suspects. */
99653d4ee642c6528e88224f12409a5f23060994eschrock if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
fa9e4066f08beec538e775443c5be79dd423fcabahrens || (Addr.au8[0] & 0xf0) == 224 /* Multicast */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan ))
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return false;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return true;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan}
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Gets the address size of a network layer type.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns size in bytes.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param enmType The type.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrockDECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
990b4856d0eaada6f8140335733a1b1771ed2746lling switch (enmType)
990b4856d0eaada6f8140335733a1b1771ed2746lling {
fa9e4066f08beec538e775443c5be79dd423fcabahrens case kIntNetAddrType_IPv4: return 4;
fa9e4066f08beec538e775443c5be79dd423fcabahrens case kIntNetAddrType_IPv6: return 16;
990b4856d0eaada6f8140335733a1b1771ed2746lling case kIntNetAddrType_IPX: return 4 + 6;
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock default: AssertFailedReturn(0);
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling * Compares two address to see if they are equal, assuming naturally align structures.
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns true if equal, false if not.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pAddr1 The first address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pAddr2 The second address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param cbAddr The address size.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens switch (cbAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens case 4: /* IPv4 */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return pAddr1->au32[0] == pAddr2->au32[0];
fa9e4066f08beec538e775443c5be79dd423fcabahrens case 16: /* IPv6 */
99653d4ee642c6528e88224f12409a5f23060994eschrock return pAddr1->au64[0] == pAddr2->au64[0]
99653d4ee642c6528e88224f12409a5f23060994eschrock && pAddr1->au64[1] == pAddr2->au64[1];
fa9e4066f08beec538e775443c5be79dd423fcabahrens case 10: /* IPX */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return pAddr1->au64[0] == pAddr2->au64[0]
99653d4ee642c6528e88224f12409a5f23060994eschrock && pAddr1->au16[4] == pAddr2->au16[4];
99653d4ee642c6528e88224f12409a5f23060994eschrock default:
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertFailedReturn(false);
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
5ad820458efd0fdb914baff9c1447c22b819fa23nd}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Worker for intnetR0IfAddrCacheLookup that performs the lookup
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks * in the remaining cache entries after the caller has check the
99653d4ee642c6528e88224f12409a5f23060994eschrock * most likely ones.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns -1 if not found, the index of the cache entry if found.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pCache The cache.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pAddr The address.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param cbAddr The address size (optimization).
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrockstatic int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
fa9e4066f08beec538e775443c5be79dd423fcabahrens unsigned i = pCache->cEntries - 2;
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (i >= 1)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
fa9e4066f08beec538e775443c5be79dd423fcabahrens return i;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pbEntry -= pCache->cbEntry;
fa9e4066f08beec538e775443c5be79dd423fcabahrens i--;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return -1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Lookup an address in a cache without any expectations.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns -1 if not found, the index of the cache entry if found.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pCache The cache.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pAddr The address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param cbAddr The address size (optimization).
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock{
99653d4ee642c6528e88224f12409a5f23060994eschrock Assert(pCache->cbAddress == cbAddr);
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * The optimized case is when there is one cache entry and
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * it doesn't match.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrock unsigned i = pCache->cEntries;
99653d4ee642c6528e88224f12409a5f23060994eschrock if ( i > 0
99653d4ee642c6528e88224f12409a5f23060994eschrock && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return 0;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan if (i <= 1)
99653d4ee642c6528e88224f12409a5f23060994eschrock return -1;
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Check the last entry.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrock i--;
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return i;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan if (i <= 1)
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return -1;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan}
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
990b4856d0eaada6f8140335733a1b1771ed2746llingDECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
fa9e4066f08beec538e775443c5be79dd423fcabahrens /** @todo implement this. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
fa9e4066f08beec538e775443c5be79dd423fcabahrens * the lookup in the remaining cache entries after the caller
fa9e4066f08beec538e775443c5be79dd423fcabahrens * has check the most likely ones.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The routine is expecting not to find the address.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
99653d4ee642c6528e88224f12409a5f23060994eschrock * @returns -1 if not found, the index of the cache entry if found.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pCache The cache.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pAddr The address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param cbAddr The address size (optimization).
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Perform a full table lookup.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens unsigned i = pCache->cEntries - 2;
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (i >= 1)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
fa9e4066f08beec538e775443c5be79dd423fcabahrens return i;
99653d4ee642c6528e88224f12409a5f23060994eschrock pbEntry -= pCache->cbEntry;
99653d4ee642c6528e88224f12409a5f23060994eschrock i--;
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock return -1;
99653d4ee642c6528e88224f12409a5f23060994eschrock}
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin * Lookup an address in a cache expecting not to find it.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns -1 if not found, the index of the cache entry if found.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pCache The cache.
b1b8ab34de515a5e83206da22c3d7e563241b021lling * @param pAddr The address.
b1b8ab34de515a5e83206da22c3d7e563241b021lling * @param cbAddr The address size (optimization).
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin */
8654d0253136055bd4cc2423d87378e8a37f2eb5perrinDECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
b1b8ab34de515a5e83206da22c3d7e563241b021lling{
b1b8ab34de515a5e83206da22c3d7e563241b021lling Assert(pCache->cbAddress == cbAddr);
b1b8ab34de515a5e83206da22c3d7e563241b021lling
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan /*
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * The optimized case is when there is one cache entry and
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * it doesn't match.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan unsigned i = pCache->cEntries;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan if (RT_UNLIKELY( i > 0
fa9e4066f08beec538e775443c5be79dd423fcabahrens && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
99653d4ee642c6528e88224f12409a5f23060994eschrock return 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (RT_LIKELY(i <= 1))
fa9e4066f08beec538e775443c5be79dd423fcabahrens return -1;
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Then check the last entry and return if there are just two cache entries.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens i--;
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
fa9e4066f08beec538e775443c5be79dd423fcabahrens return i;
99653d4ee642c6528e88224f12409a5f23060994eschrock if (i <= 1)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return -1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Deletes a specific cache entry.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIf The interface (for logging).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pCache The cache.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param iEntry The entry to delete.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pszMsg Log message.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarksstatic void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling{
99653d4ee642c6528e88224f12409a5f23060994eschrock AssertReturnVoid(iEntry < pCache->cEntries);
99653d4ee642c6528e88224f12409a5f23060994eschrock AssertReturnVoid(iEntry >= 0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens#ifdef LOG_ENABLED
fa9e4066f08beec538e775443c5be79dd423fcabahrens INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
fa9e4066f08beec538e775443c5be79dd423fcabahrens PCRTNETADDRU pAddr = (PCRTNETADDRU)(pCache->pbEntries + iEntry * pCache->cbEntry);
fa9e4066f08beec538e775443c5be79dd423fcabahrens switch (enmAddrType)
990b4856d0eaada6f8140335733a1b1771ed2746lling {
990b4856d0eaada6f8140335733a1b1771ed2746lling case kIntNetAddrType_IPv4:
990b4856d0eaada6f8140335733a1b1771ed2746lling Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
990b4856d0eaada6f8140335733a1b1771ed2746lling pIf->hIf, &pIf->MacAddr, iEntry, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
990b4856d0eaada6f8140335733a1b1771ed2746lling break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens default:
fa9e4066f08beec538e775443c5be79dd423fcabahrens Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
99653d4ee642c6528e88224f12409a5f23060994eschrock pIf->hIf, &pIf->MacAddr, enmAddrType, iEntry, pCache->cbAddress, pAddr, pszMsg));
990b4856d0eaada6f8140335733a1b1771ed2746lling break;
990b4856d0eaada6f8140335733a1b1771ed2746lling }
990b4856d0eaada6f8140335733a1b1771ed2746lling#endif
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling pCache->cEntries--;
990b4856d0eaada6f8140335733a1b1771ed2746lling if (iEntry < pCache->cEntries)
990b4856d0eaada6f8140335733a1b1771ed2746lling memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
990b4856d0eaada6f8140335733a1b1771ed2746lling pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
990b4856d0eaada6f8140335733a1b1771ed2746lling (pCache->cEntries - iEntry) * pCache->cbEntry);
990b4856d0eaada6f8140335733a1b1771ed2746lling}
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Deletes an address from the cache, assuming it isn't actually in the cache.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * May or may not own the spinlock when calling this.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pIf The interface (for logging).
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pCache The cache.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pAddr The address.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param cbAddr The address size (optimization).
c5904d138f3bdf0762dbf452a43d5a5c387ea6a8eschrock */
990b4856d0eaada6f8140335733a1b1771ed2746llingDECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
990b4856d0eaada6f8140335733a1b1771ed2746lling{
990b4856d0eaada6f8140335733a1b1771ed2746lling int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
990b4856d0eaada6f8140335733a1b1771ed2746lling if (RT_UNLIKELY(i >= 0))
990b4856d0eaada6f8140335733a1b1771ed2746lling intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
990b4856d0eaada6f8140335733a1b1771ed2746lling}
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
990b4856d0eaada6f8140335733a1b1771ed2746lling * Deletes the address from all the interface caches.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is used to remove stale entries that has been reassigned to
990b4856d0eaada6f8140335733a1b1771ed2746lling * other machines on the network.
c5904d138f3bdf0762dbf452a43d5a5c387ea6a8eschrock *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pNetwork The network.
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock * @param pAddr The address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param enmType The address type.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param cbAddr The address size (optimization).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pszMsg Log message.
990b4856d0eaada6f8140335733a1b1771ed2746lling */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint8_t const cbAddr, const char *pszMsg)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockAcquire(pNetwork->hAddrSpinlock);
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling uint32_t iIf = pNetwork->MacTab.cEntries;
990b4856d0eaada6f8140335733a1b1771ed2746lling while (iIf--)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
99653d4ee642c6528e88224f12409a5f23060994eschrock PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
99653d4ee642c6528e88224f12409a5f23060994eschrock if (RT_UNLIKELY(i >= 0))
99653d4ee642c6528e88224f12409a5f23060994eschrock intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
990b4856d0eaada6f8140335733a1b1771ed2746lling
990b4856d0eaada6f8140335733a1b1771ed2746lling/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Deletes the address from all the interface caches except the specified one.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
990b4856d0eaada6f8140335733a1b1771ed2746lling * This is used to remove stale entries that has been reassigned to
fa9e4066f08beec538e775443c5be79dd423fcabahrens * other machines on the network.
990b4856d0eaada6f8140335733a1b1771ed2746lling *
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling * @param pNetwork The network.
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pAddr The address.
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling * @param enmType The address type.
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling * @param cbAddr The address size (optimization).
990b4856d0eaada6f8140335733a1b1771ed2746lling * @param pszMsg Log message.
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling */
990b4856d0eaada6f8140335733a1b1771ed2746llingDECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
990b4856d0eaada6f8140335733a1b1771ed2746lling INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
990b4856d0eaada6f8140335733a1b1771ed2746lling{
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockAcquire(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint32_t iIf = pNetwork->MacTab.cEntries;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (iIf--)
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling {
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
99653d4ee642c6528e88224f12409a5f23060994eschrock if (pIf != pIfSender)
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling {
fa9e4066f08beec538e775443c5be79dd423fcabahrens int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
c5904d138f3bdf0762dbf452a43d5a5c387ea6a8eschrock if (RT_UNLIKELY(i >= 0))
fa9e4066f08beec538e775443c5be79dd423fcabahrens intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Lookup an address on the network, returning the (first) interface having it
fa9e4066f08beec538e775443c5be79dd423fcabahrens * in its address cache.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns Pointer to the interface on success, NULL if not found. The caller
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * must release the interface by calling intnetR0BusyDecIf.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pNetwork The network.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pAddr The address to lookup.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param enmType The address type.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param cbAddr The size of the address.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrockDECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
b5989ec7cd031c9bb066c68877fb5017e45f205beschrock{
b5989ec7cd031c9bb066c68877fb5017e45f205beschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
b5989ec7cd031c9bb066c68877fb5017e45f205beschrock
b5989ec7cd031c9bb066c68877fb5017e45f205beschrock uint32_t iIf = pNetwork->MacTab.cEntries;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (iIf--)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
fa9e4066f08beec538e775443c5be79dd423fcabahrens int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (i >= 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens intnetR0BusyIncIf(pIf);
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return pIf;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
94de1d4cf6ec0a3bf040dcc4b8df107c4ed36b51eschrock return NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Adds an address to the cache, the caller is responsible for making sure it's
fa9e4066f08beec538e775443c5be79dd423fcabahrens * not already in the cache.
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock *
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling * The caller must not
351420b34707afeafa8d5c3e0c77b7bcffb1edc0lling *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIf The interface (for logging).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pCache The address cache.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pAddr The address.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pszMsg log message.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, const char *pszMsg)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETNETWORK pNetwork = pIf->pNetwork;
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertReturnVoid(pNetwork);
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockAcquire(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock if (RT_UNLIKELY(!pCache->cEntriesAlloc))
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* This shouldn't happen*/
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return;
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* When the table is full, drop the older entry (FIFO). Do proper ageing? */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pCache->cEntries >= pCache->cEntriesAlloc)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
99653d4ee642c6528e88224f12409a5f23060994eschrock (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
99653d4ee642c6528e88224f12409a5f23060994eschrock memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
99653d4ee642c6528e88224f12409a5f23060994eschrock pCache->cEntries--;
99653d4ee642c6528e88224f12409a5f23060994eschrock Assert(pCache->cEntries < pCache->cEntriesAlloc);
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
a43d325b828008a3ab54eed57fd7c00b6470172bek /*
a43d325b828008a3ab54eed57fd7c00b6470172bek * Add the new entry to the end of the array.
a43d325b828008a3ab54eed57fd7c00b6470172bek */
a43d325b828008a3ab54eed57fd7c00b6470172bek uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
99653d4ee642c6528e88224f12409a5f23060994eschrock memcpy(pbEntry, pAddr, pCache->cbAddress);
99653d4ee642c6528e88224f12409a5f23060994eschrock memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan#ifdef LOG_ENABLED
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock switch (enmAddrType)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock case kIntNetAddrType_IPv4:
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pIf->hIf, &pIf->MacAddr, pCache->cEntries, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
99653d4ee642c6528e88224f12409a5f23060994eschrock break;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock default:
99653d4ee642c6528e88224f12409a5f23060994eschrock Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs type=%d added #%d %.*Rhxs %s\n",
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pIf->hIf, &pIf->MacAddr, enmAddrType, pCache->cEntries, pCache->cbAddress, pAddr, pszMsg));
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock break;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock#endif
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pCache->cEntries++;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock Assert(pCache->cEntries <= pCache->cEntriesAlloc);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock}
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock/**
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock *
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pIf The interface (for logging).
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pCache The address cache.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pAddr The address.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param cbAddr The size of the address (optimization).
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pszMsg Log message.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrockstatic void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock{
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Check all but the first and last entries, the caller
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * has already checked those.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock int i = pCache->cEntries - 2;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
99653d4ee642c6528e88224f12409a5f23060994eschrock while (i >= 1)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
99653d4ee642c6528e88224f12409a5f23060994eschrock return;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan pbEntry += pCache->cbEntry;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock i--;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Not found, add it.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan intnetR0IfAddrCacheAddIt(pIf, pCache, pAddr, pszMsg);
a43d325b828008a3ab54eed57fd7c00b6470172bek}
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Adds an address to the cache if it's not already there.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * Must not own any spinlocks when calling this function.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan *
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param pIf The interface (for logging).
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param pCache The address cache.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param pAddr The address.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param cbAddr The size of the address (optimization).
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param pszMsg Log message.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendanDECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr,
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan uint8_t const cbAddr, const char *pszMsg)
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan{
99653d4ee642c6528e88224f12409a5f23060994eschrock Assert(pCache->cbAddress == cbAddr);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * The optimized case is when the address the first or last cache entry.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan unsigned i = pCache->cEntries;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (RT_LIKELY( i > 0
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock && ( intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock || (i > 1
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock && intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))) ))
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock intnetR0IfAddrCacheAddSlow(pIf, pCache, pAddr, cbAddr, pszMsg);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock}
0917b783fd655a0c943e0b8fb848db2301774947eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock/**
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Destroys the specified address cache.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pCache The address cache.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrockstatic void intnetR0IfAddrCacheDestroy(PINTNETADDRCACHE pCache)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock{
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock void *pvFree = pCache->pbEntries;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pCache->pbEntries = NULL;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pCache->cEntries = 0;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pCache->cEntriesAlloc = 0;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock RTMemFree(pvFree);
a43d325b828008a3ab54eed57fd7c00b6470172bek}
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan/**
a43d325b828008a3ab54eed57fd7c00b6470172bek * Initialize the address cache for the specified address type.
a43d325b828008a3ab54eed57fd7c00b6470172bek *
a43d325b828008a3ab54eed57fd7c00b6470172bek * The cache storage is preallocated and fixed size so that we can handle
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * inserts from problematic contexts.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan *
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @returns VINF_SUCCESS or VERR_NO_MEMORY.
a43d325b828008a3ab54eed57fd7c00b6470172bek * @param pCache The cache to initialize.
a43d325b828008a3ab54eed57fd7c00b6470172bek * @param enmAddrType The address type.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param fEnabled Whether the address cache is enabled or not.
a43d325b828008a3ab54eed57fd7c00b6470172bek */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendanstatic int intnetR0IfAddrCacheInit(PINTNETADDRCACHE pCache, INTNETADDRTYPE enmAddrType, bool fEnabled)
a43d325b828008a3ab54eed57fd7c00b6470172bek{
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan pCache->cEntries = 0;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan pCache->cbAddress = intnetR0AddrSize(enmAddrType);
a43d325b828008a3ab54eed57fd7c00b6470172bek pCache->cbEntry = RT_ALIGN(pCache->cbAddress, 4);
a43d325b828008a3ab54eed57fd7c00b6470172bek if (fEnabled)
a43d325b828008a3ab54eed57fd7c00b6470172bek {
a43d325b828008a3ab54eed57fd7c00b6470172bek pCache->cEntriesAlloc = 32;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cEntriesAlloc * pCache->cbEntry);
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan if (!pCache->pbEntries)
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan return VERR_NO_MEMORY;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan }
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan else
a43d325b828008a3ab54eed57fd7c00b6470172bek {
a43d325b828008a3ab54eed57fd7c00b6470172bek pCache->cEntriesAlloc = 0;
a43d325b828008a3ab54eed57fd7c00b6470172bek pCache->pbEntries = NULL;
a43d325b828008a3ab54eed57fd7c00b6470172bek }
a43d325b828008a3ab54eed57fd7c00b6470172bek return VINF_SUCCESS;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock}
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock/**
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * Is it a multicast or broadcast MAC address?
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns true if multicast, false if not.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * @param pMacAddr The address to inspect.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECL_FORCE_INLINE(bool) intnetR0IsMacAddrMulticast(PCRTMAC pMacAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens return !!(pMacAddr->au8[0] & 0x01);
99653d4ee642c6528e88224f12409a5f23060994eschrock}
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
99653d4ee642c6528e88224f12409a5f23060994eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Is it a dummy MAC address?
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock *
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * We use dummy MAC addresses for interfaces which we don't know the MAC
fa9e4066f08beec538e775443c5be79dd423fcabahrens * address of because they haven't sent anything (learning) or explicitly set
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * it.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns true if dummy, false if not.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pMacAddr The address to inspect.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendanDECL_FORCE_INLINE(bool) intnetR0IsMacAddrDummy(PCRTMAC pMacAddr)
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan{
a43d325b828008a3ab54eed57fd7c00b6470172bek /* The dummy address are broadcast addresses, don't bother check it all. */
a43d325b828008a3ab54eed57fd7c00b6470172bek return pMacAddr->au16[0] == 0xffff;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock}
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks/**
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * Compares two MAC addresses.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock *
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * @returns true if equal, false if not.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * @param pDstAddr1 Address 1.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pDstAddr2 Address 2.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensDECL_FORCE_INLINE(bool) intnetR0AreMacAddrsEqual(PCRTMAC pDstAddr1, PCRTMAC pDstAddr2)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens return pDstAddr1->au16[2] == pDstAddr2->au16[2]
fa9e4066f08beec538e775443c5be79dd423fcabahrens && pDstAddr1->au16[1] == pDstAddr2->au16[1]
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock && pDstAddr1->au16[0] == pDstAddr2->au16[0];
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * Switch a unicast frame based on the network layer address (OSI level 3) and
99653d4ee642c6528e88224f12409a5f23060994eschrock * return a destination table.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pNetwork The network to switch on.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pDstMacAddr The destination MAC address.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @param enmL3AddrType The level-3 destination address type.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pL3Addr The level-3 destination address.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param cbL3Addr The size of the level-3 destination address.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param fSrc The frame source (INTNETTRUNKDIR_WIRE).
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pDstTab The destination output table.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendanstatic INTNETSWDECISION intnetR0NetworkSwitchLevel3(PINTNETNETWORK pNetwork, PCRTMAC pDstMacAddr,
a43d325b828008a3ab54eed57fd7c00b6470172bek INTNETADDRTYPE enmL3AddrType, PCRTNETADDRU pL3Addr, uint8_t cbL3Addr,
a43d325b828008a3ab54eed57fd7c00b6470172bek uint32_t fSrc, PINTNETDSTTAB pDstTab)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock{
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock Assert(fSrc == INTNETTRUNKDIR_WIRE);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks /*
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * Grab the spinlock first and do the switching.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock */
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock PINTNETMACTAB pTab = &pNetwork->MacTab;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->fTrunkDst = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->pTrunk = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->cIfs = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock /* Find exactly matching or promiscuous interfaces. */
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock uint32_t cExactHits = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock uint32_t iIfMac = pTab->cEntries;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock while (iIfMac-- > 0)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (pTab->paEntries[iIfMac].fActive)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock bool fExact = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmL3AddrType], pL3Addr, cbL3Addr) >= 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (fExact || pTab->paEntries[iIfMac].fPromiscuousSeeTrunk)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock cExactHits += fExact;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock uint32_t iIfDst = pDstTab->cIfs++;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->aIfs[iIfDst].pIf = pIf;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->aIfs[iIfDst].fReplaceDstMac = fExact;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock intnetR0BusyIncIf(pIf);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
441d80aa4f613b6298fc8bd3151f4be02dbf84fclling if (fExact)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstMacAddr = &pIf->MacAddr; /* Avoids duplicates being sent to the host. */
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Network only promicuous mode ifs should see related trunk traffic. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ( cExactHits
fa9e4066f08beec538e775443c5be79dd423fcabahrens && fSrc
99653d4ee642c6528e88224f12409a5f23060994eschrock && pNetwork->MacTab.cPromiscuousNoTrunkEntries)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens iIfMac = pTab->cEntries;
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (iIfMac-- > 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
99653d4ee642c6528e88224f12409a5f23060994eschrock if ( pTab->paEntries[iIfMac].fActive
fa9e4066f08beec538e775443c5be79dd423fcabahrens && pTab->paEntries[iIfMac].fPromiscuousEff
99653d4ee642c6528e88224f12409a5f23060994eschrock && !pTab->paEntries[iIfMac].fPromiscuousSeeTrunk)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmL3AddrType], pL3Addr, cbL3Addr) < 0)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock uint32_t iIfDst = pDstTab->cIfs++;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->aIfs[iIfDst].pIf = pIf;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock intnetR0BusyIncIf(pIf);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock /* Does it match the host, or is the host promiscuous? */
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (pTab->fHostActive)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstMacAddr);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if ( fExact
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock || intnetR0IsMacAddrDummy(&pTab->HostMac)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock || pTab->fHostPromiscuousEff)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock cExactHits += fExact;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
99653d4ee642c6528e88224f12409a5f23060994eschrock if (pTab->fWireActive && (!cExactHits || pTab->fWirePromiscuousEff))
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst &= ~fSrc;
99653d4ee642c6528e88224f12409a5f23060994eschrock if (pDstTab->fTrunkDst)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock PINTNETTRUNKIF pTrunk = pTab->pTrunk;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->pTrunk = pTrunk;
99653d4ee642c6528e88224f12409a5f23060994eschrock intnetR0BusyIncTrunk(pTrunk);
99653d4ee642c6528e88224f12409a5f23060994eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock return pDstTab->cIfs
99653d4ee642c6528e88224f12409a5f23060994eschrock ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
99653d4ee642c6528e88224f12409a5f23060994eschrock : (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
99653d4ee642c6528e88224f12409a5f23060994eschrock}
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Pre-switch a unicast MAC address.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
99653d4ee642c6528e88224f12409a5f23060994eschrock * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
99653d4ee642c6528e88224f12409a5f23060994eschrock * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pNetwork The network to switch on.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param fSrc The frame source.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pSrcAddr The source address of the frame.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pDstAddr The destination address of the frame.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic INTNETSWDECISION intnetR0NetworkPreSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PCRTMAC pSrcAddr,
fa9e4066f08beec538e775443c5be79dd423fcabahrens PCRTMAC pDstAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens Assert(!intnetR0IsMacAddrMulticast(pDstAddr));
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin Assert(fSrc);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Grab the spinlock first and do the switching.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens INTNETSWDECISION enmSwDecision = INTNETSWDECISION_BROADCAST;
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETMACTAB pTab = &pNetwork->MacTab;
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockAcquire(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Iterate the internal network interfaces and look for matching source and
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan destination addresses. */
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin uint32_t iIfMac = pTab->cEntries;
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock while (iIfMac-- > 0)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock if (pTab->paEntries[iIfMac].fActive)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Unknown interface address? */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr))
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock break;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /* Promiscuous mode? */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (pTab->paEntries[iIfMac].fPromiscuousSeeTrunk)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock break;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /* Paranoia - this shouldn't happen, right? */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ( pSrcAddr
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan && intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pSrcAddr))
99653d4ee642c6528e88224f12409a5f23060994eschrock break;
99653d4ee642c6528e88224f12409a5f23060994eschrock
a43d325b828008a3ab54eed57fd7c00b6470172bek /* Exact match? */
99653d4ee642c6528e88224f12409a5f23060994eschrock if (intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr))
99653d4ee642c6528e88224f12409a5f23060994eschrock {
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan enmSwDecision = pTab->fHostPromiscuousEff && fSrc == INTNETTRUNKDIR_WIRE
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan ? INTNETSWDECISION_BROADCAST
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan : INTNETSWDECISION_INTNET;
99653d4ee642c6528e88224f12409a5f23060994eschrock break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
99653d4ee642c6528e88224f12409a5f23060994eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock return enmSwDecision;
99653d4ee642c6528e88224f12409a5f23060994eschrock}
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Switch a unicast MAC address and return a destination table.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock * @param pNetwork The network to switch on.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param fSrc The frame source.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pIfSender The sender interface, NULL if trunk. Used to
99653d4ee642c6528e88224f12409a5f23060994eschrock * prevent sending an echo to the sender.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pDstAddr The destination address of the frame.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pDstTab The destination output table.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
0430f8daa551890e0788d3fd28aef3be44cf8730eschrockstatic INTNETSWDECISION intnetR0NetworkSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
a43d325b828008a3ab54eed57fd7c00b6470172bek PCRTMAC pDstAddr, PINTNETDSTTAB pDstTab)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
99653d4ee642c6528e88224f12409a5f23060994eschrock AssertPtr(pDstTab);
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock Assert(!intnetR0IsMacAddrMulticast(pDstAddr));
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Grab the spinlock first and do the switching.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrock PINTNETMACTAB pTab = &pNetwork->MacTab;
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst = 0;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->pTrunk = 0;
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock pDstTab->cIfs = 0;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Find exactly matching or promiscuous interfaces. */
99653d4ee642c6528e88224f12409a5f23060994eschrock uint32_t cExactHits = 0;
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock uint32_t iIfMac = pTab->cEntries;
99653d4ee642c6528e88224f12409a5f23060994eschrock while (iIfMac-- > 0)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock if (pTab->paEntries[iIfMac].fActive)
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock {
0430f8daa551890e0788d3fd28aef3be44cf8730eschrock bool fExact = intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr);
990b4856d0eaada6f8140335733a1b1771ed2746lling if ( fExact
99653d4ee642c6528e88224f12409a5f23060994eschrock || intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens || ( pTab->paEntries[iIfMac].fPromiscuousSeeTrunk
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks || (!fSrc && pTab->paEntries[iIfMac].fPromiscuousEff) )
fa9e4066f08beec538e775443c5be79dd423fcabahrens )
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens cExactHits += fExact;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (RT_LIKELY(pIf != pIfSender)) /* paranoia */
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint32_t iIfDst = pDstTab->cIfs++;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->aIfs[iIfDst].pIf = pIf;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
fa9e4066f08beec538e775443c5be79dd423fcabahrens intnetR0BusyIncIf(pIf);
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin }
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin }
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin }
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin }
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin /* Network only promicuous mode ifs should see related trunk traffic. */
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin if ( cExactHits
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin && fSrc
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin && pNetwork->MacTab.cPromiscuousNoTrunkEntries)
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin {
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin iIfMac = pTab->cEntries;
99653d4ee642c6528e88224f12409a5f23060994eschrock while (iIfMac-- > 0)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock if ( pTab->paEntries[iIfMac].fPromiscuousEff
8654d0253136055bd4cc2423d87378e8a37f2eb5perrin && !pTab->paEntries[iIfMac].fPromiscuousSeeTrunk
99653d4ee642c6528e88224f12409a5f23060994eschrock && pTab->paEntries[iIfMac].fActive
fa9e4066f08beec538e775443c5be79dd423fcabahrens && !intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens && !intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr) )
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint32_t iIfDst = pDstTab->cIfs++;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->aIfs[iIfDst].pIf = pIf;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
99653d4ee642c6528e88224f12409a5f23060994eschrock intnetR0BusyIncIf(pIf);
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Does it match the host, or is the host promiscuous? */
99653d4ee642c6528e88224f12409a5f23060994eschrock if ( fSrc != INTNETTRUNKDIR_HOST
99653d4ee642c6528e88224f12409a5f23060994eschrock && pTab->fHostActive)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstAddr);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if ( fExact
fa9e4066f08beec538e775443c5be79dd423fcabahrens || intnetR0IsMacAddrDummy(&pTab->HostMac)
fa9e4066f08beec538e775443c5be79dd423fcabahrens || pTab->fHostPromiscuousEff)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
99653d4ee642c6528e88224f12409a5f23060994eschrock cExactHits += fExact;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ( fSrc != INTNETTRUNKDIR_WIRE
fa9e4066f08beec538e775443c5be79dd423fcabahrens && pTab->fWireActive
fa9e4066f08beec538e775443c5be79dd423fcabahrens && (!cExactHits || pTab->fWirePromiscuousEff)
99653d4ee642c6528e88224f12409a5f23060994eschrock )
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
99653d4ee642c6528e88224f12409a5f23060994eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Grab the trunk if we're sending to it. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pDstTab->fTrunkDst)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETTRUNKIF pTrunk = pTab->pTrunk;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->pTrunk = pTrunk;
fa9e4066f08beec538e775443c5be79dd423fcabahrens intnetR0BusyIncTrunk(pTrunk);
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return pDstTab->cIfs
99653d4ee642c6528e88224f12409a5f23060994eschrock ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
fa9e4066f08beec538e775443c5be79dd423fcabahrens : (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
99653d4ee642c6528e88224f12409a5f23060994eschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Create a destination table for a broadcast frame.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns INTNETSWDECISION_BROADCAST.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pNetwork The network to switch on.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param fSrc The frame source.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pIfSender The sender interface, NULL if trunk. Used to
fa9e4066f08beec538e775443c5be79dd423fcabahrens * prevent sending an echo to the sender.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pDstTab The destination output table.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendanstatic INTNETSWDECISION intnetR0NetworkSwitchBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
99653d4ee642c6528e88224f12409a5f23060994eschrock PINTNETDSTTAB pDstTab)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock AssertPtr(pDstTab);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Grab the spinlock first and record all active interfaces.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan */
99653d4ee642c6528e88224f12409a5f23060994eschrock PINTNETMACTAB pTab = &pNetwork->MacTab;
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockAcquire(pNetwork->hAddrSpinlock);
a43d325b828008a3ab54eed57fd7c00b6470172bek
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst = 0;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->pTrunk = 0;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan pDstTab->cIfs = 0;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan /* Regular interfaces. */
99653d4ee642c6528e88224f12409a5f23060994eschrock uint32_t iIfMac = pTab->cEntries;
99653d4ee642c6528e88224f12409a5f23060994eschrock while (iIfMac-- > 0)
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pTab->paEntries[iIfMac].fActive)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pIf != pIfSender)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
fa9e4066f08beec538e775443c5be79dd423fcabahrens uint32_t iIfDst = pDstTab->cIfs++;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->aIfs[iIfDst].pIf = pIf;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
99653d4ee642c6528e88224f12409a5f23060994eschrock intnetR0BusyIncIf(pIf);
99653d4ee642c6528e88224f12409a5f23060994eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /* The trunk interface. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pTab->fHostActive)
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pTab->fWireActive)
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pDstTab->fTrunkDst &= ~fSrc;
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (pDstTab->fTrunkDst)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock PINTNETTRUNKIF pTrunk = pTab->pTrunk;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->pTrunk = pTrunk;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock intnetR0BusyIncTrunk(pTrunk);
99653d4ee642c6528e88224f12409a5f23060994eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock return INTNETSWDECISION_BROADCAST;
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan}
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Create a destination table with the trunk and any promiscuous interfaces.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
99653d4ee642c6528e88224f12409a5f23060994eschrock * This is only used in a fallback case of the level-3 switching, so we can
99653d4ee642c6528e88224f12409a5f23060994eschrock * assume the wire as source and skip the sender interface filtering.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
99653d4ee642c6528e88224f12409a5f23060994eschrock * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pNetwork The network to switch on.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param fSrc The frame source.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pDstTab The destination output table.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrockstatic INTNETSWDECISION intnetR0NetworkSwitchTrunkAndPromisc(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan{
99653d4ee642c6528e88224f12409a5f23060994eschrock Assert(fSrc == INTNETTRUNKDIR_WIRE);
99653d4ee642c6528e88224f12409a5f23060994eschrock
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Grab the spinlock first and do the switching.
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan */
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan PINTNETMACTAB pTab = &pNetwork->MacTab;
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->fTrunkDst = 0;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->pTrunk = 0;
99653d4ee642c6528e88224f12409a5f23060994eschrock pDstTab->cIfs = 0;
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks
99653d4ee642c6528e88224f12409a5f23060994eschrock /* Find promiscuous interfaces. */
99653d4ee642c6528e88224f12409a5f23060994eschrock uint32_t iIfMac = pTab->cEntries;
99653d4ee642c6528e88224f12409a5f23060994eschrock while (iIfMac-- > 0)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if ( pTab->paEntries[iIfMac].fActive
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock && ( pTab->paEntries[iIfMac].fPromiscuousSeeTrunk
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock || (!fSrc && pTab->paEntries[iIfMac].fPromiscuousEff) )
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock )
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint32_t iIfDst = pDstTab->cIfs++;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->aIfs[iIfDst].pIf = pIf;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
99653d4ee642c6528e88224f12409a5f23060994eschrock intnetR0BusyIncIf(pIf);
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan }
99653d4ee642c6528e88224f12409a5f23060994eschrock }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /* The trunk interface. */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (pTab->fHostActive)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock if (pTab->fWireActive)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->fTrunkDst &= ~fSrc;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (pDstTab->fTrunkDst)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock PINTNETTRUNKIF pTrunk = pTab->pTrunk;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pDstTab->pTrunk = pTrunk;
99653d4ee642c6528e88224f12409a5f23060994eschrock intnetR0BusyIncTrunk(pTrunk);
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan }
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
99653d4ee642c6528e88224f12409a5f23060994eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return !pDstTab->cIfs
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK)
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan : (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST);
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan}
fa94a07fd0519b8abfd871ad8fe60e6bebe1e2bbbrendan
a43d325b828008a3ab54eed57fd7c00b6470172bek
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Create a destination table for a trunk frame.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
99653d4ee642c6528e88224f12409a5f23060994eschrock * @returns INTNETSWDECISION_BROADCAST.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param pNetwork The network to switch on.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param fSrc The frame source.
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks * @param pDstTab The destination output table.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
99653d4ee642c6528e88224f12409a5f23060994eschrockstatic INTNETSWDECISION intnetR0NetworkSwitchTrunk(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
99653d4ee642c6528e88224f12409a5f23060994eschrock{
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertPtr(pDstTab);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock /*
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * Grab the spinlock first and record all active interfaces.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock */
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock PINTNETMACTAB pTab= &pNetwork->MacTab;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->fTrunkDst = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->pTrunk = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->cIfs = 0;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock /* The trunk interface. */
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (pTab->fHostActive)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (pTab->fWireActive)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->fTrunkDst &= ~fSrc;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if (pDstTab->fTrunkDst)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock {
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock PINTNETTRUNKIF pTrunk = pTab->pTrunk;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock pDstTab->pTrunk = pTrunk;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock intnetR0BusyIncTrunk(pTrunk);
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl return pDstTab->fTrunkDst ? INTNETSWDECISION_TRUNK : INTNETSWDECISION_DROP;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl}
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl/**
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * Wrapper around RTMemAlloc for allocating a destination table.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @returns VINF_SUCCESS or VERR_NO_MEMORY.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param cEntries The size given as an entry count.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param ppDstTab Where to store the pointer (always).
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl */
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahlDECLINLINE(int) intnetR0AllocDstTab(uint32_t cEntries, PINTNETDSTTAB *ppDstTab)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl{
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETDSTTAB pDstTab;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl *ppDstTab = pDstTab = (PINTNETDSTTAB)RTMemAlloc(RT_OFFSETOF(INTNETDSTTAB, aIfs[cEntries]));
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (RT_UNLIKELY(!pDstTab))
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl return VERR_NO_MEMORY;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl return VINF_SUCCESS;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl}
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl/**
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * Ensures that there is space for another interface in the MAC address lookup
fa9e4066f08beec538e775443c5be79dd423fcabahrens * table as well as all the destination tables.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * The caller must own the create/open/destroy mutex.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns VINF_SUCCESS, VERR_NO_MEMORY or VERR_OUT_OF_RANGE.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param pNetwork The network to operate on.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl */
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahlstatic int intnetR0NetworkEnsureTabSpace(PINTNETNETWORK pNetwork)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens /*
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * The cEntries and cEntriesAllocated members are only updated while
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * owning the big mutex, so we only need the spinlock when doing the
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * actual table replacing.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl */
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETMACTAB pTab = &pNetwork->MacTab;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl int rc = VINF_SUCCESS;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl AssertReturn(pTab->cEntries <= pTab->cEntriesAllocated, VERR_INTERNAL_ERROR_2);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (pTab->cEntries + 1 > pTab->cEntriesAllocated)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl uint32_t const cAllocated = pTab->cEntriesAllocated + INTNET_GROW_DSTTAB_SIZE;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (cAllocated <= INTNET_MAX_IFS)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl /*
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * Resize the destination tables first, this can be kind of tedious.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl */
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl for (uint32_t i = 0; i < pTab->cEntries; i++)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETIF pIf = pTab->paEntries[i].pIf; AssertPtr(pIf);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETDSTTAB pNew;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl rc = intnetR0AllocDstTab(cAllocated, &pNew);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (RT_FAILURE(rc))
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl break;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl for (;;)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETDSTTAB pOld = pIf->pDstTab;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if ( pOld
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl && ASMAtomicCmpXchgPtr(&pIf->pDstTab, pNew, pOld))
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl RTMemFree(pOld);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl break;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl intnetR0BusyWait(pNetwork, &pIf->cBusy);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl /*
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * The trunk.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl */
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if ( RT_SUCCESS(rc)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl && pNetwork->MacTab.pTrunk)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl AssertCompileAdjacentMembers(INTNETTRUNKIF, apTaskDstTabs, apIntDstTabs);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETDSTTAB * const ppEndDstTab = &pTrunk->apIntDstTabs[pTrunk->cIntDstTabs];
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl for (PINTNETDSTTAB *ppDstTab = &pTrunk->apTaskDstTabs[0];
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl ppDstTab != ppEndDstTab && RT_SUCCESS(rc);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl ppDstTab++)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETDSTTAB pNew;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl rc = intnetR0AllocDstTab(cAllocated, &pNew);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (RT_FAILURE(rc))
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl break;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl for (;;)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl RTSpinlockAcquire(pTrunk->hDstTabSpinlock);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl void *pvOld = *ppDstTab;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (pvOld)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl *ppDstTab = pNew;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl RTSpinlockReleaseNoInts(pTrunk->hDstTabSpinlock);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (pvOld)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl RTMemFree(pvOld);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl break;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl }
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl /*
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * The MAC Address table itself.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl */
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl if (RT_SUCCESS(rc))
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl PINTNETMACTABENTRY paNew = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * cAllocated);
3aefe2c713c6309989772b3ff922a68037186a75ahrens if (paNew)
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl {
3aefe2c713c6309989772b3ff922a68037186a75ahrens RTSpinlockAcquire(pNetwork->hAddrSpinlock);
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl
3aefe2c713c6309989772b3ff922a68037186a75ahrens PINTNETMACTABENTRY paOld = pTab->paEntries;
3aefe2c713c6309989772b3ff922a68037186a75ahrens uint32_t i = pTab->cEntries;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl while (i-- > 0)
3aefe2c713c6309989772b3ff922a68037186a75ahrens {
3aefe2c713c6309989772b3ff922a68037186a75ahrens paNew[i] = paOld[i];
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens paOld[i].fActive = false;
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl paOld[i].pIf = NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens pTab->paEntries = paNew;
fa9e4066f08beec538e775443c5be79dd423fcabahrens pTab->cEntriesAllocated = cAllocated;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens RTMemFree(paOld);
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens else
fa9e4066f08beec538e775443c5be79dd423fcabahrens rc = VERR_NO_MEMORY;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens else
fa9e4066f08beec538e775443c5be79dd423fcabahrens rc = VERR_OUT_OF_RANGE;
99653d4ee642c6528e88224f12409a5f23060994eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock return rc;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
3aefe2c713c6309989772b3ff922a68037186a75ahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#ifdef INTNET_WITH_DHCP_SNOOPING
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * Snoops IP assignments and releases from the DHCPv4 traffic.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl *
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * The caller is responsible for making sure this traffic between the
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * BOOTPS and BOOTPC ports and validate the IP header. The UDP packet
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * need not be validated beyond the ports.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl *
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param pNetwork The network this frame was seen on.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param pIpHdr Pointer to a valid IP header. This is for pseudo
fa9e4066f08beec538e775443c5be79dd423fcabahrens * header validation, so only the minimum header size
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * needs to be available and valid here.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param pUdpHdr Pointer to the UDP header in the frame.
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl * @param cbUdpPkt What's left of the frame when starting at the UDP header.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param fGso Set if this is a GSO frame, clear if regular.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
f3861e1a2ceec23a5b699c24d814b7775a9e0b52ahl /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Check if the DHCP message is valid and get the type.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt, true /*fCheckSum*/))
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock Log6(("Bad UDP packet\n"));
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock return;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock uint8_t MsgType;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock Log6(("Bad DHCP packet\n"));
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock return;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock#ifdef LOG_ENABLED
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock /*
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Log it.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock const char *pszType = "unknown";
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock switch (MsgType)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_DISCOVER: pszType = "discover"; break;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_OFFER: pszType = "offer"; break;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_REQUEST: pszType = "request"; break;
99653d4ee642c6528e88224f12409a5f23060994eschrock case RTNET_DHCP_MT_DECLINE: pszType = "decline"; break;
99653d4ee642c6528e88224f12409a5f23060994eschrock case RTNET_DHCP_MT_ACK: pszType = "ack"; break;
99653d4ee642c6528e88224f12409a5f23060994eschrock case RTNET_DHCP_MT_NAC: pszType = "nac"; break;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_RELEASE: pszType = "release"; break;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_INFORM: pszType = "inform"; break;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock Log6(("DHCP msg: %d (%s) client %.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d\n", MsgType, pszType, &pDhcp->bp_chaddr,
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock pDhcp->bp_ciaddr.au8[0], pDhcp->bp_ciaddr.au8[1], pDhcp->bp_ciaddr.au8[2], pDhcp->bp_ciaddr.au8[3],
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock pDhcp->bp_yiaddr.au8[0], pDhcp->bp_yiaddr.au8[1], pDhcp->bp_yiaddr.au8[2], pDhcp->bp_yiaddr.au8[3]));
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock#endif /* LOG_EANBLED */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock /*
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Act upon the message.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock switch (MsgType)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock#if 0
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_REQUEST:
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock /** @todo Check for valid non-broadcast requests w/ IP for any of the MACs we
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * know, and add the IP to the cache. */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock break;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock#endif
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock /*
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Lookup the interface by its MAC address and insert the IPv4 address into the cache.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Delete the old client address first, just in case it changed in a renewal.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_ACK:
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (intnetR0IPv4AddrIsGood(pDhcp->bp_yiaddr))
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock PINTNETIF pMatchingIf = NULL;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock uint32_t iIf = pNetwork->MacTab.cEntries;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock while (iIf-- > 0)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if ( intnetR0IfHasMacAddr(pCur)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock && !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (!pMatchingIf)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock pMatchingIf = pCur;
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock intnetR0BusyIncIf(pMatchingIf);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (pMatchingIf)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock intnetR0IfAddrCacheAdd(pMatchingIf, &pMatchingIf->aAddrCache[kIntNetAddrType_IPv4],
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock intnetR0BusyDecIf(pMatchingIf);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock return;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock /*
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock case RTNET_DHCP_MT_RELEASE:
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock RTSpinlockAcquire(pNetwork->hAddrSpinlock);
99653d4ee642c6528e88224f12409a5f23060994eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock uint32_t iIf = pNetwork->MacTab.cEntries;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock while (iIf-- > 0)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock if ( intnetR0IfHasMacAddr(pCur)
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock && !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
5ad820458efd0fdb914baff9c1447c22b819fa23nd }
5ad820458efd0fdb914baff9c1447c22b819fa23nd }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock break;
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock }
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock}
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock/**
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * Worker for intnetR0TrunkIfSnoopAddr that takes care of what
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock * is likely to be a DHCP message.
3d7072f8bd27709dba14f6fe336f149d25d9e207eschrock *
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * The caller has already check that the UDP source and destination ports
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * are BOOTPS or BOOTPC.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock *
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * @param pNetwork The network this frame was seen on.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * @param pSG The gather list for the frame.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrockstatic void intnetR0TrunkIfSnoopDhcp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock{
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock /*
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Get a pointer to a linear copy of the full packet, using the
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * temporary buffer if necessary.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock uint32_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (pSG->cSegsUsed > 1)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
99653d4ee642c6528e88224f12409a5f23060994eschrock Log6(("intnetR0TrunkIfSnoopDhcp: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
99653d4ee642c6528e88224f12409a5f23060994eschrock if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
99653d4ee642c6528e88224f12409a5f23060994eschrock return;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Validate the IP header and find the UDP packet.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fChecksum*/))
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock {
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock Log(("intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock return;
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint32_t cbIpHdr = pIpHdr->ip_hl * 4;
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
99653d4ee642c6528e88224f12409a5f23060994eschrock * Hand it over to the common DHCP snooper.
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock intnetR0NetworkSnoopDhcp(pNetwork, pIpHdr, (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr), cbPacket - cbIpHdr);
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock}
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock#endif /* INTNET_WITH_DHCP_SNOOPING */
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock/**
99653d4ee642c6528e88224f12409a5f23060994eschrock * Snoops up source addresses from ARP requests and purge these from the address
99653d4ee642c6528e88224f12409a5f23060994eschrock * caches.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
99653d4ee642c6528e88224f12409a5f23060994eschrock * The purpose of this purging is to get rid of stale addresses.
99653d4ee642c6528e88224f12409a5f23060994eschrock *
5ad820458efd0fdb914baff9c1447c22b819fa23nd * @param pNetwork The network this frame was seen on.
99653d4ee642c6528e88224f12409a5f23060994eschrock * @param pSG The gather list for the frame.
99653d4ee642c6528e88224f12409a5f23060994eschrock */
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrockstatic void intnetR0TrunkIfSnoopArp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock{
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
c67d9675bbc8392fe45f3a7dfbda1ad4daa1eb07eschrock * Check the minimum size first.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /*
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Copy to temporary buffer if necessary.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint32_t cbPacket = RT_MIN(pSG->cbTotal, sizeof(RTNETARPIPV4));
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock PCRTNETARPIPV4 pArpIPv4 = (PCRTNETARPIPV4)((uintptr_t)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if ( pSG->cSegsUsed != 1
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock && pSG->aSegs[0].cb < cbPacket)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
55434c770c89aa1b84474f2559a106803511aba0ek if ( (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP))
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock != (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock && !intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return;
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock pArpIPv4 = (PCRTNETARPIPV4)pNetwork->pbTmp;
55434c770c89aa1b84474f2559a106803511aba0ek }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock /*
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * Ignore packets which doesn't interest us or we perceive as malformed.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
75519f380eac71fe6d10b26e736f01567d6c13c9ek return;
75519f380eac71fe6d10b26e736f01567d6c13c9ek uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
5ad820458efd0fdb914baff9c1447c22b819fa23nd && ar_oper != RTNET_ARPOP_REPLY))
99653d4ee642c6528e88224f12409a5f23060994eschrock {
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock Log6(("ts-ar: op=%#x\n", ar_oper));
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock }
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /*
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock * Delete the source address if it's OK.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock */
bf561db0030e7d3435bcc1e1f5889fac05a18814vb if ( !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_sha)
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock && ( pArpIPv4->ar_sha.au16[0]
bf561db0030e7d3435bcc1e1f5889fac05a18814vb || pArpIPv4->ar_sha.au16[1]
bf561db0030e7d3435bcc1e1f5889fac05a18814vb || pArpIPv4->ar_sha.au16[2])
99653d4ee642c6528e88224f12409a5f23060994eschrock && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock Log6(("ts-ar: %d.%d.%d.%d / %.6Rhxs\n", pArpIPv4->ar_spa.au8[0], pArpIPv4->ar_spa.au8[1],
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock pArpIPv4->ar_spa.au8[2], pArpIPv4->ar_spa.au8[3], &pArpIPv4->ar_sha));
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_spa,
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_spa), "tif/arp");
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock }
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock}
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock#ifdef INTNET_WITH_DHCP_SNOOPING
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock/**
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock * Snoop up addresses from ARP and DHCP traffic from frames coming
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * over the trunk connection.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock *
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * The caller is responsible for do some basic filtering before calling
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock * this function.
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock * For IPv4 this means checking against the minimum DHCPv4 frame size.
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock *
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pNetwork The network.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param pSG The SG list for the frame.
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock * @param EtherType The Ethertype of the frame.
55434c770c89aa1b84474f2559a106803511aba0ek */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrockstatic void intnetR0TrunkIfSnoopAddr(PINTNETNETWORK pNetwork, PCINTNETSG pSG, uint16_t EtherType)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock{
55434c770c89aa1b84474f2559a106803511aba0ek switch (EtherType)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock case RTNET_ETHERTYPE_IPV4:
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock uint32_t cbIpHdr;
c0a81264b59ba24de8701436570c3aae5689dc89ek uint8_t b;
c0a81264b59ba24de8701436570c3aae5689dc89ek
c0a81264b59ba24de8701436570c3aae5689dc89ek Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock {
55434c770c89aa1b84474f2559a106803511aba0ek /* check if the protocol is UDP */
55434c770c89aa1b84474f2559a106803511aba0ek PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
55434c770c89aa1b84474f2559a106803511aba0ek if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
55434c770c89aa1b84474f2559a106803511aba0ek return;
55434c770c89aa1b84474f2559a106803511aba0ek
99653d4ee642c6528e88224f12409a5f23060994eschrock /* get the TCP header length */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock cbIpHdr = pIpHdr->ip_hl * 4;
55434c770c89aa1b84474f2559a106803511aba0ek }
55434c770c89aa1b84474f2559a106803511aba0ek else
55434c770c89aa1b84474f2559a106803511aba0ek {
55434c770c89aa1b84474f2559a106803511aba0ek /* check if the protocol is UDP */
55434c770c89aa1b84474f2559a106803511aba0ek if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_p))
55434c770c89aa1b84474f2559a106803511aba0ek != RTNETIPV4_PROT_UDP)
55434c770c89aa1b84474f2559a106803511aba0ek return;
55434c770c89aa1b84474f2559a106803511aba0ek
55434c770c89aa1b84474f2559a106803511aba0ek /* get the TCP header length */
55434c770c89aa1b84474f2559a106803511aba0ek b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock cbIpHdr = (b & 0x0f) * 4;
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock }
3ccfa83cd9cddd1e34808ba18082c156758c5ec8ahrens if (cbIpHdr < RTNETIPV4_MIN_LEN)
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock return;
99653d4ee642c6528e88224f12409a5f23060994eschrock
99653d4ee642c6528e88224f12409a5f23060994eschrock /* compare the ports. */
e9dbad6f263d5570ed7ff5443ec5b958af8c24d7eschrock if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + cbIpHdr + RTNETUDP_MIN_LEN)
99653d4ee642c6528e88224f12409a5f23060994eschrock {
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR) + cbIpHdr);
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock if ( ( RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPS
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock && RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS)
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock || ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPC
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock && RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC))
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock return;
990b4856d0eaada6f8140335733a1b1771ed2746lling }
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock else
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock {
99653d4ee642c6528e88224f12409a5f23060994eschrock /* get the lower byte of the UDP source port number. */
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport) + 1);
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock if ( b != RTNETIPV4_PORT_BOOTPS
990b4856d0eaada6f8140335733a1b1771ed2746lling && b != RTNETIPV4_PORT_BOOTPC)
990b4856d0eaada6f8140335733a1b1771ed2746lling return;
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks uint8_t SrcPort = b;
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport));
99653d4ee642c6528e88224f12409a5f23060994eschrock if (b)
99653d4ee642c6528e88224f12409a5f23060994eschrock return;
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock
eaca9bbd5f5d1e4e554da4c7108e8a03c8c33481eschrock /* get the lower byte of the UDP destination port number. */
06eeb2ad640ce72d394ac521094bed7681044408ek b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport) + 1);
06eeb2ad640ce72d394ac521094bed7681044408ek if ( b != RTNETIPV4_PORT_BOOTPS
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek && b != RTNETIPV4_PORT_BOOTPC)
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek if (b == SrcPort)
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport));
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek if (b)
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek return;
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek }
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek break;
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek }
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek case RTNET_ETHERTYPE_IPV6:
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek {
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek * need to be edited. Check out how NDP works... */
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek break;
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek }
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek case RTNET_ETHERTYPE_ARP:
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek intnetR0TrunkIfSnoopArp(pNetwork, pSG);
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek break;
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek }
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek}
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek#endif /* INTNET_WITH_DHCP_SNOOPING */
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek
228975cc44c2290cc190960e5894ac6ce0863855ek/**
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks * Deals with an IPv4 packet.
06eeb2ad640ce72d394ac521094bed7681044408ek *
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek * This will fish out the source IP address and add it to the cache.
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek * Then it will look for DHCPRELEASE requests (?) and anything else
06eeb2ad640ce72d394ac521094bed7681044408ek * that we might find useful later.
2a6b87f07ac0c0b819179c84afe5a60afa04cfa5ek *
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pIf The interface that's sending the frame.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pIpHdr Pointer to the IPv4 header in the frame.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param cbPacket The size of the packet, or more correctly the
06eeb2ad640ce72d394ac521094bed7681044408ek * size of the frame without the ethernet header.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param fGso Set if this is a GSO frame, clear if regular.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ekstatic void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket, bool fGso)
06eeb2ad640ce72d394ac521094bed7681044408ek{
06eeb2ad640ce72d394ac521094bed7681044408ek /*
06eeb2ad640ce72d394ac521094bed7681044408ek * Check the header size first to prevent access invalid data.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ek if (cbPacket < RTNETIPV4_MIN_LEN)
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
06eeb2ad640ce72d394ac521094bed7681044408ek if ( cbHdr < RTNETIPV4_MIN_LEN
06eeb2ad640ce72d394ac521094bed7681044408ek || cbPacket < cbHdr)
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek /*
06eeb2ad640ce72d394ac521094bed7681044408ek * If the source address is good (not broadcast or my network) and
06eeb2ad640ce72d394ac521094bed7681044408ek * not already in the address cache of the sender, add it. Validate
06eeb2ad640ce72d394ac521094bed7681044408ek * the IP header before adding it.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ek bool fValidatedIpHdr = false;
06eeb2ad640ce72d394ac521094bed7681044408ek RTNETADDRU Addr;
06eeb2ad640ce72d394ac521094bed7681044408ek Addr.IPv4 = pIpHdr->ip_src;
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
06eeb2ad640ce72d394ac521094bed7681044408ek {
06eeb2ad640ce72d394ac521094bed7681044408ek if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket, !fGso /*fChecksum*/))
06eeb2ad640ce72d394ac521094bed7681044408ek {
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek }
d7306b64c847d897abb9ece8624fca9cf28d358fek intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, "if/ipv4");
d7306b64c847d897abb9ece8624fca9cf28d358fek fValidatedIpHdr = true;
d7306b64c847d897abb9ece8624fca9cf28d358fek }
d7306b64c847d897abb9ece8624fca9cf28d358fek
06eeb2ad640ce72d394ac521094bed7681044408ek#ifdef INTNET_WITH_DHCP_SNOOPING
ece3d9b3bacef51a5f34d993935eedbb7bb87059lling /*
06eeb2ad640ce72d394ac521094bed7681044408ek * Check for potential DHCP packets.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ek if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
06eeb2ad640ce72d394ac521094bed7681044408ek && cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN /* Min DHCP packet len. */
06eeb2ad640ce72d394ac521094bed7681044408ek && !fGso) /* GSO is not applicable to DHCP traffic. */
06eeb2ad640ce72d394ac521094bed7681044408ek {
06eeb2ad640ce72d394ac521094bed7681044408ek PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
06eeb2ad640ce72d394ac521094bed7681044408ek if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
06eeb2ad640ce72d394ac521094bed7681044408ek || RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
06eeb2ad640ce72d394ac521094bed7681044408ek && ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
06eeb2ad640ce72d394ac521094bed7681044408ek || RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
06eeb2ad640ce72d394ac521094bed7681044408ek {
06eeb2ad640ce72d394ac521094bed7681044408ek if ( fValidatedIpHdr
06eeb2ad640ce72d394ac521094bed7681044408ek || RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket, !fGso /*fChecksum*/))
06eeb2ad640ce72d394ac521094bed7681044408ek intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
06eeb2ad640ce72d394ac521094bed7681044408ek else
06eeb2ad640ce72d394ac521094bed7681044408ek Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
06eeb2ad640ce72d394ac521094bed7681044408ek }
06eeb2ad640ce72d394ac521094bed7681044408ek }
06eeb2ad640ce72d394ac521094bed7681044408ek#endif /* INTNET_WITH_DHCP_SNOOPING */
06eeb2ad640ce72d394ac521094bed7681044408ek}
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek/**
06eeb2ad640ce72d394ac521094bed7681044408ek * Snoop up source addresses from an ARP request or reply.
06eeb2ad640ce72d394ac521094bed7681044408ek *
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pIf The interface that's sending the frame.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pHdr The ARP header.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param cbPacket The size of the packet (might be larger than the ARP
06eeb2ad640ce72d394ac521094bed7681044408ek * request 'cause of min ethernet frame size).
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
06eeb2ad640ce72d394ac521094bed7681044408ek * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ekstatic void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
06eeb2ad640ce72d394ac521094bed7681044408ek{
06eeb2ad640ce72d394ac521094bed7681044408ek /*
06eeb2ad640ce72d394ac521094bed7681044408ek * Ignore packets which doesn't interest us or we perceive as malformed.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ek if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
06eeb2ad640ce72d394ac521094bed7681044408ek || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
06eeb2ad640ce72d394ac521094bed7681044408ek || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
06eeb2ad640ce72d394ac521094bed7681044408ek || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
06eeb2ad640ce72d394ac521094bed7681044408ek if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
06eeb2ad640ce72d394ac521094bed7681044408ek && ar_oper != RTNET_ARPOP_REPLY))
06eeb2ad640ce72d394ac521094bed7681044408ek {
06eeb2ad640ce72d394ac521094bed7681044408ek Log6(("ar_oper=%#x\n", ar_oper));
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek }
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek /*
06eeb2ad640ce72d394ac521094bed7681044408ek * Tag the SG as ARP IPv4 for later editing, then check for addresses
06eeb2ad640ce72d394ac521094bed7681044408ek * which can be removed or added to the address cache of the sender.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ek *pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek if ( ar_oper == RTNET_ARPOP_REPLY
06eeb2ad640ce72d394ac521094bed7681044408ek && !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_tha)
06eeb2ad640ce72d394ac521094bed7681044408ek && ( pArpIPv4->ar_tha.au16[0]
06eeb2ad640ce72d394ac521094bed7681044408ek || pArpIPv4->ar_tha.au16[1]
06eeb2ad640ce72d394ac521094bed7681044408ek || pArpIPv4->ar_tha.au16[2])
06eeb2ad640ce72d394ac521094bed7681044408ek && intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
06eeb2ad640ce72d394ac521094bed7681044408ek intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
06eeb2ad640ce72d394ac521094bed7681044408ek (PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek if ( !memcmp(&pArpIPv4->ar_sha, &pIf->MacAddr, sizeof(RTMAC))
06eeb2ad640ce72d394ac521094bed7681044408ek && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
06eeb2ad640ce72d394ac521094bed7681044408ek intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
06eeb2ad640ce72d394ac521094bed7681044408ek (PCRTNETADDRU)&pArpIPv4->ar_spa, sizeof(RTNETADDRIPV4), "if/arp");
06eeb2ad640ce72d394ac521094bed7681044408ek}
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek
06eeb2ad640ce72d394ac521094bed7681044408ek/**
06eeb2ad640ce72d394ac521094bed7681044408ek * Checks packets send by a normal interface for new network
06eeb2ad640ce72d394ac521094bed7681044408ek * layer addresses.
06eeb2ad640ce72d394ac521094bed7681044408ek *
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pIf The interface that's sending the frame.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pbFrame The frame.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param cbFrame The size of the frame.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param fGso Set if this is a GSO frame, clear if regular.
06eeb2ad640ce72d394ac521094bed7681044408ek * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
06eeb2ad640ce72d394ac521094bed7681044408ek * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ekstatic void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, bool fGso, uint16_t *pfSgFlags)
06eeb2ad640ce72d394ac521094bed7681044408ek{
06eeb2ad640ce72d394ac521094bed7681044408ek /*
06eeb2ad640ce72d394ac521094bed7681044408ek * Fish out the ethertype and look for stuff we can handle.
06eeb2ad640ce72d394ac521094bed7681044408ek */
06eeb2ad640ce72d394ac521094bed7681044408ek if (cbFrame <= sizeof(RTNETETHERHDR))
06eeb2ad640ce72d394ac521094bed7681044408ek return;
06eeb2ad640ce72d394ac521094bed7681044408ek cbFrame -= sizeof(RTNETETHERHDR);
06eeb2ad640ce72d394ac521094bed7681044408ek
55434c770c89aa1b84474f2559a106803511aba0ek uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
55434c770c89aa1b84474f2559a106803511aba0ek switch (EtherType)
55434c770c89aa1b84474f2559a106803511aba0ek {
55434c770c89aa1b84474f2559a106803511aba0ek case RTNET_ETHERTYPE_IPV4:
55434c770c89aa1b84474f2559a106803511aba0ek intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso);
55434c770c89aa1b84474f2559a106803511aba0ek break;
55434c770c89aa1b84474f2559a106803511aba0ek#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
55434c770c89aa1b84474f2559a106803511aba0ek case RTNET_ETHERTYPE_IPV6:
55434c770c89aa1b84474f2559a106803511aba0ek /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
55434c770c89aa1b84474f2559a106803511aba0ek * need to be edited. Check out how NDP works... */
55434c770c89aa1b84474f2559a106803511aba0ek intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso, pfSgFlags);
55434c770c89aa1b84474f2559a106803511aba0ek break;
55434c770c89aa1b84474f2559a106803511aba0ek#endif
55434c770c89aa1b84474f2559a106803511aba0ek#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
55434c770c89aa1b84474f2559a106803511aba0ek case RTNET_ETHERTYPE_IPX_1:
55434c770c89aa1b84474f2559a106803511aba0ek case RTNET_ETHERTYPE_IPX_2:
55434c770c89aa1b84474f2559a106803511aba0ek case RTNET_ETHERTYPE_IPX_3:
55434c770c89aa1b84474f2559a106803511aba0ek intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
55434c770c89aa1b84474f2559a106803511aba0ek break;
55434c770c89aa1b84474f2559a106803511aba0ek#endif
55434c770c89aa1b84474f2559a106803511aba0ek case RTNET_ETHERTYPE_ARP:
55434c770c89aa1b84474f2559a106803511aba0ek intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
55434c770c89aa1b84474f2559a106803511aba0ek break;
55434c770c89aa1b84474f2559a106803511aba0ek }
55434c770c89aa1b84474f2559a106803511aba0ek}
55434c770c89aa1b84474f2559a106803511aba0ek
55434c770c89aa1b84474f2559a106803511aba0ek
55434c770c89aa1b84474f2559a106803511aba0ek/**
55434c770c89aa1b84474f2559a106803511aba0ek * Writes a frame packet to the ring buffer.
55434c770c89aa1b84474f2559a106803511aba0ek *
55434c770c89aa1b84474f2559a106803511aba0ek * @returns VBox status code.
55434c770c89aa1b84474f2559a106803511aba0ek * @param pBuf The buffer.
55434c770c89aa1b84474f2559a106803511aba0ek * @param pRingBuf The ring buffer to read from.
55434c770c89aa1b84474f2559a106803511aba0ek * @param pSG The gather list.
55434c770c89aa1b84474f2559a106803511aba0ek * @param pNewDstMac Set the destination MAC address to the address if specified.
55434c770c89aa1b84474f2559a106803511aba0ek */
55434c770c89aa1b84474f2559a106803511aba0ekstatic int intnetR0RingWriteFrame(PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
55434c770c89aa1b84474f2559a106803511aba0ek{
55434c770c89aa1b84474f2559a106803511aba0ek PINTNETHDR pHdr = NULL; /* shut up gcc*/
55434c770c89aa1b84474f2559a106803511aba0ek void *pvDst = NULL; /* ditto */
55434c770c89aa1b84474f2559a106803511aba0ek int rc;
55434c770c89aa1b84474f2559a106803511aba0ek if (pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
55434c770c89aa1b84474f2559a106803511aba0ek rc = IntNetRingAllocateFrame(pRingBuf, pSG->cbTotal, &pHdr, &pvDst);
55434c770c89aa1b84474f2559a106803511aba0ek else
55434c770c89aa1b84474f2559a106803511aba0ek rc = IntNetRingAllocateGsoFrame(pRingBuf, pSG->cbTotal, &pSG->GsoCtx, &pHdr, &pvDst);
55434c770c89aa1b84474f2559a106803511aba0ek if (RT_SUCCESS(rc))
55434c770c89aa1b84474f2559a106803511aba0ek {
55434c770c89aa1b84474f2559a106803511aba0ek IntNetSgRead(pSG, pvDst);
b1b8ab34de515a5e83206da22c3d7e563241b021lling if (pNewDstMac)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor ((PRTNETETHERHDR)pvDst)->DstMac = *pNewDstMac;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor IntNetRingCommitFrame(pRingBuf, pHdr);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return VINF_SUCCESS;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor }
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return rc;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor}
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw/**
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * Sends a frame to a specific interface.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw *
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * @param pIf The interface.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * @param pSG The gather buffer which data is being sent to the interface.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * @param pNewDstMac Set the destination MAC address to the address if specified.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgwstatic void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw{
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /*
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * Grab the receive/producer lock and copy over the frame.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTSpinlockAcquire(pIf->hRecvInSpinlock);
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw int rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTSpinlockReleaseNoInts(pIf->hRecvInSpinlock);
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw if (RT_SUCCESS(rc))
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw {
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw pIf->cYields = 0;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw RTSemEventSignal(pIf->hRecvEvent);
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw return;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw }
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw /*
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw * Scheduling hack, for unicore machines primarily.
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw if ( pIf->fActive
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw && pIf->cYields < 4 /* just twice */
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw && pIfSender /* but not if it's from the trunk */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor && RTThreadPreemptIsEnabled(NIL_RTTHREAD)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor )
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor {
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor unsigned cYields = 2;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor while (--cYields > 0)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor {
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor RTSemEventSignal(pIf->hRecvEvent);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor RTThreadYield();
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor RTSpinlockAcquire(pIf->hRecvInSpinlock);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor RTSpinlockReleaseNoInts(pIf->hRecvInSpinlock);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor if (RT_SUCCESS(rc))
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor {
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor RTSemEventSignal(pIf->hRecvEvent);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor }
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor pIf->cYields++;
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw }
15e6edf145a9c2bb0e0272cf8debe823bb97529bgw STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor }
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor /* ok, the frame is lost. */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor RTSemEventSignal(pIf->hRecvEvent);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor}
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor/**
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Fallback path that does the GSO segmenting before passing the frame on to the
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * trunk interface.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor *
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * The caller holds the trunk lock.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor *
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pThis The trunk.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pIfSender The IF sending the frame.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pSG Pointer to the gather list.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param fDst The destination flags.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylorstatic int intnetR0TrunkIfSendGsoFallback(PINTNETTRUNKIF pThis, PINTNETIF pIfSender, PINTNETSG pSG, uint32_t fDst)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor{
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor /*
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Since we're only using this for GSO frame coming from the internal
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * network interfaces and never the trunk, we can assume there is only
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * one segment. This simplifies the code quite a bit.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor Assert(PDMNetGsoIsValid(&pSG->GsoCtx, sizeof(pSG->GsoCtx), pSG->cbTotal));
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante AssertReturn(pSG->cSegsUsed == 1, VERR_INTERNAL_ERROR_4);
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante union
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante {
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint8_t abBuf[sizeof(INTNETSG) + sizeof(INTNETSEG)];
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor INTNETSG SG;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor } u;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor /*
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Carve out the frame segments with the header and frame in different
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * scatter / gather segments.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint32_t const cSegs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor {
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint32_t cbSegPayload, cbSegHdrs;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint32_t offSegPayload = PDMNetGsoCarveSegment(&pSG->GsoCtx, (uint8_t *)pSG->aSegs[0].pv, pSG->cbTotal, iSeg, cSegs,
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor pIfSender->abGsoHdrs, &cbSegHdrs, &cbSegPayload);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor IntNetSgInitTempSegs(&u.SG, cbSegHdrs + cbSegPayload, 2, 2);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor u.SG.aSegs[0].Phys = NIL_RTHCPHYS;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor u.SG.aSegs[0].pv = pIfSender->abGsoHdrs;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor u.SG.aSegs[0].cb = cbSegHdrs;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor u.SG.aSegs[1].Phys = NIL_RTHCPHYS;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor u.SG.aSegs[1].pv = (uint8_t *)pSG->aSegs[0].pv + offSegPayload;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor u.SG.aSegs[1].cb = (uint32_t)cbSegPayload;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor int rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pIfSender->pvIfData, &u.SG, fDst);
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante if (RT_FAILURE(rc))
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante return rc;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor }
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return VINF_SUCCESS;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor}
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor/**
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Checks if any of the given trunk destinations can handle this kind of GSO SG.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor *
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @returns true if it can, false if it cannot.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pThis The trunk.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pSG The scatter / gather buffer.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param fDst The destination mask.
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante */
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusanteDECLINLINE(bool) intnetR0TrunkIfCanHandleGsoFrame(PINTNETTRUNKIF pThis, PINTNETSG pSG, uint32_t fDst)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor{
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint8_t u8Type = pSG->GsoCtx.u8Type;
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor AssertReturn(u8Type < 32, false); /* paranoia */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint32_t fMask = RT_BIT_32(u8Type);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor if (fDst == INTNETTRUNKDIR_HOST)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return !!(pThis->fHostGsoCapabilites & fMask);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor if (fDst == INTNETTRUNKDIR_WIRE)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return !!(pThis->fWireGsoCapabilites & fMask);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor Assert(fDst == (INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST));
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor return !!(pThis->fHostGsoCapabilites & pThis->fWireGsoCapabilites & fMask);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor}
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor/**
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Sends a frame down the trunk.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor *
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pThis The trunk.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pNetwork The network the frame is being sent to.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pIfSender The IF sending the frame. Used for MAC address
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * checks in shared MAC mode.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param fDst The destination flags.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * @param pSG Pointer to the gather list.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylorstatic void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor uint32_t fDst, PINTNETSG pSG)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor{
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor /*
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Quick sanity check.
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor AssertPtr(pThis);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor AssertPtr(pNetwork);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor AssertPtr(pIfSender);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor AssertPtr(pSG);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor Assert(fDst);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor AssertReturnVoid(pThis->pIfPort);
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor /*
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * Edit the frame if we're sharing the MAC address with the host on the wire.
c6ef114f2f708797a9cba68e8c08f42a03f094bbmmusante *
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * If the frame is headed for both the host and the wire, we'll have to send
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * it to the host before making any modifications, and force the OS specific
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * backend to copy it. We do this by marking it as TEMP (which is always the
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor * case right now).
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor */
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
8488aeb5df27784d479c16cde06a9e25cd9a1152taylor && (fDst & INTNETTRUNKDIR_WIRE))
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw {
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Dispatch it to the host before making changes.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (fDst & INTNETTRUNKDIR_HOST)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw {
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw fDst &= ~INTNETTRUNKDIR_HOST;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw }
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Edit the source address so that it it's the same as the host.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /* ASSUME frame from IntNetR0IfSend! */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw AssertReturnVoid(pSG->cSegsUsed == 1);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw AssertReturnVoid(pIfSender);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw pEthHdr->SrcMac = pThis->MacAddr;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Deal with tags from the snooping phase.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw {
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * APR IPv4: replace hardware (MAC) addresses because these end up
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * in ARP caches. So, if we don't the other machines will
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * send the packets to the MAC address of the guest
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * instead of the one of the host, which won't work on
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * wireless of course...
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (!memcmp(&pArp->ar_sha, &pIfSender->MacAddr, sizeof(RTMAC)))
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw {
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->MacAddr));
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw pArp->ar_sha = pThis->MacAddr;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw }
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (!memcmp(&pArp->ar_tha, &pIfSender->MacAddr, sizeof(RTMAC))) /* just in case... */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw {
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->MacAddr));
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw pArp->ar_tha = pThis->MacAddr;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw }
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw }
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw //else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw //{ /// @todo move the editing into a different function
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw //}
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw }
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Send the frame, handling the GSO fallback .
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * .
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Note! The trunk implementation will re-check that the trunk is active .
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * before sending, so we don't have to duplicate that effort here.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw STAM_REL_PROFILE_START(&pIfSender->pIntBuf->StatSend2, a);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw int rc;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if ( pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw || intnetR0TrunkIfCanHandleGsoFrame(pThis, pSG, fDst) )
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pIfSender->pvIfData, pSG, fDst);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw else
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw rc = intnetR0TrunkIfSendGsoFallback(pThis, pIfSender, pSG, fDst);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw STAM_REL_PROFILE_STOP(&pIfSender->pIntBuf->StatSend2, a);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /** @todo failure statistics? */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst)); NOREF(rc);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw}
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw/**
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Edits an ARP packet arriving from the wire via the trunk connection.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw *
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * @param pNetwork The network the frame is being sent to.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * @param pSG Pointer to the gather list for the frame.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * The flags and data content may be updated.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * @param pEthHdr Pointer to the ethernet header. This may also be
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * updated if it's a unicast...
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gwstatic void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw{
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Check the minimum size and get a linear copy of the thing to work on,
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * using the temporary buffer if necessary.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw return;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if ( pSG->cSegsUsed != 1
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw {
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw return;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw }
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw /*
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw * Ignore packets which doesn't interest us or we perceive as malformed.
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw */
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw return;
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
e7cbe64f7a72dae5cb44f100db60ca88f3313c65gw && 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)
{
NOREF(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 bMsgType;
if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &bMsgType))
{
Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad DHCP packet\n"));
return;
}
switch (bMsgType)
{
case RTNET_DHCP_MT_DISCOVER:
case RTNET_DHCP_MT_REQUEST:
/*
* Must set the broadcast flag or we won't catch the respons.
*/
if (!(pDhcp->bp_flags & RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST)))
{
Log6(("intnetR0NetworkEditDhcpFromIntNet: Setting broadcast flag in DHCP %#x, previously %x\n",
bMsgType, pDhcp->bp_flags));
/* 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);
}
#ifdef RT_OS_DARWIN
/*
* Work around little endian checksum issue in mac os x 10.7.0 GM.
*/
if ( pIpHdr->ip_tos
&& (pNetwork->fFlags & INTNET_OPEN_FLAGS_WORKAROUND_1))
{
/* Patch it. */
uint8_t uTos = pIpHdr->ip_tos;
uint8_t uZero = 0;
intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR) + 1, sizeof(uZero), &uZero);
/* Patch the IP header checksum. */
uint32_t uChecksum = (uint32_t)~pIpHdr->ip_sum - (uTos << 8);
while (uChecksum >> 16)
uChecksum = (uChecksum >> 16) + (uChecksum & 0xFFFF);
uChecksum = ~uChecksum;
Log(("intnetR0NetworkEditDhcpFromIntNet: cleared ip_tos (was %#04x); ip_sum=%#06x -> %#06x\n",
uTos, RT_BE2H_U16(pIpHdr->ip_sum), RT_BE2H_U16(uChecksum) ));
intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_sum),
sizeof(pIpHdr->ip_sum), &uChecksum);
}
#endif
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)
{
NOREF(pNetwork);
/* 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 HM 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 HM 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. */
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
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);
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 originating 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 originated via
* the trunk.
*/
static void intnetR0NetworkDeliver(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab, PINTNETSG pSG, PINTNETIF pIfSender)
{
/*
* Do the interfaces first before sending it to the wire and risk having to
* modify it.
*/
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;
/*
* Send to the trunk.
*
* 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;
}
}
/**
* 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));
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
PINTNETMACTABENTRY pIfEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIfSender);
if (pIfEntry)
pIfEntry->MacAddr = EthHdr.SrcMac;
pIfSender->MacAddr = EthHdr.SrcMac;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
}
/*
* 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;
STAM_REL_PROFILE_START(&pIf->pIntBuf->StatSend1, a);
/*
* 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 = ASMAtomicXchgPtrT(&pIf->pDstTab, NULL, PINTNETDSTTAB);
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 right 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(&pIf->pDstTab, pDstTab);
}
else
rc = VERR_INTERNAL_ERROR_4;
}
else
rc = VERR_INTERNAL_ERROR_3;
/*
* Release the interface.
*/
intnetR0BusyDecIf(pIf);
STAM_REL_PROFILE_STOP(&pIf->pIntBuf->StatSend1, a);
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)
{
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
if (pIf->fPromiscuousReal != fPromiscuous)
{
const bool fPromiscuousEff = fPromiscuous
&& (pIf->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW)
&& (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS);
Log(("IntNetR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d (%d)\n",
hIf, !fPromiscuous, !!fPromiscuous, fPromiscuousEff));
pIf->fPromiscuousReal = fPromiscuous;
PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
if (RT_LIKELY(pEntry))
{
if (pEntry->fPromiscuousEff)
{
pNetwork->MacTab.cPromiscuousEntries--;
if (!pEntry->fPromiscuousSeeTrunk)
pNetwork->MacTab.cPromiscuousNoTrunkEntries--;
Assert(pNetwork->MacTab.cPromiscuousEntries < pNetwork->MacTab.cEntries);
Assert(pNetwork->MacTab.cPromiscuousNoTrunkEntries < pNetwork->MacTab.cEntries);
}
pEntry->fPromiscuousEff = fPromiscuousEff;
pEntry->fPromiscuousSeeTrunk = fPromiscuousEff
&& (pIf->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK);
if (pEntry->fPromiscuousEff)
{
pNetwork->MacTab.cPromiscuousEntries++;
if (!pEntry->fPromiscuousSeeTrunk)
pNetwork->MacTab.cPromiscuousNoTrunkEntries++;
}
Assert(pNetwork->MacTab.cPromiscuousEntries <= pNetwork->MacTab.cEntries);
Assert(pNetwork->MacTab.cPromiscuousNoTrunkEntries <= pNetwork->MacTab.cEntries);
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
}
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)
{
PINTNETTRUNKIF pTrunk = NULL;
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
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);
if (pTrunk)
{
Log(("IntNetR0IfSetMacAddress: pfnNotifyMacAddress hIf=%RX32\n", hIf));
PINTNETTRUNKIFPORT pIfPort = pTrunk->pIfPort;
if (pIfPort)
pIfPort->pfnNotifyMacAddress(pIfPort, pIf->pvIfData, 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.
*
* The call must own the giant lock (we cannot take it here).
*
* @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 sanity 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 hassle.
*/
PINTNETTRUNKIF pTrunk = NULL;
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
/*
* 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 = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
pNetwork->MacTab.fWireActive = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED);
}
}
}
else
{
pNetwork->cActiveIFs--;
if (pNetwork->cActiveIFs == 0)
{
pTrunk = pNetwork->MacTab.pTrunk;
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWireActive = false;
}
}
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
/*
* Tell the trunk if necessary.
* The wait for !busy is for the Solaris streams trunk driver (mostly).
*/
if (pTrunk && pTrunk->pIfPort)
{
if (!fActive)
intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, fActive ? INTNETTRUNKIFSTATE_ACTIVE : INTNETTRUNKIFSTATE_INACTIVE);
}
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.
*
* 1. We take the giant lock here. This makes sure nobody is re-enabling
* the network while we're pausing it and vice versa. This also enables
* us to wait for the network to become idle before telling the trunk.
* (Important on Solaris.)
*
* 2. For paranoid reasons, we grab a busy reference to the calling
* interface. This is totally unnecessary but should hurt (when done
* after grabbing the giant lock).
*/
int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
if (RT_SUCCESS(rc))
{
intnetR0BusyIncIf(pIf);
PINTNETNETWORK pNetwork = pIf->pNetwork;
if (pNetwork)
rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
else
rc = VERR_WRONG_ORDER;
intnetR0BusyDecIf(pIf);
RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
}
intnetR0IfRelease(pIf, pSession);
LogFlow(("IntNetR0IfSetActive: returns %Rrc\n", rc));
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;
const bool fDestroying = ASMAtomicReadBool(&pIf->fDestroying);
if ( hIfSelf != hIf /* paranoia */
|| hRecvEvent == NIL_RTSEMEVENT
|| fDestroying
)
{
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);
}
/**
* Wake up any threads waiting on the interface.
*
* @returns VBox status code.
* @param hIf The interface handle.
* @param pSession The caller's session.
* @param fNoMoreWaits When set, no more waits are permitted.
*/
INTNETR0DECL(int) IntNetR0IfAbortWait(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fNoMoreWaits)
{
Log4(("IntNetR0IfAbortWait: hIf=%RX32 fNoMoreWaits=%RTbool\n", hIf, fNoMoreWaits));
/*
* 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(("IntNetR0IfAbortWait: returns VERR_INVALID_HANDLE\n"));
return VERR_INVALID_HANDLE;
}
const INTNETIFHANDLE hIfSelf = pIf->hIf;
const RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
const bool fDestroying = ASMAtomicReadBool(&pIf->fDestroying);
if ( hIfSelf != hIf /* paranoia */
|| hRecvEvent == NIL_RTSEMEVENT
|| fDestroying
)
{
Log(("IntNetR0IfAbortWait: returns VERR_SEM_DESTROYED\n"));
return VERR_SEM_DESTROYED;
}
/*
* Set fDestroying if requested to do so and then wake up all the sleeping
* threads (usually just one). We leave the semaphore in the signalled
* state so the next caller will return immediately.
*/
if (fNoMoreWaits)
ASMAtomicWriteBool(&pIf->fDestroying, true);
uint32_t cSleepers = ASMAtomicReadU32(&pIf->cSleepers) + 1;
while (cSleepers-- > 0)
{
int rc = RTSemEventSignal(pIf->hRecvEvent);
AssertRC(rc);
}
Log4(("IntNetR0IfWait: returns %Rrc\n", VINF_SUCCESS));
return VINF_SUCCESS;
}
/**
* VMMR0 request wrapper for IntNetR0IfAbortWait.
*
* @returns see IntNetR0IfWait.
* @param pSession The caller's session.
* @param pReq The request packet.
*/
INTNETR0DECL(int) IntNetR0IfAbortWaitReq(PSUPDRVSESSION pSession, PINTNETIFABORTWAITREQ pReq)
{
if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
return VERR_INVALID_PARAMETER;
return IntNetR0IfAbortWait(pReq->hIf, pSession, pReq->fNoMoreWaits);
}
/**
* 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.
*/
PINTNET pIntNet = g_pIntNet;
AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
if (!pIf)
return VERR_INVALID_HANDLE;
/* Mark the handle as freed so intnetR0IfDestruct won't free it again. */
ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
/*
* 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. */
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
{
if (pNetwork->MacTab.paEntries[iIf].fPromiscuousEff)
{
pNetwork->MacTab.cPromiscuousEntries--;
if (!pNetwork->MacTab.paEntries[iIf].fPromiscuousSeeTrunk)
pNetwork->MacTab.cPromiscuousNoTrunkEntries--;
}
Assert(pNetwork->MacTab.cPromiscuousEntries < pNetwork->MacTab.cEntries);
Assert(pNetwork->MacTab.cPromiscuousNoTrunkEntries < pNetwork->MacTab.cEntries);
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;
}
/* recalc the min flags. */
if (pIf->fOpenFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES)
{
uint32_t fMinFlags = 0;
iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
{
PINTNETIF pIf2 = pNetwork->MacTab.paEntries[iIf].pIf;
if ( pIf2 /* paranoia */
&& (pIf2->fOpenFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES))
fMinFlags |= pIf2->fOpenFlags & INTNET_OPEN_FLAGS_STRICT_MASK;
}
pNetwork->fMinFlags = fMinFlags;
}
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
/* Notify the trunk about the interface being destroyed. */
if (pTrunk && pTrunk->pIfPort)
pTrunk->pIfPort->pfnDisconnectInterface(pTrunk->pIfPort, pIf->pvIfData);
/* Wait for the interface to quiesce while we still can. */
intnetR0BusyWait(pNetwork, &pIf->cBusy);
/* Release our reference to the network. */
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
pIf->pNetwork = NULL;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
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 fFlags The open network flags.
* @param phIf Where to store the interface handle.
*/
static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession,
unsigned cbSend, unsigned cbRecv, uint32_t fFlags,
PINTNETIFHANDLE phIf)
{
LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u fFlags=%#x phIf=%p\n",
pNetwork, pSession, cbSend, cbRecv, fFlags, phIf));
/*
* Assert input.
*/
AssertPtr(pNetwork);
AssertPtr(phIf);
/*
* Adjust the flags with defaults for the interface policies.
* Note: Main restricts promiscuous mode per interface.
*/
uint32_t const fDefFlags = INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW
| INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK;
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkIfFlags); i++)
if (!(fFlags & g_afIntNetOpenNetworkIfFlags[i].fPair))
fFlags |= g_afIntNetOpenNetworkIfFlags[i].fPair & fDefFlags;
/*
* 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 initialize 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->fPromiscuousReal = false;
//pIf->fActive = false;
//pIf->fDestroying = false;
pIf->fOpenFlags = fFlags;
//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;
//pIf->pvIfData = 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, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "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.
*/
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
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].fPromiscuousEff = false;
pNetwork->MacTab.paEntries[iIf].fPromiscuousSeeTrunk = false;
pNetwork->MacTab.paEntries[iIf].pIf = pIf;
pNetwork->MacTab.cEntries = iIf + 1;
pIf->pNetwork = pNetwork;
/*
* Grab a busy reference (paranoia) to the trunk before releasing
* the spinlock and then notify it about the new interface.
*/
PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
if (pTrunk)
intnetR0BusyIncTrunk(pTrunk);
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
if (pTrunk)
{
Log(("intnetR0NetworkCreateIf: pfnConnectInterface hIf=%RX32\n", pIf->hIf));
if (pTrunk->pIfPort)
rc = pTrunk->pIfPort->pfnConnectInterface(pTrunk->pIfPort, pIf, &pIf->pvIfData);
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)
{
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
pNetwork->MacTab.HostMac = *pMacAddr;
pThis->MacAddr = *pMacAddr;
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
}
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)
{
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
pNetwork->MacTab.fHostPromiscuousReal = fPromiscuous
|| (pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE);
pNetwork->MacTab.fHostPromiscuousEff = pNetwork->MacTab.fHostPromiscuousReal
&& (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST);
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
}
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);
/* assert some sanity */
AssertPtr(pvSrc);
AssertReturn(cbSrc >= 6, INTNETSWDECISION_BROADCAST);
Assert(fSrc);
/*
* Mark the trunk as busy, make sure we've got a network and that there are
* some active interfaces around.
*/
INTNETSWDECISION enmSwDecision = INTNETSWDECISION_TRUNK;
intnetR0BusyIncTrunk(pThis);
PINTNETNETWORK pNetwork = pThis->pNetwork;
if (RT_LIKELY( pNetwork
&& pNetwork->cActiveIFs > 0 ))
{
/*
* Lazy bird! No pre-switching of multicast and shared-MAC-on-wire.
*/
PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvSrc;
if (intnetR0IsMacAddrMulticast(&pEthHdr->DstMac))
enmSwDecision = INTNETSWDECISION_BROADCAST;
else if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
enmSwDecision = INTNETSWDECISION_BROADCAST;
else
enmSwDecision = intnetR0NetworkPreSwitchUnicast(pNetwork,
fSrc,
cbSrc >= 12 ? &pEthHdr->SrcMac : NULL,
&pEthHdr->DstMac);
}
intnetR0BusyDecTrunk(pThis);
return enmSwDecision;
}
/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, void *pvIf, PINTNETSG pSG, uint32_t fSrc)
{
PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
/* assert some sanity */
AssertPtr(pSG);
Assert(fSrc);
NOREF(pvIf); /* later */
/*
* 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;
RTSpinlockAcquire(pThis->hDstTabSpinlock);
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);
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);
Assert(iDstTab < RT_ELEMENTS(pThis->apTaskDstTabs));
}
else
{
RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock);
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
{
RTSpinlockAcquire(pThis->hDstTabSpinlock);
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);
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 */
}
}
/**
* 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' didn'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' didn'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' didn'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, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "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.fHostPromiscuousReal = false;
pNetwork->MacTab.fHostPromiscuousEff = (pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE)
&& (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST);
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWirePromiscuousReal = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE);
pNetwork->MacTab.fWirePromiscuousEff = pNetwork->MacTab.fWirePromiscuousReal
&& (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_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 */
NOREF(pSession);
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.
*/
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
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);
/* 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. */
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
while ((iIf = pNetwork->MacTab.cEntries) > 0)
{
PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf - 1].pIf;
RTSpinlockRelease(pNetwork->hAddrSpinlock);
intnetR0BusyWait(pNetwork, &pIf->cBusy);
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
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);
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);
}
/**
* Checks if the open network flags are compatible.
*
* @returns VBox status code.
* @param pNetwork The network.
* @param fFlags The open network flags.
*/
static int intnetR0CheckOpenNetworkFlags(PINTNETNETWORK pNetwork, uint32_t fFlags)
{
uint32_t const fNetFlags = pNetwork->fFlags;
if ( (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
^ (fNetFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE))
return VERR_INTNET_INCOMPATIBLE_FLAGS;
if (fFlags & INTNET_OPEN_FLAGS_REQUIRE_EXACT)
{
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
if ( (fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair)
&& (fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair)
!= (fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fPair) )
return VERR_INTNET_INCOMPATIBLE_FLAGS;
}
if (fFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES)
{
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
if ( (fFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive)
&& !(fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive)
&& (fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fFixed) )
return VERR_INTNET_INCOMPATIBLE_FLAGS;
}
return VINF_SUCCESS;
}
/**
* Adapts flag changes on network opening.
*
* @returns VBox status code.
* @param pNetwork The network.
* @param fFlags The open network flags.
*/
static int intnetR0AdaptOpenNetworkFlags(PINTNETNETWORK pNetwork, uint32_t fFlags)
{
/*
* Upgrade the minimum policy flags.
*/
uint32_t fNetMinFlags = pNetwork->fMinFlags;
Assert(!(fNetMinFlags & INTNET_OPEN_FLAGS_RELAXED_MASK));
if (fFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES)
{
fNetMinFlags |= fFlags & INTNET_OPEN_FLAGS_STRICT_MASK;
if (fNetMinFlags != pNetwork->fMinFlags)
{
LogRel(("INTNET: %s - min flags changed %#x -> %#x\n", pNetwork->szName, pNetwork->fMinFlags, fNetMinFlags));
pNetwork->fMinFlags = fNetMinFlags;
}
}
/*
* Calculate the new network flags.
* (Depends on fNetMinFlags being recalculated first.)
*/
uint32_t fNetFlags = pNetwork->fFlags;
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
{
Assert(fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fPair);
Assert(!(fNetMinFlags & g_afIntNetOpenNetworkNetFlags[i].fRelaxed));
if (!(fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair))
continue;
if (fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fFixed)
continue;
if ( (fNetMinFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive)
|| (fFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive) )
{
fNetFlags &= ~g_afIntNetOpenNetworkNetFlags[i].fPair;
fNetFlags |= g_afIntNetOpenNetworkNetFlags[i].fRestrictive;
}
else if (!(fFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES))
{
fNetFlags &= ~g_afIntNetOpenNetworkNetFlags[i].fPair;
fNetFlags |= g_afIntNetOpenNetworkNetFlags[i].fRelaxed;
}
}
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
{
Assert(fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fPair);
fNetFlags |= fFlags & g_afIntNetOpenNetworkNetFlags[i].fFixed;
}
/*
* Apply the flags if they changed.
*/
uint32_t const fOldNetFlags = pNetwork->fFlags;
if (fOldNetFlags != fNetFlags)
{
LogRel(("INTNET: %s - flags changed %#x -> %#x\n", pNetwork->szName, fOldNetFlags, fNetFlags));
RTSpinlockAcquire(pNetwork->hAddrSpinlock);
pNetwork->fFlags = fNetFlags;
/* Recalculate some derived switcher variables. */
bool fActiveTrunk = pNetwork->MacTab.pTrunk
&& pNetwork->cActiveIFs > 0;
pNetwork->MacTab.fHostActive = fActiveTrunk
&& (fNetFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
pNetwork->MacTab.fHostPromiscuousEff = ( pNetwork->MacTab.fHostPromiscuousReal
|| (fNetFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE))
&& (fNetFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST);
pNetwork->MacTab.fWireActive = fActiveTrunk
&& (fNetFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
pNetwork->MacTab.fWirePromiscuousReal= RT_BOOL(fNetFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE);
pNetwork->MacTab.fWirePromiscuousEff = pNetwork->MacTab.fWirePromiscuousReal
&& (fNetFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE);
if ((fOldNetFlags ^ fNetFlags) & INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS)
{
pNetwork->MacTab.cPromiscuousEntries = 0;
pNetwork->MacTab.cPromiscuousNoTrunkEntries = 0;
uint32_t iIf = pNetwork->MacTab.cEntries;
while (iIf-- > 0)
{
PINTNETMACTABENTRY pEntry = &pNetwork->MacTab.paEntries[iIf];
PINTNETIF pIf2 = pEntry->pIf;
if ( pIf2 /* paranoia */
&& pIf2->fPromiscuousReal)
{
bool fPromiscuousEff = (fNetFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS)
&& (pIf2->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW);
pEntry->fPromiscuousEff = fPromiscuousEff;
pEntry->fPromiscuousSeeTrunk = fPromiscuousEff
&& (pIf2->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK);
if (pEntry->fPromiscuousEff)
{
pNetwork->MacTab.cPromiscuousEntries++;
if (!pEntry->fPromiscuousSeeTrunk)
pNetwork->MacTab.cPromiscuousNoTrunkEntries++;
}
}
}
}
RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock);
}
return VINF_SUCCESS;
}
/**
* 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 specific 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)))
{
rc = intnetR0CheckOpenNetworkFlags(pCur, fFlags);
if (RT_SUCCESS(rc))
{
/*
* 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_ACCESS_RESTRICTED)
rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
if (RT_SUCCESS(rc))
*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_TRUNK;
LogRel(("intnetR0OpenNetwork failed. rc=%Rrc pCur->szTrunk=%s pszTrunk=%s pCur->enmTrunkType=%d enmTrunkType=%d\n",
rc, pCur->szTrunk, pszTrunk, pCur->enmTrunkType, enmTrunkType));
}
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 specific 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;
/*
* Adjust the flags with defaults for the network policies.
* Note: Main restricts promiscuous mode on the per interface level.
*/
fFlags &= ~( INTNET_OPEN_FLAGS_IF_FIXED
| INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW
| INTNET_OPEN_FLAGS_IF_PROMISC_DENY
| INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK
| INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK
| INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES
| INTNET_OPEN_FLAGS_REQUIRE_EXACT);
uint32_t fDefFlags = INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS
| INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST
| INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE
| INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED
| INTNET_OPEN_FLAGS_TRUNK_HOST_CHASTE_MODE
| INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED
| INTNET_OPEN_FLAGS_TRUNK_WIRE_CHASTE_MODE;
if ( enmTrunkType == kIntNetTrunkType_WhateverNone
|| enmTrunkType == kIntNetTrunkType_None)
fDefFlags |= INTNET_OPEN_FLAGS_ACCESS_RESTRICTED;
else
fDefFlags |= INTNET_OPEN_FLAGS_ACCESS_PUBLIC;
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
if (!(fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair))
fFlags |= g_afIntNetOpenNetworkNetFlags[i].fPair & fDefFlags;
/*
* 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.cPromiscuousEntries = 0;
//pNetwork->MacTab.cPromiscuousNoTrunkEntries = 0;
pNetwork->MacTab.paEntries = NULL;
pNetwork->MacTab.fHostPromiscuousReal = false;
pNetwork->MacTab.fHostPromiscuousEff = false;
pNetwork->MacTab.fHostActive = false;
pNetwork->MacTab.fWirePromiscuousReal = false;
pNetwork->MacTab.fWirePromiscuousEff = 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->fMinFlags = 0;
//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, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "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 specific 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 = RTStrEnd(pszNetwork, 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 = RTStrEnd(pszTrunk, 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);
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
AssertMsgReturn((fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair) != g_afIntNetOpenNetworkNetFlags[i].fPair,
("%#x (%#x)\n", fFlags, g_afIntNetOpenNetworkNetFlags[i].fPair), VERR_INVALID_PARAMETER);
for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkIfFlags); i++)
AssertMsgReturn((fFlags & g_afIntNetOpenNetworkIfFlags[i].fPair) != g_afIntNetOpenNetworkIfFlags[i].fPair,
("%#x (%#x)\n", fFlags, g_afIntNetOpenNetworkIfFlags[i].fPair), 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, fFlags, phIf);
if (RT_SUCCESS(rc))
{
intnetR0AdaptOpenNetworkFlags(pNetwork, fFlags);
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, fFlags, 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);
}
/**
* Initializes 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;
}