Files
vmware-host-modules/vmnet-only/smac.c
T

4951 lines
159 KiB
C

/*********************************************************
* Copyright (C) 2002 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 and no later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*********************************************************/
/*
* smac.c --
*
* This file defines functionality that allows the
* bridge to be used across links that do
* not support promiscuous mode, or do not provide the
* ability to transmit ethernet frames whose MAC source
* address does not match the hardware's MAC address.
*
* This code extension basically forces the bridge to
* use a single MAC, thus the name SMAC.
*/
/* platform-dependent includes */
#ifdef _WIN32
#define BINARY_COMPATIBLE 0 // NT-only driver (optimizes some NDIS calls)
#include <ndis.h>
#include <ntstrsafe.h>
#include "vnetInt.h"
#else /* _WIN32 */
#undef __KERNEL__ //To prevent including any kernel specific stuff
#ifdef VMX86_DEVEL
#define DBG 1
#else
#undef DBG
#endif /* VMX86_DEVEL */
#ifdef __APPLE__
#include <sys/kpi_mbuf.h>
#include <libkern/libkern.h>
#endif
#include "smac_compat.h"
#endif /* _WIN32 */
/* platform-independent includes */
#include "smac.h"
#include "vm_basic_defs.h"
#include "vm_basic_asm.h"
#define SMAC_MODULE "SMAC: "
#define MODULE_NAME SMAC_MODULE
/* platform-dependent defines */
#ifdef _WIN32
#define ALLOCATEMEMORY(a,b) VNet_AllocateMemoryWithTag((a),(b))
#define FREEMEMORY(a) VNet_FreeMemory((a))
#define MEMCPY(a,b,c) NdisMoveMemory((a),(b),(c))
#define MEMSET(a,b,c) NdisFillMemory((a),(c),(b))
#define SPINLOCKINIT() do { } while (0)
#define INITSPINLOCK(a) do { NdisAllocateSpinLock( (a) ); } while(0)
#define RAISEIRQL() do { irql = KeRaiseIrqlToDpcLevel(); } while(0)
#define ACQUIRESPINLOCK(a) do { ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); \
NdisDprAcquireSpinLock( (a) ); } while(0)
#define RELEASESPINLOCK(a) do { NdisDprReleaseSpinLock( (a) ); } while(0)
#define LOWERIRQL() do { KeLowerIrql(irql); } while(0)
#define FREESPINLOCK(a) do { NdisFreeSpinLock( (a) ); } while(0)
#define ASSERTLOCKHELD() ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL)
#define SNPRINTF(a) (RtlStringCbPrintfA a)
#elif defined __linux__ || defined __APPLE__
#define ALLOCATEMEMORY(a,b) SMACL_Alloc((a))
#define MEMCPY(a,b,c) SMACL_Memcpy((a),(b),(c))
#define MEMSET(a,b,c) SMACL_Memset((a),(b),(c))
#define INITSPINLOCK(a) SMACL_InitSpinlock( (a) )
#define RAISEIRQL() do { } while (0)
#define LOWERIRQL() do { } while (0)
#define ASSERTLOCKHELD() do { } while (0)
#define UNREFERENCED_PARAMETER(a) { (a) = (a); }
/*
* The following are defined to create OS dependent versions of
* functionality available on Windows.
*/
#undef ASSERT
#ifdef DBG
# if defined __APPLE__
# define VNETKdPrint(a) (Log a)
# else // __linux__
# define VNETKdPrint(a) (SMACL_Print a)
# endif
# define ASSERT(a) do {if (!(a)) {VNETKdPrint(("ASSERT FAILED: "#a));}} while(0)
#else
# define VNETKdPrint(a) do { } while (0)
# define ASSERT(a) do { } while (0)
#endif
#define VNETKdPrintCall(a) VNETKdPrint((MODULE_NAME "Calling : %s\n", a))
#define VNETKdPrintReturn(a) VNETKdPrint((MODULE_NAME "Returned : %s\n", a))
#ifndef MAC_EQ
#define MAC_EQ(a,b) (SMACL_Memcmp((a),(b),ETH_ALEN)==0)
#endif /* MAC_EQ */
#define IS_MULTICAST(_hdr) ((_hdr)[0] & 0x1)
#define IS_BROADCAST(_a) \
((_a)[0] == 0xff && (_a)[1] == 0xff && (_a)[2] == 0xff && \
(_a)[3] == 0xff && (_a)[4] == 0xff && (_a)[5] == 0xff)
#ifdef __linux__
#define FREEMEMORY(a) SMACL_Free((a))
#define SPINLOCKINIT() unsigned long flags
#define FREESPINLOCK(a) SMACL_Free(*a)
#define ACQUIRESPINLOCK(a) SMACL_AcquireSpinlock( (a), &flags)
#define RELEASESPINLOCK(a) SMACL_ReleaseSpinlock( (a), &flags)
extern int VNetSnprintf(char *str, size_t size, const char *format, ...);
#define SNPRINTF(a) (VNetSnprintf a)
#else /* __APPLE__ */
#define FREEMEMORY(a) SMACL_Free((a), sizeof *(a))
#define SPINLOCKINIT() do { } while (0)
#define FREESPINLOCK(a) SMACL_FreeSpinlock( (a) )
#define ACQUIRESPINLOCK(a) SMACL_AcquireSpinlock( *(a) )
#define RELEASESPINLOCK(a) SMACL_ReleaseSpinlock( *(a) )
#define SNPRINTF(a) (snprintf a)
#endif
#else
#error "unknown platform"
#endif /* _WIN32 */
/* Offsets/lengths for IPv4, UDP, and ARP headers. */
#define IP_HEADER_LEN 20
#define IP_HEADER_DEST_ADDR_OFFSET 16
#define IP_HEADER_SRC_ADDR_OFFSET 12
#define IP_HEADER_FLAGS_OFFSET 6
#define IP_HEADER_PROTO_OFFSET 9
#define UDP_HEADER_LEN 8
#define ARP_HEADER_LEN 28
#define ARP_SENDER_MAC_OFFSET 8
#define ARP_SENDER_IP_OFFSET 14
#define ARP_TARGET_MAC_OFFSET 18
#define ARP_TARGET_IP_OFFSET 24
#define IP_ADDR_BROADCAST 0xFFFFFFFF
#define IPv4 4
#define IPv6 6
#define IP_STRING_SIZE 40 /* To express IP in string format. */
#define MAC_STRING_SIZE 18 /* To express MAC in string format. */
/* Offsets/lengths for IPv6 headers. */
#define IPv6_HEADER_LEN 40
#define IPv6_HEADER_SRC_ADDR_OFFSET 8
#define IPv6_HEADER_DST_ADDR_OFFSET 24
#define IPv6_NEXT_HEADER_OFFSET 6
#define ICMPv6_TYPE_OFFSET 0 /* From start of ICMPv6 payload. */
#define ICMPv6_CHECKSUM_OFFSET 2 /* From start of ICMPv6 payload. */
#define ICMPv6_NDP_NBR_LEN 24 /*
* Message length (not including
* options) for neighbor solicitation
* and advertisement messages.
*/
#define ICMPv6_NDP_RTR_SOL_LEN 8 /* For router solicitation. */
#define ICMPv6_NDP_RTR_ADV_LEN 16 /* For router advertisement. */
#define ICMPv6_TARGET_IP_OFFSET 8 /* From start of ICMPv6 payload. */
#define ICMPv6_NDP_OPTION_TYPE_OFFSET 0 /* Offset from start of option. */
#define ICMPv6_NDP_OPTION_LEN_OFFSET 1 /* Offset from start of option. */
#define ICMPv6_NDP_MAC_OFFSET 2 /* Offset from start of option. */
#define ICMPv6_NDP_OPTION_SRC_MAC 1 /* Source link-layer option. */
#define ICMPv6_NDP_OPTION_TARGET_MAC 2 /* Target link-layer option. */
#define ICMPv6_NDP_RTR_SOLICITATION 133 /* Router solicitation. */
#define ICMPv6_NDP_RTR_ADVERTISEMENT 134 /* Router advertisement. */
#define ICMPv6_NDP_NBR_SOLICITATION 135 /* Neighbor solicitation. */
#define ICMPv6_NDP_NBR_ADVERTISEMENT 136 /* Neighbor advertisement. */
/*
* To limit the amount of kernel log information, define
* WIRELESS_BE_QUIET or WIRELESS_BE_VERY_QUIET. The former
* reduces the logging to a point where the host system isn't
* bogged down with logging all the details of broadcast traffic
* coming in from the company network. The latter define will
* completely turn off wireless logging.
*/
#define WIRELESS_BE_VERY_QUIET
//#define WIRELESS_BE_QUIET
#ifdef WIRELESS_BE_VERY_QUIET
#define WW_VNETKdPrint(a)
#define WW_DEVEL_ONLY(a)
#define W_VNETKdPrint(a)
#define W_DEVEL_ONLY(a)
#else // WIRELESS_BE_VERY_QUIET
#ifdef WIRELESS_BE_QUIET
#define WW_VNETKdPrint(a)
#define WW_DEVEL_ONLY(a)
#define W_VNETKdPrint(a) VNETKdPrint(a)
# ifdef DBG
# define W_DEVEL_ONLY(a) a
# else
# define W_DEVEL_ONLY(a)
#endif
#else // WIRELESS_BE_QUIET
#define WW_VNETKdPrint(a) VNETKdPrint(a)
# ifdef DBG
# define WW_DEVEL_ONLY(a) a
# else
# define WW_DEVEL_ONLY(a)
# endif
#define W_VNETKdPrint(a) VNETKdPrint(a)
# ifdef DBG
# define W_DEVEL_ONLY(a) a
# else
# define W_DEVEL_ONLY(a)
# endif
#endif // WIRELESS_BE_QUIET
#endif // WIRELESS_BE_VERY_QUIET
/*
* Host-to-Network / Network-to-Host byte-order routines
*/
#ifdef __APPLE__
#undef HTONL
#undef NTOHL
#undef HTONS
#undef NTOHS
#endif /* __APPLE__ */
/*
* For use in case labels where compile-time consts are required (obj builds)
*/
#define CONST_NTOHL(i) \
(((uint32)(i))>>24 | ((i)&0xff)<<24 | ((i)&0x00ff0000)>>8 | ((i)&0x0000ff00)<<8)
/*
* All other uses should call inline BswapNN() for type checking
*/
#define HTONL(i) Bswap32(i)
#define NTOHL(i) Bswap32(i)
#define HTONS(i) Bswap16(i)
#define NTOHS(i) Bswap16(i)
/*
* IPmacLookupEntry: defines entry in IP/MAC hash tables for finding which
* IP corresonds to which MAC
*/
#ifdef _WIN32
typedef uint64 SmacLastAccess;
#define LAST_ACCESS_FORMAT "%"FMT64"u" // format of lastAccess for printf()
#else
typedef unsigned long SmacLastAccess;
#define LAST_ACCESS_FORMAT "%lu" // format of lastAccess for printf()
#endif /* _WIN32 */
typedef union IPAddrUnion {
uint32 ipv4Addr;
IPv6Addr ipv6Addr;
} IPAddrUnion;
typedef struct IPAddrContainer {
IPAddrUnion addr;
uint16 ver; /* IPv4 or IPv6 (uint16 pads better). */
} IPAddrContainer;
typedef struct IPmacLookupEntry {
struct IPmacLookupEntry *ipNext; // pointer to next item in bucket in IP hash table
IPAddrContainer addrContainer; // Struct holding the v4/v6 address
uint8 mac[ETH_ALEN]; // ethernet MAC address
SmacLastAccess lastAccess; // estimated time of entry's last use
} IPmacLookupEntry;
/*
* EthernetHeader: struct that corresponds with common ethernet header
* (an ethernet frame that contians a VLAN header has different
* format: 2 additional bytes after srcAddr)
*/
typedef struct EthernetHeader {
uint8 destAddr[ETH_ALEN]; // destination MAC
uint8 srcAddr[ETH_ALEN]; // source MAC
uint16 lengthType; // length/type field
} EthernetHeader;
/*
* EthClass: used to classify the various ethernet media types
* into a small group of classes.
*/
typedef enum {
EthClassIllegal = 0x345, // media type in an unrefined/reserved range
EthClassCommon, // means known but no special handling needed
EthClassUncommon, // like common, but should trigger more debug printouts
EthClassUnknown, // not specifically known/handled), but a legal type
EthClassIPv4, // IPv4 type
EthClassARP, // one of the various ARP protocols
EthClassVLAN, // VLAN type
EthClassIPv6, // IPv6 type
EthClassEAPOL, // 802.1x type (EAPOL)
} EthClass;
/*
* SMACState: encapsulates all wireless state for a specific host adapter
*/
#define SMAC_HASH_TABLE_SIZE 256 // length of table, must be power of 2
#define SMAC_HASH_MASK (SMAC_HASH_TABLE_SIZE - 1) // hash bits
typedef struct SMACState {
#ifdef _WIN32
NDIS_SPIN_LOCK smacSpinLock; // spinlock that protects wireless state
#else /* _WIN32 */
void *smacSpinLock; // spinlock that protects wireless state
#endif /* _WIN32 */
SmacLastAccess lastUptimeRead; // used to track uptime counter overflow
struct IPmacLookupEntry * IPlookupTable[SMAC_HASH_TABLE_SIZE]; // IP hash table IP->MAC
uint32 numberOfIPandMACEntries; // # of hash table entries
IPAddrContainer lastIPadded; // last IP added to hash
uint8 lastMACadded[ETH_ALEN]; // last MAC added to hash
struct IPmacLookupEntry * lastEntryAdded; // ptr to cache entry (to update timestamp)
Bool smacForwardUnknownPackets; // forward "all" packets? (typically doesn't)
uint8 macAddress[ETH_ALEN]; // pointer to host MAC address
} SMACState;
/*
* Function prototypes
*/
static INLINE uint32 SUM32(uint32 in);
static uint32 CalcChecksumDiff(uint32 sumBefore, uint32 sumAfter);
static uint16 UpdateSum(uint16 oricheck, uint32 sumDiff);
static INLINE IPmacLookupEntry *
LookupByIPNoAcquireLock(SMACState *state,
const IPAddrContainer *addrContainer);
static Bool LookupByIP(SMACState *state, const IPAddrContainer *addrContainer,
uint8 *macAddress);
static INLINE Bool LookupByIPv4(SMACState *state, uint32 ipv4Addr,
uint8 *macAddress);
static INLINE Bool RemoveIPfromHashTableNoAcquireLock(SMACState *state,
IPmacLookupEntry *entryToRemove);
static void TrimLookupTableIfNecessary(SMACState *state);
static INLINE void SetCacheEntry(SMACState *state, IPmacLookupEntry *entry);
static Bool AddIPandMACcombo(SMACState *state,
const IPAddrContainer *addrContainer, uint8 *mac);
static INLINE Bool AddIPv4andMACcombo(SMACState *state, uint32 ipv4Addr,
uint8 *mac);
static INLINE Bool AddIPv6andMACcombo(SMACState *state,
const IPv6Addr *ipv6Addr, uint8 *mac);
static void ProcessOutgoingIPv4Packet(SMACPacket *packet, uint32 ethHeaderLen);
static Bool ProcessOutgoingIPv6Packet(SMACPacket *packet, uint32 ethHeaderLen,
const uint8 *smacAddress, Bool *toHost);
static Bool
PatchMacAddrFixChecksum(SMACPacket *packet, const uint32 packetLen,
const uint32 checksumOffset,
const uint32 patchMacOffset, const uint8 *macAddress,
const char *logPrefix);
#ifdef DBG
static void ProcessIncomingIPv4Packet(SMACPacket *packet,
Bool knownMacForIp);
#endif
static SmacLastAccess GetSystemUptime(SMACState *state);
/* get information from packet */
static INLINE uint32 GetPacketLength(SMACPacket *packet);
static Bool GetPacketData(SMACPacket *packet, uint32 offset,
uint32 length, void *data);
/* set information in packet */
static Bool SetPacketByte(SMACPacket *packet, uint32 offset,
uint8 data);
/* clone source / write data to clone */
static Bool ClonePacket(SMACPackets *packets);
static Bool CopyDataToClonedPacket(SMACPackets *packets, const void *source,
uint32 offset, uint32 length);
/* write data to source (on Windows) / write data to clone (on Linux) */
static Bool CopyDataForPacketFromHost(SMACPackets *packets, uint32 changeNum,
uint32 offset, const uint8 *macAddress);
static EthClass LookupTypeClass(uint16 typeValue);
#ifdef DBG
#ifdef _WIN32
_At_(type, _In_z_bytecount_(typeLen))
#endif
static void LookupTypeName(uint16 typeValue, char *type, size_t typeLen);
#endif
/*
* Various utility functions operating on IPv4/v6 addresses, address
* containers, or IP/MAC lookup entries.
*/
/*
*----------------------------------------------------------------------
*
* IPv4Hash --
* IPv6Hash --
*
* Returns a one byte hash of an IPv4 (IPv6) address by adding
* all the octets in the address.
*
* Results:
* One byte hash value.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE uint8
IPv4Hash(uint32 addr) // IN:
{
return ((addr >> 24 & 0xff) + (addr >> 16 & 0xff) + (addr >> 8 & 0xff) +
(addr & 0xff)) & SMAC_HASH_MASK;
}
static INLINE uint8
IPv6Hash(const IPv6Addr *addr) // IN:
{
int i;
uint8 hash = (uint8)(addr->addrLo & 0xff) + (uint8)(addr->addrHi & 0xff);
for (i = 1; i < 4; i++) {
hash += (uint8)(addr->addrLo >> (i * 8) & 0xff) +
(uint8)(addr->addrHi >> (i * 8) & 0xff);
}
return hash & SMAC_HASH_MASK;
}
/*
*----------------------------------------------------------------------
*
* IsIPAddrContainerV4 --
* IsIPAddrContainerV6 --
* IsLookupEntryV4 --
* IsLookupEntryV6 --
*
* Checks if an IP address container (lookup entry) is of type v4 (v6).
*
* Results:
* TRUE iff of type v4 (v6).
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
IsIPAddrContainerV4(const IPAddrContainer *addrContainer) // IN:
{
return addrContainer->ver == IPv4;
}
static INLINE Bool
IsIPAddrContainerV6(const IPAddrContainer *addrContainer) // IN:
{
return addrContainer->ver == IPv6;
}
static INLINE Bool
IsLookupEntryV4(const IPmacLookupEntry *entry) // IN:
{
return IsIPAddrContainerV4(&entry->addrContainer);
}
static INLINE Bool
IsLookupEntryV6(const IPmacLookupEntry *entry) // IN:
{
return IsIPAddrContainerV6(&entry->addrContainer);
}
/*
*----------------------------------------------------------------------
*
* Container{Get,Set}IPv4Addr --
* Container{Get,Set}IPv6Addr --
* LookupEntry{Get,Set}IPv4Addr --
* LookupEntry{Get,Set}IPv4Addr --
*
* Returns/sets an IPv4 (IPv6) address from/to an IP address container
* (IP/MAC lookup entry) structure. Assumes that the caller has checked
* the version type of the address prior to calling the Get function.
*
* Results:
* IP address (for the Get function), none for the Set function.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE uint32
ContainerGetIPv4Addr(const IPAddrContainer *addrContainer) // IN:
{
ASSERT(IsIPAddrContainerV4(addrContainer));
return addrContainer->addr.ipv4Addr;
}
static INLINE uint32
LookupEntryGetIPv4Addr(const IPmacLookupEntry *entry) // IN:
{
ASSERT(IsLookupEntryV4(entry));
return ContainerGetIPv4Addr(&entry->addrContainer);
}
static INLINE void
ContainerSetIPv4Addr(IPAddrContainer *addrContainer, // IN:
uint32 ipv4Addr) // IN:
{
addrContainer->ver = IPv4;
addrContainer->addr.ipv4Addr = ipv4Addr;
}
static INLINE void
LookupEntrySetIPv4Addr(IPmacLookupEntry *entry, // IN:
uint32 ipv4Addr) // IN:
{
ContainerSetIPv4Addr(&entry->addrContainer, ipv4Addr);
}
static INLINE const IPv6Addr *
ContainerGetIPv6Addr(const IPAddrContainer *addrContainer) // IN:
{
ASSERT(IsIPAddrContainerV6(addrContainer));
return &addrContainer->addr.ipv6Addr;
}
static INLINE const IPv6Addr *
LookupEntryGetIPv6Addr(const IPmacLookupEntry *entry) // IN:
{
ASSERT(IsLookupEntryV6(entry));
return ContainerGetIPv6Addr(&entry->addrContainer);
}
static INLINE void
ContainerSetIPv6Addr(IPAddrContainer *addrContainer, // IN:
const IPv6Addr *ipv6Addr) // IN:
{
addrContainer->ver = IPv6;
addrContainer->addr.ipv6Addr.addrHi = ipv6Addr->addrHi;
addrContainer->addr.ipv6Addr.addrLo = ipv6Addr->addrLo;
}
static INLINE void
LookupEntrySetIPv6Addr(IPmacLookupEntry *entry, // IN:
const IPv6Addr *ipv6Addr) // IN:
{
ContainerSetIPv6Addr(&entry->addrContainer, ipv6Addr);
}
/*
*----------------------------------------------------------------------
*
* LookupEntrySetIPAddrContainer --
*
* Copies an IP address container to an IP/MAC lookup entry.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE void
LookupEntrySetIPAddrContainer(IPmacLookupEntry *entry, // OUT:
const IPAddrContainer *addrContainer) // IN:
{
if (IsIPAddrContainerV4(addrContainer)) {
LookupEntrySetIPv4Addr(entry, ContainerGetIPv4Addr(addrContainer));
} else {
LookupEntrySetIPv6Addr(entry, ContainerGetIPv6Addr(addrContainer));
}
}
/*
*----------------------------------------------------------------------
*
* IPAddrContainerHash --
* LookupEntryIPAddrHash --
*
* Return the hash of a given IP address container (IP/MAC lookup
* entry) structure.
*
* Results:
* One byte hash of the IP address.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE uint8
IPAddrContainerHash(const IPAddrContainer *addrContainer) // IN:
{
return IsIPAddrContainerV4(addrContainer) ?
IPv4Hash(ContainerGetIPv4Addr(addrContainer)) :
IPv6Hash(ContainerGetIPv6Addr(addrContainer));
}
static INLINE uint8
LookupEntryIPAddrHash(const IPmacLookupEntry *entry) // IN:
{
return IPAddrContainerHash(&entry->addrContainer);
}
/*
*----------------------------------------------------------------------
*
* IPv6AddrEquals --
*
* Checks if two given IPv6 addresses match.
*
* Results:
* TRUE iff the addresses match.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
IPv6AddrEquals(const IPv6Addr *addr1, // IN:
const IPv6Addr *addr2) // IN:
{
return addr1->addrHi == addr2->addrHi && addr1->addrLo == addr2->addrLo;
}
/*
*----------------------------------------------------------------------
*
* AddrContainersVersionsMatch --
*
* Checks if two given IP address containers are of the same version.
*
* Results:
* TRUE iff the versions match.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
AddrContainersVersionsMatch(const IPAddrContainer *one, // IN:
const IPAddrContainer *two) // IN:
{
return one->ver == two->ver;
}
/*
*----------------------------------------------------------------------
*
* AddrContainersMatch --
*
* Checks if two given IP address containers match (same version and
* same IP address).
*
* Results:
* TRUE iff the versions and the addresses match.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
AddrContainersMatch(const IPAddrContainer *one, // IN:
const IPAddrContainer *two) // IN:
{
if (AddrContainersVersionsMatch(one, two)) {
if (IsIPAddrContainerV4(one)) {
return ContainerGetIPv4Addr(one) == ContainerGetIPv4Addr(two);
} else {
return IPv6AddrEquals(&one->addr.ipv6Addr, &two->addr.ipv6Addr);
}
}
return FALSE;
}
/*
*----------------------------------------------------------------------
*
* AddrContainerMatchIPv4Address --
*
* Checks if a given IP address container matches a given IPv4 address.
*
* Results:
* TRUE iff the versions and the addresses match.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
AddrContainerMatchIPv4Address(const IPAddrContainer *addrContainer, // IN:
uint32 ipv4Addr) // IN:
{
IPAddrContainer ipv4AddrContainer;
ContainerSetIPv4Addr(&ipv4AddrContainer, ipv4Addr);
return AddrContainersMatch(addrContainer, &ipv4AddrContainer);
}
#ifdef DBG
/*
*----------------------------------------------------------------------
*
* ContainerPrintIPAddrToString --
* LookupEntryPrintIPAddrToString --
* PrintIPv4AddrToString --
* PrintIPv6AddrToString --
* PrintMACAddrToString
*
* Uses platform specific snprintf() to convert an IP address (or
* MAC address) to string format and copies it to a given output
* string.
*
* Results:
* Pointer to the given string.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE char *
ContainerPrintIPAddrToString(char *str, // OUT:
size_t size, // IN:
const IPAddrContainer *addrContainer) // IN:
{
if (IsIPAddrContainerV4(addrContainer)) {
uint32 addr = ContainerGetIPv4Addr(addrContainer);
SNPRINTF((str, size, "%u.%u.%u.%u", addr & 0xff, addr >> 8 & 0xff,
addr >> 16 & 0xff, addr >> 24 & 0xff));
} else {
const IPv6Addr *addr = ContainerGetIPv6Addr(addrContainer);
SNPRINTF((str, size, "%x:%x:%x:%x:%x:%x:%x:%x",
NTOHS((uint16)(addr->addrHi & 0xffff)),
NTOHS((uint16)(addr->addrHi >> 16 & 0xffff)),
NTOHS((uint16)(addr->addrHi >> 32 & 0xffff)),
NTOHS((uint16)(addr->addrHi >> 48 & 0xffff)),
NTOHS((uint16)(addr->addrLo & 0xffff)),
NTOHS((uint16)(addr->addrLo >> 16 & 0xffff)),
NTOHS((uint16)(addr->addrLo >> 32 & 0xffff)),
NTOHS((uint16)(addr->addrLo >> 48 & 0xffff))));
}
return str;
}
static INLINE char *
LookupEntryPrintIPAddrToString(char *str, // OUT:
size_t size, // IN:
const IPmacLookupEntry *entry) // IN:
{
return ContainerPrintIPAddrToString(str, size, &entry->addrContainer);
}
static INLINE void
PrintIPv4AddrToString(char *str, // OUT:
size_t size, // IN:
uint32 ipv4Addr) // IN:
{
IPAddrContainer addrContainer;
ContainerSetIPv4Addr(&addrContainer, ipv4Addr);
ContainerPrintIPAddrToString(str, size, &addrContainer);
}
static INLINE char *
PrintIPv6AddrToString(char *str, // OUT:
size_t size, // IN:
const IPv6Addr *ipv6Addr) // IN:
{
IPAddrContainer addrContainer;
ContainerSetIPv6Addr(&addrContainer, ipv6Addr);
return ContainerPrintIPAddrToString(str, size, &addrContainer);
}
static INLINE char *
PrintMACAddrToString(char *str, // OUT:
size_t size, // IN:
const uint8 *mac) // IN:
{
SNPRINTF((str, size, "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
return str;
}
#endif /* DBG */
/*
* IP hash table routines: the following routines pertain to operations
* on the IP hash table. SMACState->smacSpinLock should be held when reading or
* writing data in the hash table. A read/write lock might be better
* but the locks are usually held for a brief period of time.
*
* 'lastIPadded' and 'lastMACadded' are used to cache the last entry that
* was added to the hash table. For most packets we attempt to add IP/MAC
* information from that packet to the hash table. In most cases (especially
* during file transfers) the entry will already be added to the table so we
* cache the last addition to minimize overhead. The cache information is
* not used for lookups, only to make adds more efficient.
*/
/*
*----------------------------------------------------------------------
*
* LookupByIP --
* LookupByIPNoAcquireLock --
*
* Lookup entry or MAC address that corresponds to the
* specified IP address. Locking and non-locking versions
* are provided. The non-locking version returns the actual
* table entry, while the locking version only returns the
* actual MAC address (to avoid reference counting).
*
* Results:
* Nonlocking: pointer to entry (if found), otherwise NULL
* Locking: TRUE if MAC found, otherwise FALSE
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE IPmacLookupEntry *
LookupByIPNoAcquireLock(SMACState *state, // IN: state
const IPAddrContainer *addrContainer) // IN: v4/v6 addr
{
uint8 hash = IPAddrContainerHash(addrContainer);
IPmacLookupEntry *curr;
ASSERT(SMAC_HASH_TABLE_SIZE == 256);
/*
* Search thru bucket for match.
*/
for (curr = state->IPlookupTable[hash]; curr; curr = curr->ipNext) {
if (AddrContainersMatch(&curr->addrContainer, addrContainer)) {
break;
}
}
return curr;
}
static Bool
LookupByIP(SMACState *state, // IN: adapter
const IPAddrContainer *addrContainer, // IN: IP to lookup
uint8 *macAddress) // OUT: (optional) MAC of IP
// (uint8[ETH_ALEN])
{
IPmacLookupEntry *entry;
WW_DEVEL_ONLY(char ipStr[IP_STRING_SIZE];)
SPINLOCKINIT();
WW_VNETKdPrint((MODULE_NAME "LookupByIP: told to find %s\n",
ContainerPrintIPAddrToString(ipStr, sizeof ipStr,
addrContainer)));
ACQUIRESPINLOCK(&state->smacSpinLock);
entry = LookupByIPNoAcquireLock(state, addrContainer);
if (entry != NULL && macAddress != NULL) {
MEMCPY(macAddress, entry->mac, ETH_ALEN);
}
RELEASESPINLOCK(&state->smacSpinLock);
return entry != NULL;
}
/*
*----------------------------------------------------------------------
*
* LookupByIPv4 --
*
* Helper wrapper function for LookupByIP() for IPv4 cases.
*
* Results:
* As in LookupByIP().
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
LookupByIPv4(SMACState *state, // IN:
uint32 ipv4Addr, // IN:
uint8 *macAddress) // OUT:
{
IPAddrContainer addrContainer;
ContainerSetIPv4Addr(&addrContainer, ipv4Addr);
return LookupByIP(state, &addrContainer, macAddress);
}
/*
*----------------------------------------------------------------------
*
* RemoveIPfromHashTable --
* RemoveIPfromHashTableNoAcquireLock --
*
* Removed specified entry from the IP hash table. Function
* presumes that specified table entry still contains the IP
* address that was used to add the entry to the hash table.
* Locking and no-locking version of function are provided
*
* This function doesn't check whether the cached entry is
* being removed (and thus won't reset the cached entry). This
* code is primarily used to remove the oldest entry, and by
* definition the cached entry is the newest (i.e., it's never
* the oldest and thus won't be removed).
*
* Results:
* TRUE if entry removed, FALSE otherwise.
*
* Side effects:
* May remove entry from IP hash table. Actual entry is not
* modified nor deallocated by this function.
*
*----------------------------------------------------------------------
*/
static INLINE Bool
RemoveIPfromHashTableNoAcquireLock(SMACState *state, // IN: state
IPmacLookupEntry * entryToRemove) // IN: packet
{
uint8 ipHashToRemove;
IPmacLookupEntry * prev = NULL, *entry;
ASSERT(entryToRemove);
ASSERT(SMAC_HASH_TABLE_SIZE == 256);
ipHashToRemove = LookupEntryIPAddrHash(entryToRemove);
entry = state->IPlookupTable[ipHashToRemove]; // get bucket
/*
* locate and remove old IP entry from bucket
*/
while (entry) {
if (entry == entryToRemove) {
if (prev) {
W_VNETKdPrint((MODULE_NAME "RemoveIPfromHashTable: removed IP "
"entry from middle of bucket\n"));
prev->ipNext = entry->ipNext;
} else {
W_VNETKdPrint((MODULE_NAME "RemoveIPfromHashTable: removed IP "
"entry from front of bucket\n"));
state->IPlookupTable[ipHashToRemove] = entry->ipNext;
}
return TRUE;
} else {
prev = entry;
entry = entry->ipNext;
}
}
return FALSE;
}
/*
*----------------------------------------------------------------------
*
* TrimLookupTableIfNecessary --
*
* If the number of entries in the IP and MAC tables exceeds a
* specified value (currently 20), then we remove and deallocate
* an entry--ideally the entry which has been used least recently
* is removed. The code presumes that this function will be called
* anytime a new entry is added, thus we should never need to
* remove more than one entry per function call.
*
* Function presumes that state lock is held while this function
* is called.
*
* Results:
* None.
*
* Side effects:
* May remove and deallocates an entry from the IP and MAC hash
* tables.
*
*----------------------------------------------------------------------
*/
static void
TrimLookupTableIfNecessary(SMACState *state) // IN: smac state
{
IPmacLookupEntry * oldestEntry = NULL; // oldest entry found
SmacLastAccess oldestUpdate = (uint64)~0; // age of oldest entry
SmacLastAccess currentUptime = 0; // time since the system was booted
int i;
VNETKdPrintCall(("TrimLookupTableIfNecessary"));
ASSERT(state);
ASSERT(SMAC_HASH_TABLE_SIZE == 256);
ASSERTLOCKHELD();
if (state->numberOfIPandMACEntries <= 20) { // if not too many entries
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNeccessary: number of "
"entries is small: %u.\n", state->numberOfIPandMACEntries));
return;
}
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: "
"reducing # of entries\n"));
currentUptime = GetSystemUptime(state); /* must be called with lock held */
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: current uptime is "
LAST_ACCESS_FORMAT "\n", currentUptime));
/*
* NOTE: this code presumes that no system will ever be up long enough
* for the uptime to wrap. To get around this assumption we could try to
* determine "oldest" by computing which entry has the largest difference
* from the current updtime. However, there are no guarantees that this
* metric is any more accurate. Given that we never expect more than 20
* entries to ever exist, I won't implement a more sophisiticated
* mechanism at this time.
*/
/*
* Search thru entire table to find oldest uptime, and remove it
*/
for (i = 0; i < SMAC_HASH_TABLE_SIZE; ++i) {
IPmacLookupEntry *currentEntry = state->IPlookupTable[i];
while (currentEntry) {
if (currentEntry->lastAccess < oldestUpdate) { // if older than candidate
/*
* Skip cached entry since the entry was used most recently, but its
* timestamp might be old since the timestamp is only updated after
* the entry is no longer cached (i.e., something else is now the newest)
*
* This code will only be executed if at least 20 entries exist, so there
* must be some much better candidates elsewhere in the table.
*/
if (currentEntry == state->lastEntryAdded) {
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: oldest "
"candidate is the cached entry, so skipping\n"));
} else {
DEVEL_ONLY(char ipStr[IP_STRING_SIZE];)
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: current "
"oldest candidate: %s time " LAST_ACCESS_FORMAT "\n",
LookupEntryPrintIPAddrToString(ipStr, sizeof ipStr,
currentEntry),
currentEntry->lastAccess));
oldestEntry = currentEntry;
oldestUpdate = currentEntry->lastAccess;
}
}
if (currentEntry->lastAccess > currentUptime) {
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: "
"ERROR: last access " LAST_ACCESS_FORMAT
" > current uptime " LAST_ACCESS_FORMAT "\n",
currentEntry->lastAccess, currentUptime));
}
currentEntry = currentEntry->ipNext;
}
}
if (oldestEntry) {
DEVEL_ONLY(char ipStr[IP_STRING_SIZE];)
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: found oldest "
"candidate: %s time " LAST_ACCESS_FORMAT "\n",
LookupEntryPrintIPAddrToString(ipStr, sizeof ipStr,
oldestEntry),
oldestEntry->lastAccess));
if (!RemoveIPfromHashTableNoAcquireLock(state, oldestEntry)) {
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: could not "
"find entry in IP table\n"));
ASSERT(0); // should never occur
} else {
FREEMEMORY(oldestEntry);
--state->numberOfIPandMACEntries;
}
}
else {
VNETKdPrint((MODULE_NAME "TrimLookupTableIfNecessary: "
"found no entry to remove!!\n"));
}
VNETKdPrintReturn(("TrimLookupTableIfNecessary"));
return;
}
/*
*----------------------------------------------------------------------
*
* SetCacheEntry --
*
* Sets the cached MAC/IP entry for an adapter, and updates the
* access time for the previous cache entry (if any). The cache
* is used to avoid the overhead of checking for the existance of,
* for the purposes of adding, a MAC/IP entry that has already
* been added recently.
*
* Function is called with state->smacSpinLock held.
*
* Results:
* None.
*
* Side effects:
* Modified the cached entry for the adapter, and updates the
* access time for the previous cache entry (if any)
*
*----------------------------------------------------------------------
*/
static INLINE void
SetCacheEntry(SMACState *state, // IN: smac state
IPmacLookupEntry *entry) // IN: entry to cache
{
ASSERT(state);
ASSERT(entry);
/*
* Set new cached entry, but first set the current cache entry's
* access time to the current time
*/
if (state->lastEntryAdded) {
state->lastEntryAdded->lastAccess = GetSystemUptime(state);
}
state->lastIPadded = entry->addrContainer;
MEMCPY(state->lastMACadded, entry->mac, ETH_ALEN);
state->lastEntryAdded = entry;
entry->lastAccess = GetSystemUptime(state);
}
/*
*----------------------------------------------------------------------
*
* AddIPandMACcombo --
*
* Adds an entry for a paired MAC/IP{v4,v6} into the IP and MAC
* hash tables.
*
* Results:
* TRUE if added, updated, or already present, FALSE on error.
*
* Side effects:
* Allocates a new table entry and adds it to the IP and MAC
* hash tables.
*
*----------------------------------------------------------------------
*/
static Bool
AddIPandMACcombo(SMACState *state, // IN: smac state
const IPAddrContainer *addrContainer, // IN: IPv4/v6 address to add
uint8 *mac) // IN: ethernet MAC to add
{
Bool result = TRUE;
IPmacLookupEntry *entryIP = NULL;
DEVEL_ONLY(char ipStr[IP_STRING_SIZE];)
DEVEL_ONLY(char macStr[MAC_STRING_SIZE];)
SPINLOCKINIT();
/*
* If the current IP/MAC is the same as the immediately prior add, then
* return and don't bother to process this request.
*/
VNETKdPrint((MODULE_NAME "AddIPMAC: told to add %s %s\n",
ContainerPrintIPAddrToString(ipStr, sizeof ipStr,
addrContainer),
PrintMACAddrToString(macStr, sizeof macStr, mac)));
ASSERTLOCKHELD();
ASSERT(SMAC_HASH_TABLE_SIZE == 256);
if (AddrContainersMatch(&state->lastIPadded, addrContainer) &&
MAC_EQ(mac, state->lastMACadded)) {
VNETKdPrint((MODULE_NAME "AddIPMAC: cache says already present\n"));
return TRUE;
}
if (IsIPAddrContainerV4(addrContainer)) {
uint32 addr = ContainerGetIPv4Addr(addrContainer);
/*
* Don't allow an IP of 0.0.0.0 or 255.255.255.255 to be added. In fact,
* consider deleting any existing MAC entry that was provided, since the
* IP is apparently no longer in use.
*/
if (!addr || addr == IP_ADDR_BROADCAST) {
VNETKdPrint((MODULE_NAME "AddIPMAC: trying to add IP 0.0.0.0 or "
"255.255.255.255, disallowing add & removing MAC "
"entry.\n"));
return TRUE;
}
} else {
const IPv6Addr *addr = ContainerGetIPv6Addr(addrContainer);
/*
* Don't allow unspecified IPv6 addresses. There is no IPv6 broadcast
* address to check here.
*/
if (addr->addrHi == CONST64U(0) && addr->addrLo == CONST64U(0)) {
VNETKdPrint((MODULE_NAME "AddIPMAC: trying to add unspecified IPv6 "
"address ::/128, disallowing adding of MAC entry.\n"));
return TRUE;
}
}
ACQUIRESPINLOCK(&state->smacSpinLock);
/*
* Lookup table entry for specified IP addr and ethernet MAC.
*/
entryIP = LookupByIPNoAcquireLock(state, addrContainer);
/*
* If an entry for the specified IP addr was found, and the MACs match,
* then we don't need to add a new entry nor modify any existing entries
* and can return immediately.
*/
if (entryIP && MAC_EQ(entryIP->mac, mac)) {
VNETKdPrint((MODULE_NAME "AddIPMAC: entry already exists, "
"and matches current IP/MAC\n"));
/*
* Update the cached entry to this new request.
*/
SetCacheEntry(state, entryIP);
goto exit;
}
/*
* If no table entry was found for the IP, then this is a completely new add
* (also, no changes need to made to pre-existing table entries).
*/
if (!entryIP) {
uint8 ipHash = IPAddrContainerHash(addrContainer);
IPmacLookupEntry *entry = ALLOCATEMEMORY(sizeof *entry,
REORDER_TAG('SMle'));
VNETKdPrint((MODULE_NAME "AddIPMACnew: neither MAC or IP is in table, "
"so adding new entry for %s %s\n",
ContainerPrintIPAddrToString(ipStr, sizeof ipStr,
addrContainer),
PrintMACAddrToString(macStr, sizeof macStr, mac)));
if (!entry) {
// entry allocation error
VNETKdPrint((MODULE_NAME "AddIPMACnew: Failed to allocate "
" MAC/IP entry\n"));
result = FALSE;
goto exit;
}
++state->numberOfIPandMACEntries;
// initialize the contents of the table entry
LookupEntrySetIPAddrContainer(entry, addrContainer);
entry->lastAccess = 0; /* initialize to 0 for sanity, although it's not vital */
MEMCPY(entry->mac, mac, ETH_ALEN);
// add entry to IP hash table
entry->ipNext = state->IPlookupTable[ipHash];
state->IPlookupTable[ipHash] = entry;
VNETKdPrint((MODULE_NAME "AddIPMACnew: entry allocated, and added\n"));
SetCacheEntry(state, entry);
TrimLookupTableIfNecessary(state);
} else {
/*
* If table entry was found for IP, but the MACs don't match, then this means
* that a new/different ethernet device/MAC is using the IP address. We need
* to update the contents of the table entry to specify the new MAC
*/
VNETKdPrint((MODULE_NAME "AddIPMACmacmod: IP has changed from known "
"MAC %02x:%02x:%02x:%02x:%02x:%02x to new unknown "
"MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
entryIP->mac[0]&0xff, entryIP->mac[1]&0xff,
entryIP->mac[2]&0xff, entryIP->mac[3]&0xff,
entryIP->mac[4]&0xff, entryIP->mac[5]&0xff,
mac[0]&0xff, mac[1]&0xff, mac[2]&0xff, mac[3]&0xff,
mac[4]&0xff, mac[5]&0xff));
// update MAC in the table entry
MEMCPY(entryIP->mac, mac, ETH_ALEN);
// update which was the last IP/MAC combo to be added
SetCacheEntry(state, entryIP);
// no new entry added, so no need to call TrimLookupTableIfNecessary()
}
exit:
RELEASESPINLOCK(&state->smacSpinLock);
return result;
}
/*
*----------------------------------------------------------------------
*
* AddIPv4andMACcombo --
* AddIPv6andMACcombo --
*
* Helper wrapper functions for AddIPandMACcombo() for the IPv4/v6
* cases.
*
* Results:
* As in AddIPandMACcombo().
*
* Side effects:
* As in AddIPandMACcombo().
*
*----------------------------------------------------------------------
*/
static INLINE Bool
AddIPv4andMACcombo(SMACState *state, // IN:
uint32 ipv4Addr, // IN: IPv4 address to add
uint8 *mac) // IN: ethernet MAC to add
{
IPAddrContainer addrContainer;
ContainerSetIPv4Addr(&addrContainer, ipv4Addr);
return AddIPandMACcombo(state, &addrContainer, mac);
}
static INLINE Bool
AddIPv6andMACcombo(SMACState *state,
const IPv6Addr *ipv6Addr, // IN: IPv6 address to add
uint8 *mac) // IN: ethernet MAC to add
{
IPAddrContainer addrContainer;
ContainerSetIPv6Addr(&addrContainer, ipv6Addr);
return AddIPandMACcombo(state, &addrContainer, mac);
}
/*
* Overview of functions:
*
* "LookupTypeClass" and "LookupTypeName" are used to identify an
* ethernet frame's type, esentially IP, ARP, or neither.
* "LookupTypeName" is used for debugging purposes, since it also
* returns a string representing the name of the type.
*
* When the bridge wishes to send a packet to the host/network, it
* calls "SMAC_CheckPacketToHost", which essentially handles the
* link layer and ARP. If the packet is IPv4 then it calls
* ProcessOutgoingIPv4Packet to analyze (and potentially modify)
* the packet. Currently ProcessOutgoingIPv4Packet only turns on
* the broadcast flag for DHCP client packets.
*
* When the bridge receives a packet from the host/network, it
* calls "SMAC_CheckPacketFromHost", which essentially handles the
* link layer and ARP. If the packet is IPv4 then it calls
* ProcessIncomingIPv4Packet to analyze (and potentially modify)
* the packet.
*
* On Linux, the packet modifications are made to a private clone
* of the network packet. We don't want to modify a packet from
* the host, nor do we want to make modifications in a way that's
* visible to VMs on the same subnet.
*
* On Windows, we only clone packets that are headed towards the host.
* Packets from the host can be modified since they're already a private
* copy. However, we might not have the whole packet and thus need to
* store the changes in a separate table until the whole packet is
* received (at which time the modifications can then be made).
*
* On Mac OS, the packet modifications are made to the original network packet,
* since the packet passed to us is already pre-cloned and private and can be
* modified here. So cloning is faked by simply setting the clone to be a
* pointer to the original packet.
*/
/*
*----------------------------------------------------------------------
*
* SMAC_CheckPacketFromHost --
*
* Examines the contents of a packet that has been received from
* the network. On Windows the function will provide suggestions
* (via table) for where MAC substitution should occur.
* Function supports IP, ARP, RARP, IARP, and DHCP. On Linux the
* function will clone the packet and make modifications to the
* clone (the caller is responsible for freeing the original and
* the cloned packets).
*
* NOTE: On Windows this function presumes it is called at DISPATCH_LEVEL
*
* Results:
* Returns 'PacketStatusForwardPacket' if packet should/can be
* received, 'PacketStatusDropPacket' if packet should be filtered
* and not received, PacketStatusTooShort if insufficient data
* provided to process packet (suggested action it to receive
* packet in its entirety, and call this function
* again on the whole packet)
*
* Side effects:
* May add/modify the adapter's MAC/IP hash tables. May clone a
* packet if function returns PacketStatusForwardPacket.
*
*----------------------------------------------------------------------
*/
PacketStatus SMACINT
SMAC_CheckPacketFromHost(SMACState *state, // IN: pointer to state
SMACPackets *packets) // IN/OUT: packet to process
{
EthernetHeader eh;
EthClass typeClass;
SMACPacket *packet = NULL; // original packet from host
ASSERT(state);
ASSERT(packets);
WW_VNETKdPrint((MODULE_NAME "FromHost: Called\n"));
ASSERTLOCKHELD();
#ifdef _WIN32
{
MacReplacementTable *macTable = packets->table;
ASSERT(macTable);
macTable->numOfOffsets = 0;
}
#endif /* _WIN32 */
packet = &(packets->orig);
/*
* Read in the Ethernet header, and return failure if this
* is a runt packet that doesn't have a whole header
*/
ASSERT(sizeof eh == ETH_HLEN);
if (!GetPacketData(packet, 0, sizeof eh, &eh)) {
VNETKdPrint((MODULE_NAME "FromHost: Packet missing eth header\n"));
return PacketStatusDropPacket; // instruct bridge to drop this runt packet
}
#ifdef __linux__
/*
* Reject the duplicate packet (occurs only in Infrastructure mode)
*
* When the vm is communicating with the host, the host arp table
* would have the vm's mac address same as the physical hw address.
* so when SMAC_PacketFromHost is called it will create a duplicate of
* the packet and let the original pass as is, this original packet
* would then be transmitted on the network, where the AP would return
* it back to us because it matches the hardware address , this is the
* duplicate packet we are talking about.
*/
if (SMACL_IsSkbHostBound(packet->skb) &&
MAC_EQ(state->macAddress, eh.srcAddr)) {
W_VNETKdPrint((MODULE_NAME "FromHostIP: incoming request has "
"same mac as destination, so blackholing\n"));
return PacketStatusDropPacket;
}
#endif
/*
* Lookup the ethernet media type of the packet
*/
typeClass = LookupTypeClass(NTOHS(eh.lengthType));
/*
* For reference, the VLAN support was removed since it was determined that
* for Windows, such information would be present only in the OOB area of a
* NDIS packet, and thus no specific support/handling for VLAN is required
* for that OS.
*
* For any other OS, we need to add support to the vmnet driver for that OS
* to allow VLAN tagged frames.
*/
#if 0
/*
* If broadcast, then allow packet with no further checks. We might be
* able to actually enable this code, depending on how we want to handle
* DHCP replies. Currently DHCP replies don't need to be processed, so
* it's probably okay to enable this. Currently it's disabled so that
* the debug statements can continue to give us information about each
* packet.
*/
if (IS_MULTICAST(eh.destAddr) || IS_BROADCAST(eh.destAddr)) {
return PacketStatusForwardPacket;
}
#endif /* 0 */
/*
* DEBUG: if not IP & not ARP & not EAPOL
*/
if (typeClass != EthClassIPv4 && typeClass != EthClassIPv6 &&
typeClass != EthClassARP && typeClass != EthClassEAPOL) {
/*
* If not a common/known media type, then print a status
* message and return
*/
#ifdef DBG
if (typeClass != EthClassCommon) { // print only if not a common type
char type[50] = ""; // holds textual name of type
LookupTypeName(NTOHS(eh.lengthType), type, sizeof type); // lookup ethernet type
VNETKdPrint((MODULE_NAME "FromHost: non-IP & non-ARP %s "
"%02x:%02x:%02x:%02x:%02x:%02x -> "
"%02x:%02x:%02x:%02x:%02x:%02x %s\n",
(IS_MULTICAST(eh.destAddr) || IS_BROADCAST(eh.destAddr))?
"(b|m)cast":"ucast",
eh.srcAddr[0], eh.srcAddr[1], eh.srcAddr[2],
eh.srcAddr[3], eh.srcAddr[4], eh.srcAddr[5],
eh.destAddr[0], eh.destAddr[1], eh.destAddr[2],
eh.destAddr[3], eh.destAddr[4], eh.destAddr[5], type));
}
#endif /* DBG */
/*
* Let these unrecognized packets through only if they are broadcast or
* multicast. Drop unicast packets because it's easier to debug a lack
* of traffic than damaged traffic.
*/
if (IS_MULTICAST(eh.destAddr) || IS_BROADCAST(eh.destAddr)) {
VNETKdPrint((MODULE_NAME "FromHost: Forward unrecognized "
"non-arp/ip b/mcast\n"));
return PacketStatusForwardPacket;
} else {
#ifdef DBG
char type[50] = ""; // holds textual name of type
LookupTypeName(NTOHS(eh.lengthType), type, sizeof type);
VNETKdPrint((MODULE_NAME "FromHost: Dropping unrecognized "
"unicast non-IP & non-ARP unicast packet: %s\n", type));
VNETKdPrint((MODULE_NAME "FromHost: the non-IP & non-ARP is "
"%02x:%02x:%02x:%02x:%02x:%02x -> "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
eh.srcAddr[0], eh.srcAddr[1], eh.srcAddr[2],
eh.srcAddr[3], eh.srcAddr[4], eh.srcAddr[5],
eh.destAddr[0], eh.destAddr[1], eh.destAddr[2],
eh.destAddr[3], eh.destAddr[4], eh.destAddr[5]));
#endif
/*
* Drop non-IP/non-ARP unicast packets, unless we've been
* requested to forward unknown/unrecognized packets.
*/
if (state->smacForwardUnknownPackets) {
return PacketStatusForwardPacket;
} else {
return PacketStatusDropPacket;
}
}
}
/*
* If IP packet, then lookup ethernet MAC based on dest IP and replace
* dest ethernet MAC with that of the lookup table entry
*/
if (typeClass == EthClassIPv4 || typeClass == EthClassIPv6) { // IP packet
IPAddrContainer addrContainer;
/*
* First, we do some IP version dependent parsing (checking basic fields
* in the packet, extracting destination IP address, etc.). Then we
* perform the common processing for IPv4/IPv6 packets (finding
* destination MAC address from SMAC hash table, replacing destination
* MAC in the packet --- the wireless PNIC's MAC address --- with the MAC
* address of the VM's VNIC.
*/
if (typeClass == EthClassIPv4) { // IPv4
uint8 ipHeader[IP_HEADER_LEN]; // IP header
uint32 ipVer; // IP header ver
uint32 ipHeaderLen; // IP header length
/* Verify that we have at least a whole, minimal IP header */
if (!GetPacketData(packet, ETH_HLEN, sizeof ipHeader, ipHeader)) {
VNETKdPrint((MODULE_NAME "FromHostIP: Got type IP, "
"but only have partial IP header\n"));
return PacketStatusTooShort;
}
ipVer = ipHeader[0] >> 4; // IP header ver
ipHeaderLen = 4 * (ipHeader[0] & 0xf); // IP header length
/*
* Verify basic fields in IP header
*/
if (ipVer != 4 || ipHeaderLen < 20 ||
GetPacketLength(packet) - ETH_HLEN < ipHeaderLen) {
VNETKdPrint((MODULE_NAME "FromHostIP: got an IP version %u, "
"or len %u < reported len %u\n", ipVer,
GetPacketLength(packet) - ETH_HLEN, ipHeaderLen));
if ((GetPacketLength(packet) - ETH_HLEN) < ipHeaderLen) {
/*
* insufficient data -- process anyway since we have the necessary
* amount of information (first 20 bytes) to process this packet?
*/
VNETKdPrint((MODULE_NAME "FromHostIP: got an IP "
"that's too short\n"));
return PacketStatusTooShort;
} else {
VNETKdPrint((MODULE_NAME "FromHostIP: got an IP "
"that's unrecognised\n"));
return PacketStatusDropPacket; // invalid/unrecognized data
}
}
/*
* Extract the IPv4 destination address.
*/
ContainerSetIPv4Addr(&addrContainer,
*((uint32 *)(ipHeader +
IP_HEADER_DEST_ADDR_OFFSET)));
} else { // IPv6
uint8 ipv6Header[IPv6_HEADER_LEN];
uint8 ipv6Ver;
IPv6Addr ipv6DstAddr;
if (!GetPacketData(packet, ETH_HLEN, sizeof ipv6Header, ipv6Header)) {
VNETKdPrint((MODULE_NAME "FromHostIP: Got type IPv6, but only have "
"partial IPv6 header.\n"));
return PacketStatusTooShort;
}
ipv6Ver = ipv6Header[0] >> 4;
if (ipv6Ver != IPv6 || GetPacketLength(packet) < ETH_HLEN +
IPv6_HEADER_LEN) {
VNETKdPrint((MODULE_NAME "FromHostIP: got an IP version %u, or "
"length %u less than minimum length %d.\n", ipv6Ver,
GetPacketLength(packet), ETH_HLEN + IPv6_HEADER_LEN));
return PacketStatusDropPacket;
}
/*
* Extract the IPv6 destination address.
*/
ipv6DstAddr.addrHi = *((uint64 *)(ipv6Header +
IPv6_HEADER_DST_ADDR_OFFSET));
ipv6DstAddr.addrLo = *((uint64 *)(ipv6Header +
IPv6_HEADER_DST_ADDR_OFFSET +
sizeof ipv6DstAddr.addrHi));
ContainerSetIPv6Addr(&addrContainer, &ipv6DstAddr);
}
/*
* Broadcast/multicast processing: don't modify dest MAC but check if the
* payload should be modified.
*
* Unicast processing: modify MAC (if packet destined for VM) and check if
* the payload should be modified
*/
if (IS_MULTICAST(eh.destAddr) || IS_BROADCAST(eh.destAddr)) {
/*
* Check if payload should be modified.
*/
/*
* NOTE: ProcessIncomingIPv4Packet doesn't currently modify packets,
* so in an actual product release we can remove this call.
*
* TODO: investigate if there is work to be done here for IPv6 case.
*/
#ifdef DBG
if (typeClass == EthClassIPv4) {
ProcessIncomingIPv4Packet(packet, FALSE);
}
#endif
W_VNETKdPrint((MODULE_NAME "FromHostIP: forward b/mcast IP\n"));
return PacketStatusForwardPacket;
} else { // unicast ethernet
Bool foundMac;
uint8 vmMacAddress[ETH_ALEN];
DEVEL_ONLY(char ipStr[IP_STRING_SIZE];)
/*
* For unicast IP processing: lookup MAC based on IP addr
*/
foundMac = LookupByIP(state, &addrContainer, vmMacAddress);
if (!foundMac &&
AddrContainerMatchIPv4Address(&addrContainer, IP_ADDR_BROADCAST)) {
/*
* If IPv4 destination address is the IP limited broadcast address
* '255.255.255.255', then SMAC the unicast ethernet MAC address into
* the ethernet broadcast MAC address 'ff:ff:ff:ff:ff:ff' and forward
* the packet.
*
* Certain DHCP Servers/Relay Agents choose to ignore the
* recommendations of RFC 1542 "Clarifications and Extensions for the
* Bootstrap Protocol" section 4.1.2, and send DHCP Offers/ACKs to a
* unicast ethernet address in response to DHCP Discovers/Requests
* with the "Broadcast" flag set. See PRs 224129, 172947.
*
* Other than such packets, there shouldn't be any other IPv4 packets
* sent to the IP limited broadcast address but not to the ethernet
* broadcast address, but even if there are, we'll forward such
* packets onto the VNet and let the guests decide what to do about
* them.
*/
ASSERT(typeClass == EthClassIPv4);
MEMSET(vmMacAddress, 0xFF, ETH_ALEN);
foundMac = TRUE;
}
if (foundMac) { // if IP is known on VNet, then substitute MAC
W_VNETKdPrint((MODULE_NAME "FromHostIP: would assign MAC"
" of IP %s to packet\n",
ContainerPrintIPAddrToString(ipStr, sizeof ipStr,
&addrContainer)));
/*
* Eth dest MAC needs to be corrected.
*/
if (!CopyDataForPacketFromHost(packets, 0, 0, vmMacAddress)) {
VNETKdPrint((MODULE_NAME "FromHostIP: couldn't clone packet\n"));
return PacketStatusDropPacket;
}
/*
* NOTE: ProcessIncomingIPv4Packet doesn't currently modify packets,
* so in an actual product release we can remove this call.
*/
#ifdef DBG
if (typeClass == EthClassIPv4) {
ProcessIncomingIPv4Packet(packet, foundMac);
}
#endif /* DBG */
W_VNETKdPrint((MODULE_NAME "FromHostIP: forward IP\n"));
return PacketStatusForwardPacket;
} else { // IP is unknown on VNet
VNETKdPrint((MODULE_NAME "FromHostIP: could not find IP "
"%s in lookup.\n",
ContainerPrintIPAddrToString(ipStr, sizeof ipStr,
&addrContainer)));
if (state->smacForwardUnknownPackets) {
return PacketStatusForwardPacket;
} else {
return PacketStatusDropPacket; // drop packet
}
} // end IP addr lookup
} // end unicast
} // end IP
/*
* If ARP packet, then lookup ethernet MAC based on dest IP and replace
* dest ethernet MAC (and potentially ARP MAC) with that of the lookup table entry
*/
else if (typeClass == EthClassARP) { // ARP packet
uint32 arpHeaderWord1; // first word in ARP header
uint32 arpHeaderWord2; // second word in ARP header
if (GetPacketLength(packet) < ETH_HLEN + ARP_HEADER_LEN) {
VNETKdPrint((MODULE_NAME "FromHostARP: ARP packet is insufficient "
"length of IPv4 and Ethernet, expected %d got %u\n",
ETH_HLEN + ARP_HEADER_LEN, GetPacketLength(packet)));
return PacketStatusTooShort;
}
/*
* Verify the first word of ARP header (hardcoded for ethernet and IPv4)
*
* I recently added IEEE802 support. These types of ARP requests were
* observed on the company network, so someone uses them. As long as
* the address lengths are the same then I imagine that the processing
* is identical and we can handle them (lengths are checked as part
* of processing the second word of ARP header).
*/
if (!GetPacketData(packet, ETH_HLEN, sizeof arpHeaderWord1,
&arpHeaderWord1)) {
VNETKdPrint((MODULE_NAME "FromHostARP: couldn't read "
"ARP header #1\n"));
return PacketStatusTooShort;
}
if (arpHeaderWord1 != HTONL(0x00010800) /* ethernet */ &&
arpHeaderWord1 != HTONL(0x00060800) /* ieee802 */ ) {
VNETKdPrint((MODULE_NAME "FromHostARP: ARP header1 appears "
"wrong, got %08x\n", arpHeaderWord1));
return PacketStatusDropPacket;
}
/*
* Perform action based on opcode in second word of ARP header.
*/
if (!GetPacketData(packet, ETH_HLEN + sizeof arpHeaderWord1,
sizeof arpHeaderWord2, &arpHeaderWord2)) {
VNETKdPrint((MODULE_NAME "FromHostARP: couldn't read "
"ARP header #2\n"));
return PacketStatusTooShort;
}
#ifdef DBG
/*
* DEBUG: print general information about the packet
*/
switch (arpHeaderWord2) {
case CONST_NTOHL(0x06040001):
WW_VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates ARP request\n"));
break;
case CONST_NTOHL(0x06040002):
W_VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates ARP reply\n"));
break;
case CONST_NTOHL(0x06040003):
VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates RARP request\n"));
break;
case CONST_NTOHL(0x06040004):
VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates RARP reply\n"));
break;
case CONST_NTOHL(0x06040008):
VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates IARP request\n"));
break;
case CONST_NTOHL(0x06040009):
VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates IARP reply\n"));
break;
default:
VNETKdPrint((MODULE_NAME "FromHostARP: "
"ARP header2 indicates unknown opcode\n"));
break;
}
{
uint8 data[ETH_ALEN + 4] = {0}; // MAC and IP in one contiguous buffer
if (GetPacketData(packet, ETH_HLEN + ARP_SENDER_MAC_OFFSET,
sizeof data, data)) {
WW_VNETKdPrint((MODULE_NAME "FromHostARP: sender MAC is "
"%02x:%02x:%02x:%02x:%02x:%02x IP is %d.%d.%d.%d\n",
((unsigned char*)data)[0]&0xff, ((unsigned char*)data)[1]&0xff,
((unsigned char*)data)[2]&0xff, ((unsigned char*)data)[3]&0xff,
((unsigned char*)data)[4]&0xff, ((unsigned char*)data)[5]&0xff,
((unsigned char*)data)[6]&0xff, ((unsigned char*)data)[7]&0xff,
((unsigned char*)data)[8]&0xff, ((unsigned char*)data)[9]&0xff));
} else {
VNETKdPrint((MODULE_NAME "FromHostARP: couldn't read "
"sender MAC/IP\n"));
}
if (GetPacketData(packet, ETH_HLEN + ARP_TARGET_MAC_OFFSET,
sizeof data, data)) {
WW_VNETKdPrint((MODULE_NAME "FromHostARP: target MAC is "
"%02x:%02x:%02x:%02x:%02x:%02x IP is %d.%d.%d.%d\n",
((unsigned char*)data)[0]&0xff, ((unsigned char*)data)[1]&0xff,
((unsigned char*)data)[2]&0xff, ((unsigned char*)data)[3]&0xff,
((unsigned char*)data)[4]&0xff, ((unsigned char*)data)[5]&0xff,
((unsigned char*)data)[6]&0xff, ((unsigned char*)data)[7]&0xff,
((unsigned char*)data)[8]&0xff, ((unsigned char*)data)[9]&0xff));
} else {
VNETKdPrint((MODULE_NAME "FromHostARP: couldn't read "
"target MAC/IP\n"));
}
}
#endif
/*
* ARP handling for *incoming traffic*
*
* ARP: host wants to know the MAC that corresponds to a particular IP
* 1 ARP request: <srcMAC, srcIP, 0, dstIP>
* ALLOW CONDITIONALLY: if dest eth mac broadcast then nothing to modify,
* otherwise need to patch ucast dest eth mac.
* 2 ARP reply: <srcMAC, srcIP, dstMAC, dstIP>
* ALLOW IF BROADCAST: broadcast so nothing to modify (except ARP dest MAC?)
* ALLOW IF LOOKUP: lookup dstIP and mofify dstMAC (&ethDestMAC) to match VM
*
* RARP: host knows its MAC and wants to find out which IP it is assigned
* 3 RARP request: <srcMAC, 0 , srcMAC, 0 > can't store <MAC,IP>,
* ALLOW CONDITIONALLY: if dest eth mac broadcast then nothing to modify,
* otherwise need to patch ucast dest eth mac.
* 4 RARP reply: <srcMAC, srcIP, dstMAC, dstIP>
* ALLOW IF BROADCAST: broadcast so nothing to modify (except ARP dest MAC?)
* ALLOW IF LOOKUP: lookup dstIP and modify dstMAC (&ethDestMAC) to match VM
*
* IARP: host knows a peer's MAC and wants to determine its IP address
* 8 IARP request: <srcMAC, srcIP, dstMAC, 0 >
* ALLOW CONDITIONALLY: if dest eth mac broadcast then nothing to modify,
* otherwise need to patch ucast dest eth mac.
* 9 IARP reply: <srcMAC, srcIP, dstMAC, dstIP>
* ALLOW IF BROADCAST: broadcast so nothing to modify (except ARP dest MAC?)
* ALLOW IF LOOKUP: lookup dstIP and modify dstMAC (&ethDestMAC) to match VM
*/
if (arpHeaderWord2 == HTONL(0x06040001) || // If ARP request
arpHeaderWord2 == HTONL(0x06040003) || // If RARP request
arpHeaderWord2 == HTONL(0x06040008)) { // If IARP request
uint32 targetAddr;
uint32 sourceAddr;
Bool foundIP;
if (!GetPacketData(packet, ETH_HLEN + ARP_TARGET_IP_OFFSET,
sizeof targetAddr, &targetAddr) ||
!GetPacketData(packet, ETH_HLEN + ARP_SENDER_IP_OFFSET,
sizeof sourceAddr, &sourceAddr)) {
VNETKdPrint((MODULE_NAME "FromHostARP: "
"couldn't read target and/or sourceAddr\n"));
return PacketStatusTooShort;
}
/*
* Some host configurations will require allowing packet loopback, meaning
* that we'll see the packets that we're transmitting. Since we're performing
* MAC tricks, the guest could see its own ARP request--this echoed ARP request
* will contain the host's wireless MAC, and thus the guest OS might think that
* there is a address conflict. Thus, if we receive an ARP request whose source
* IP address is a known VM (or 0.0.0.0, as commonly used by Vista-and-later),
* and the MAC is the wireless hardware, then the ARP request is dropped.
* This will allow address conflicts to be detected when a conflict
* exists with another device, but it prevents conflict detection when
* the conflict is present on/within the host.
*/
foundIP = sourceAddr == 0 || LookupByIPv4(state, sourceAddr, NULL);
if (foundIP) { // if known IP is contained within request
uint8 packetMac[ETH_ALEN];
VNETKdPrint((MODULE_NAME "FromHostARP: observed a "
"incoming request from an IP we know about\n"));
if (!GetPacketData(packet, ETH_HLEN + ARP_SENDER_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME "FromHostARP: "
"couldn't read MAC\n"));
return PacketStatusTooShort;
}
/*
* Check if the sender MAC is using the wireless MAC
*/
if (MAC_EQ(state->macAddress, packetMac)) {
/*
* Sender is using wireless MAC, so drop packet
* -- the reasons for this are stated above in the large comment block.
*/
VNETKdPrint((MODULE_NAME "FromHostARP: incoming request using "
"wireless hardware addr, so blackholing\n"));
return PacketStatusDropPacket;
}
/*
* Requester IP matches a VM IP, but the source MAC is different.
* This is apparently a true conflict with another network peer, so
* we'll forward the packet
*/
VNETKdPrint((MODULE_NAME "FromHostARP: incoming request using "
"non-wireless-hardware-addr, may conflict--allowing\n"));
} else {
/*
* We don't have information about the source (typical case),
* so forward the packet.
*/
WW_VNETKdPrint((MODULE_NAME "FromHostARP: got ARP, no record of "
"source so forward\n"));
}
if (IS_MULTICAST(eh.destAddr) || IS_BROADCAST(eh.destAddr)) {
/* dest mac is broacast, so can just forward */
return PacketStatusForwardPacket;
} else if (!MAC_EQ(state->macAddress, eh.destAddr)) {
VNETKdPrint((MODULE_NAME "FromHostARP: incoming request using "
"non-wireless-hardware-addr eth dest MAC, dropping\n"));
return PacketStatusDropPacket;
} else {
Bool foundMac;
uint8 vmMacAddress[ETH_ALEN];
/*
* Someone sent request to VM with the host's destination MAC,
* so need to patch this with the VM's MAC so the VM can reply.
*/
/*
* Unicast destination--lookup MAC in table. If entry exists then
* do MAC replacement and return. Otherwise, drop the packet
*/
foundMac = LookupByIPv4(state, targetAddr, vmMacAddress);
if (!foundMac) {
W_VNETKdPrint((MODULE_NAME "FromHostARP: target IP in request ARP was"
" NOT found in table, presuming for another peer\n"));
return PacketStatusDropPacket;
}
VNETKdPrint((MODULE_NAME "FromHostARP: target IP in "
"request ARP was found in table\n"));
#ifdef DBG
{
uint8 packetMac[ETH_ALEN];
if (!GetPacketData(packet, ETH_HLEN + ARP_TARGET_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME "FromHostARP: "
"couldn't read MAC\n"));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME "FromHostARP: will modify request "
"ARP ETH MAC %02x.%02x.%02x.%02x.%02x.%02x "
"dest address and ARP target address "
"%02x.%02x.%02x.%02x.%02x.%02x to "
"match VM %02x.%02x.%02x.%02x.%02x.%02x\n",
eh.destAddr[0], eh.destAddr[1], eh.destAddr[2],
eh.destAddr[3], eh.destAddr[4], eh.destAddr[5],
packetMac[0], packetMac[1], packetMac[2],
packetMac[3], packetMac[4], packetMac[5],
vmMacAddress[0], vmMacAddress[1], vmMacAddress[2],
vmMacAddress[3], vmMacAddress[4], vmMacAddress[5]));
}
#endif
/*
* Copy in the MAC that will be written to eth dest MAC.
*/
if (!CopyDataForPacketFromHost(packets, 0, 0, vmMacAddress)) {
W_VNETKdPrint((MODULE_NAME "FromHostIP: couldn't clone request packet\n"));
return PacketStatusDropPacket;
}
return PacketStatusForwardPacket;
}
}
if (arpHeaderWord2 == HTONL(0x06040002) || // If ARP reply
arpHeaderWord2 == HTONL(0x06040004) || // If RARP reply
arpHeaderWord2 == HTONL(0x06040009)) { // If IARP reply
uint32 targetAddr;
Bool foundMac;
uint8 vmMacAddress[ETH_ALEN];
if (!GetPacketData(packet, ETH_HLEN + ARP_TARGET_IP_OFFSET,
sizeof targetAddr, &targetAddr)) {
VNETKdPrint((MODULE_NAME "FromHostARP: couldn't target IP\n"));
return PacketStatusTooShort;
}
/*
* If broadcast then return because we don't need to modify anything
*/
if (IS_MULTICAST(eh.destAddr) || IS_BROADCAST(eh.destAddr)) {
VNETKdPrint((MODULE_NAME "FromHostARP: observed a "
"broadcast ARP, RARP, or IARP reply packet\n"));
/*
* Don't need to make changes since target MAC is the broadcast addr
*/
/*
* Ethernet MAC is broadcast, but the dest ARP address might
* be unicast (in fact, it likely is), so we should patch the
* destination ARP MAC address??
*/
foundMac = LookupByIPv4(state, targetAddr, vmMacAddress);
if (foundMac) {
uint8 packetMac[ETH_ALEN];
VNETKdPrint((MODULE_NAME "FromHostARP: target IP in "
"broadcast reply ARP was found in table\n"));
if (!GetPacketData(packet, ETH_HLEN + ARP_TARGET_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME "FromHostARP: "
"couldn't read MAC\n"));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME "FromHostARP: will modify reply "
"ARP target address %02x.%02x.%02x.%02x.%02x.%02x to "
"match VM %02x.%02x.%02x.%02x.%02x.%02x\n",
packetMac[0], packetMac[1], packetMac[2],
packetMac[3], packetMac[4], packetMac[5],
vmMacAddress[0], vmMacAddress[1], vmMacAddress[2],
vmMacAddress[3], vmMacAddress[4], vmMacAddress[5]));
/*
* Copy in the MAC that will be written to ARP dest MAC
*/
if (!CopyDataForPacketFromHost(packets, 0,
ETH_HLEN + ARP_TARGET_MAC_OFFSET,
vmMacAddress)) {
VNETKdPrint((MODULE_NAME "FromHostIP: couldn't "
"clone packet\n"));
return PacketStatusDropPacket;
}
return PacketStatusForwardPacket;
} else {
W_VNETKdPrint((MODULE_NAME "FromHostARP: target IP in broadcast "
"reply ARP was NOT found in table\n"));
return PacketStatusForwardPacket;
}
}
W_VNETKdPrint((MODULE_NAME "FromHostARP: target IP is %d.%d.%d.%d\n",
(targetAddr)&0xff, (targetAddr>>8)&0xff,
(targetAddr>>16)&0xff, (targetAddr>>24)&0xff));
#ifdef DBG
{
uint32 senderAddr;
if (!GetPacketData(packet, ETH_HLEN + ARP_SENDER_IP_OFFSET,
sizeof senderAddr, &senderAddr)) {
VNETKdPrint((MODULE_NAME "FromHostARP: couldn't "
"read IP addr\n"));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME "FromHostARP: sender IP %d.%d.%d.%d\n",
(senderAddr&0xff), (senderAddr>>8)&0xff,
(senderAddr>>16)&0xff, (senderAddr>>24)&0xff));
}
#endif
/*
* Unicast destination--lookup MAC in table. If entry exists then
* do MAC replacement and return. Otherwise, drop the packet
*/
// see if the reply is for a host that we're aware of, and modify as necessary
foundMac = LookupByIPv4(state, targetAddr, vmMacAddress);
if (foundMac) {
VNETKdPrint((MODULE_NAME "FromHostARP: target IP in "
"reply ARP was found in table\n"));
#ifdef DBG
{
uint8 packetMac[ETH_ALEN];
if (!GetPacketData(packet, ETH_HLEN + ARP_TARGET_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME "FromHostARP: "
"couldn't read MAC\n"));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME "FromHostARP: will modify reply "
"ARP ETH MAC %02x.%02x.%02x.%02x.%02x.%02x "
"dest address and ARP target address "
"%02x.%02x.%02x.%02x.%02x.%02x to "
"match VM %02x.%02x.%02x.%02x.%02x.%02x\n",
eh.destAddr[0], eh.destAddr[1], eh.destAddr[2],
eh.destAddr[3], eh.destAddr[4], eh.destAddr[5],
packetMac[0], packetMac[1], packetMac[2],
packetMac[3], packetMac[4], packetMac[5],
vmMacAddress[0], vmMacAddress[1], vmMacAddress[2],
vmMacAddress[3], vmMacAddress[4], vmMacAddress[5]));
}
#endif
/*
* Copy in the MAC that will be written to eth
* dest MAC and ARP dest MAC
*/
if (!CopyDataForPacketFromHost(packets, 0, 0, vmMacAddress)) {
W_VNETKdPrint((MODULE_NAME "FromHostIP: couldn't "
"clone packet\n"));
return PacketStatusDropPacket;
}
if (!CopyDataForPacketFromHost(packets, 1,
ETH_HLEN + ARP_TARGET_MAC_OFFSET,
vmMacAddress)) {
W_VNETKdPrint((MODULE_NAME "FromHostIP: "
"couldn't clone packet #2\n"));
return PacketStatusDropPacket;
}
return PacketStatusForwardPacket;
} else {
W_VNETKdPrint((MODULE_NAME "FromHostARP: target IP in reply ARP was"
" NOT found in table, presuming for another peer\n"));
return PacketStatusDropPacket;
}
}
VNETKdPrint((MODULE_NAME "FromHostARP: unrecognized ARP type %08x\n",
arpHeaderWord2));
return PacketStatusDropPacket;
} else { // if EAPOL packet: typeClass == EthClassEAPOL
/*
* Allow incoming EAPOL packets to proceed unmolested provided the
* destination address matches the hardware address.
*/
if (!MAC_EQ(state->macAddress, eh.destAddr)) {
VNETKdPrint((MODULE_NAME "FromHostEAPOL: incoming request using "
"non-wireless-hardware-addr eth dest MAC, dropping\n"));
return PacketStatusDropPacket;
}
return PacketStatusForwardPacket;
}
}
/*
*----------------------------------------------------------------------
*
* SMAC_CheckPacketToHost --
*
* Modifies packet contents to be suitable for transmission over
* a wireless network. Function supports IP, ARP, RARP, IARP.
* Function will support DHCP in the future. This function clones
* the source packet, modifies the clone packet in situations
* where the packet should be forwarded to the host.
*
* NOTE: On Windows this function presumes it is called at DISPATCH_LEVEL
*
* Results:
* Returns 'PacketStatusTooShort' if insufficient data to process
* packet (suggest packet drop), 'PacketStatusDropPacket' if the
* packet should be dropped, or 'PacketStatusForwardPacket' if
* the packet should be forwarded.
*
* Side effects:
* Modifies contents of network packet, if nessary.
*
*----------------------------------------------------------------------
*/
PacketStatus SMACINT
SMAC_CheckPacketToHost(SMACState *state, // IN: pointer to state
SMACPackets *packets) // IN/OUT: packet to process; cloned/modified packet
{
uint8 buf[sizeof(EthernetHeader) + 4]; /* allocate extra 4 for VLAN headers */
EthernetHeader *eh = (EthernetHeader*)buf;
uint32 ethHeaderLen = ETH_HLEN; // tracks length of ethernet header
SMACPacket *packet = NULL;
EthClass typeClass; // classification of ethernet frame's type
ASSERT(state);
W_VNETKdPrint((MODULE_NAME "ToHost: Called\n"));
ASSERTLOCKHELD();
ASSERT(sizeof(EthernetHeader) == ETH_HLEN);
packet = &(packets->orig);
ASSERT(packet);
/*
* Verify that we have at least a minimal packet
*/
if (!GetPacketData(packet, 0, ethHeaderLen, eh)) {
VNETKdPrint((MODULE_NAME "ToHost: Packet missing eth header\n"));
return PacketStatusTooShort;
}
/*
* Lookup the ethernet media type of the packet
*/
typeClass = LookupTypeClass(NTOHS(eh->lengthType));
/*
* If packet type is a VLAN header, then adjust pointers and
* decode the "real" media type which follows the VLAN header
*/
if (typeClass == EthClassVLAN) {
uint16 actualType = 0;
ethHeaderLen +=4;
if (!GetPacketData(packet, 0, ethHeaderLen, eh)) {
VNETKdPrint((MODULE_NAME " ToHost: Packet is using VLAN header, "
"but supplied header is too small\n"));
return PacketStatusTooShort;
}
actualType = *(uint16*)(((uint8*)&(eh->lengthType)) + 4);
VNETKdPrint((MODULE_NAME " ToHost: Taking special measures for VLAN\n"));
typeClass = LookupTypeClass(NTOHS(actualType));
}
/*
* If the packet is not type IP, ARP or EAPOL, then drop the packet
* unless it is a broadcast packet (broadcast packets don't
* require much manipulating, so they should be safe to let thru).
*/
if (typeClass != EthClassIPv4 && typeClass != EthClassIPv6 &&
typeClass != EthClassARP && typeClass != EthClassEAPOL) {
/*
* DEBUG: if not a common/known media type, then print a status message
*/
#ifdef DBG
if (typeClass != EthClassCommon) {
char type[50] = ""; // holds textual name of type
if (ethHeaderLen == ETH_HLEN) { // if not a vlan packet
LookupTypeName(NTOHS(eh->lengthType), type, sizeof type);
} else { // if a vlan packet
uint16 actualType = *(uint16*)(((uint8*)&(eh->lengthType)) + 4);
LookupTypeName(NTOHS(actualType), type, sizeof type);
}
VNETKdPrint((MODULE_NAME " ToHost: non-IP & non-ARP "
"%02x:%02x:%02x:%02x:%02x:%02x -> "
"%02x:%02x:%02x:%02x:%02x:%02x %s\n",
eh->srcAddr[0]&0xff, eh->srcAddr[1]&0xff,
eh->srcAddr[2]&0xff, eh->srcAddr[3]&0xff,
eh->srcAddr[4]&0xff, eh->srcAddr[5]&0xff,
eh->destAddr[0]&0xff, eh->destAddr[1]&0xff,
eh->destAddr[2]&0xff, eh->destAddr[3]&0xff,
eh->destAddr[4]&0xff, eh->destAddr[5]&0xff, type));
}
#endif
/*
* Let these unrecognized packets through only if they are broadcast or
* multicast, and drop unicast packets (it's easier/debug loss of traffic
* versus corrupted/invalid traffic).
*/
if (IS_MULTICAST(eh->destAddr) || IS_BROADCAST(eh->destAddr)) {
/*
* Modify the source address to be that of the wireless hardware, so
* that this packet can be transmitted. First we need to duplicate
* the packet so that other VMs don't get confused about MACs that
* arbitrarily change between the VM's MAC and the host's MAC.
*/
if (!ClonePacket(packets)) {
VNETKdPrint((MODULE_NAME " ToHost: couldn't clone packet\n"));
return PacketStatusDropPacket;
}
CopyDataToClonedPacket(packets, state->macAddress,
ETH_ALEN /* offset for source MAC */,
ETH_ALEN /* length */);
return PacketStatusForwardPacket;
} else {
/*
* Drop the packet, but first display a status message indicating
* that we're dropping this packet (largely so we know what kind/type
* of traffic that we are dropping
*/
#ifdef DBG
char type[50] = ""; // holds textual name of type
if (ethHeaderLen == ETH_HLEN) { // if not a vlan packet
LookupTypeName(NTOHS(eh->lengthType), type, sizeof type);
} else { // if a vlan packet
uint16 actualType = *(uint16*)(((uint8*) & (eh->lengthType)) + 4);
LookupTypeName(NTOHS(actualType), type, sizeof type);
}
VNETKdPrint((MODULE_NAME " ToHost: Dropping unrecognized "
"unicast non-IP & non-ARP unicast packet: %s\n", type));
VNETKdPrint((MODULE_NAME " ToHost: the non-IP & non-ARP is "
"%02x:%02x:%02x:%02x:%02x:%02x -> "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
eh->srcAddr[0]&0xff, eh->srcAddr[1]&0xff,
eh->srcAddr[2]&0xff, eh->srcAddr[3]&0xff,
eh->srcAddr[4]&0xff, eh->srcAddr[5]&0xff,
eh->destAddr[0]&0xff, eh->destAddr[1]&0xff,
eh->destAddr[2]&0xff, eh->destAddr[3]&0xff,
eh->destAddr[4]&0xff, eh->destAddr[5]&0xff));
#endif
return PacketStatusDropPacket;
}
}
/*
* If IP packet, then store <source IP address, source MAC address> in lookup
* table and replace source ethernet MAC with that of the wireless hardware.
*/
if (typeClass == EthClassIPv4 || typeClass == EthClassIPv6) {
W_DEVEL_ONLY(char macStr1[MAC_STRING_SIZE];)
W_DEVEL_ONLY(char macStr2[MAC_STRING_SIZE];)
if (typeClass == EthClassIPv4) {
uint8 ipHeader[IP_HEADER_LEN];
uint32 ipVer; // version of IP header
uint32 ipHeaderLen; // reported length of IPv4 header
uint16 ipLen; // reported length of IPv4 packet
uint32 ipSrcAddr; // source address
/*
* Read in the IPv4 header.
*/
if (!GetPacketData(packet, ethHeaderLen, sizeof ipHeader, ipHeader)) {
VNETKdPrint((MODULE_NAME " ToHostIP: got an IP type, "
"but incomplete header\n"));
return PacketStatusTooShort;
}
ipVer = ipHeader[0] >> 4;
ipHeaderLen = 4 * (ipHeader[0]&0xf); // reported length of IP header
ipLen = NTOHS(*(uint16*)(ipHeader + 2)); // packet len
ipSrcAddr = *((uint32*)(ipHeader + IP_HEADER_SRC_ADDR_OFFSET));
/*
* Verify basic fields in IPv4 header.
*/
if (ipVer != IPv4 || ipHeaderLen < IP_HEADER_LEN ||
GetPacketLength(packet) < ethHeaderLen + ipHeaderLen) {
VNETKdPrint((MODULE_NAME "ToHostIP: IPv4 packet's ver = %u, "
"reported length = %u, reported length = %u.\n",
ipVer, GetPacketLength(packet),
ethHeaderLen + ipHeaderLen));
return PacketStatusDropPacket;
}
/*
* Store IPv4/MAC combo in lookup table, that way we can replace
* the MAC when any replies to this IP are received.
*/
if (!AddIPv4andMACcombo(state, ipSrcAddr, eh->srcAddr)) {
return PacketStatusDropPacket;
}
} else {
uint8 ipv6Header[IPv6_HEADER_LEN];
uint8 ipv6Ver;
IPv6Addr ipv6SrcAddr;
/*
* Read the IPv6 header.
*/
if (!GetPacketData(packet, ethHeaderLen, sizeof ipv6Header,
ipv6Header)) {
VNETKdPrint((MODULE_NAME "ToHostIP: got incomplete IPv6 "
"header.\n"));
return PacketStatusTooShort;
}
ipv6Ver = ipv6Header[0] >> 4;
if (ipv6Ver != IPv6 || GetPacketLength(packet) < ethHeaderLen +
IPv6_HEADER_LEN) {
VNETKdPrint((MODULE_NAME " ToHostIP: got an IP version %u, or "
"length %u less than minimum length %u.\n", ipv6Ver,
GetPacketLength(packet), ethHeaderLen +
IPv6_HEADER_LEN));
return PacketStatusDropPacket;
}
ipv6SrcAddr.addrHi = *((uint64 *)(ipv6Header +
IPv6_HEADER_SRC_ADDR_OFFSET));
ipv6SrcAddr.addrLo = *((uint64 *)(ipv6Header +
IPv6_HEADER_SRC_ADDR_OFFSET +
sizeof ipv6SrcAddr.addrHi));
/*
* Store IPv6/MAC combo in lookup table, that way we can replace
* the MAC when any replies to this IP are received.
*/
if (!AddIPv6andMACcombo(state, &ipv6SrcAddr, eh->srcAddr)) {
return PacketStatusDropPacket;
}
}
/*
* Now the code common to IPv4 and IPv6 cases start.
*
* First, replace source MAC with wireless hardware MAC.
*/
W_VNETKdPrint((MODULE_NAME " ToHostIP: modifying ETH MAC %s source "
"address to match wireless hardware MAC %s\n",
PrintMACAddrToString(macStr1, sizeof macStr1,
eh->srcAddr),
PrintMACAddrToString(macStr2, sizeof macStr2,
state->macAddress)));
/*
* Duplicate the packet so that other VMs don't get confused about
* MACs that arbitrarily change between the VM's MAC and the host's
* MAC.
*/
if (!ClonePacket(packets)) {
VNETKdPrint((MODULE_NAME " ToHostIP: couldn't clone packet\n"));
return PacketStatusDropPacket;
}
/*
* Make any necessary modifications to packet payload.
*/
if (typeClass == EthClassIPv4) {
ProcessOutgoingIPv4Packet(&packets->clone, ethHeaderLen);
} else {
Bool toHost = FALSE; /* Updated only on Windows Vista and later. */
if (!ProcessOutgoingIPv6Packet(&packets->clone, ethHeaderLen,
state->macAddress, &toHost)) {
VNETKdPrint((MODULE_NAME " ToHostIP: error in processing "
"outgoing IPv6 packet.\n"));
return PacketStatusDropPacket;
}
if (UNLIKELY(toHost)) {
/*
* Don't replace the ethernet source MAC address in the packet with
* state->macAddress.
*/
return PacketStatusForwardPacket;
}
}
CopyDataToClonedPacket(packets, state->macAddress,
ETH_ALEN /* offset for source MAC */,
ETH_ALEN /* length */);
return PacketStatusForwardPacket;
}
/*
* If ARP packet, then store <source IP address, source MAC address> in
* lookup table (if possible) and replace source ethernet MAC (and source
* ARP MAC, if appropriate) with that of the wireless hardware
*/
else if (typeClass == EthClassARP) { // ARP packet
uint32 arpHeaderWord1; // first word of ARP header
uint32 arpHeaderWord2; // second word of ARP header
/*
* Verify that packet meets minimum length expectations, the ARP header
* is 7 words for ethernet and IPv4, but the length may actually be 60
* due to ethernet's minimum length requirements.
*/
if (GetPacketLength(packet) < ethHeaderLen + ARP_HEADER_LEN) {
VNETKdPrint((MODULE_NAME " ToHostARP: ARP packet is insufficient "
"length of IPv4 and Ethernet, expected %u got %u\n",
ethHeaderLen + ARP_HEADER_LEN, GetPacketLength(packet)));
return PacketStatusDropPacket;
}
/*
* Verify the first word of the ARP header (hardcoded for ethernet and IPv4)
*
* I recently added IEEE802 support. These types of ARP requests were
* observed on the company network, so someone uses them. As long as
* the address lengths are the same then I imagine that the processing
* is identical and we can handle them (lengths are checked as part
* of processing the second word of ARP header).
*/
if (!GetPacketData(packet, ethHeaderLen, sizeof arpHeaderWord1,
&arpHeaderWord1) ||
!GetPacketData(packet, ethHeaderLen + sizeof arpHeaderWord1,
sizeof arpHeaderWord2, &arpHeaderWord2)) {
VNETKdPrint((MODULE_NAME "ToHostARP: ARP header couldnt be loaded\n"));
return PacketStatusDropPacket;
}
if (arpHeaderWord1 != HTONL(0x00010800) /* ethernet */ &&
arpHeaderWord1 != HTONL(0x00060800) /* ieee802 */ ) {
VNETKdPrint((MODULE_NAME " ToHostARP: ARP header appears wrong, "
"got %08x\n", arpHeaderWord1));
return PacketStatusDropPacket;
}
/*
* Perform action based on opcode in second word of ARP header.
* DEBUG: print general information about the packet
*/
#ifdef DBG
switch (arpHeaderWord2) {
case CONST_NTOHL(0x06040001):
W_VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates ARP request\n"));
break;
case CONST_NTOHL(0x06040002):
W_VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates ARP reply\n"));
break;
case CONST_NTOHL(0x06040003):
VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates RARP request\n"));
break;
case CONST_NTOHL(0x06040004):
VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates RARP reply\n"));
break;
case CONST_NTOHL(0x06040008):
VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates IARP request\n"));
break;
case CONST_NTOHL(0x06040009):
VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates IARP reply\n"));
break;
default:
VNETKdPrint((MODULE_NAME " ToHostARP: "
"ARP header2 indicates unknown opcode\n"));
break;
}
{
uint8 packetMac[ETH_ALEN];
uint8 packetIP[4];
if (!GetPacketData(packet, ethHeaderLen + ARP_SENDER_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't read MAC\n"));
return PacketStatusTooShort;
}
if (!GetPacketData(packet, ethHeaderLen + ARP_SENDER_IP_OFFSET,
sizeof packetIP, packetIP)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't read IP\n"));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME " ToHostARP: sender MAC is "
"%02x:%02x:%02x:%02x:%02x:%02x IP is %d.%d.%d.%d\n",
packetMac[0], packetMac[1], packetMac[2],
packetMac[3], packetMac[4], packetMac[5],
packetIP[0], packetIP[1], packetIP[2], packetIP[3]));
if (!GetPacketData(packet, ethHeaderLen + ARP_TARGET_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't read MAC\n"));
return PacketStatusTooShort;
}
if (!GetPacketData(packet, ethHeaderLen + ARP_TARGET_IP_OFFSET,
sizeof packetIP, packetIP)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't read IP\n"));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME " ToHostARP: target MAC is "
"%02x:%02x:%02x:%02x:%02x:%02x IP is %d.%d.%d.%d\n",
packetMac[0], packetMac[1], packetMac[2],
packetMac[3], packetMac[4], packetMac[5],
packetIP[0], packetIP[1], packetIP[2], packetIP[3]));
}
#endif
/*
* ARP handling for *outgoing traffic*
*
* ARP: host wants to know the MAC that corresponds to a particular IP
* 1 ARP request: <srcMAC, srcIP, 0, dstIP> store source <MAC,IP>,
* modify source MAC in eth & ARP
* 2 ARP reply: <srcMAC, srcIP, dstMAC, dstIP> store source <MAC,IP>,
* modify source MAC in eth & ARP
*
* RARP: host knows its MAC and wants to find out which IP it is
* assigned (simple form of DHCP)
* 3 RARP request: <srcMAC, 0 , srcMAC, 0 > can't store <MAC,IP>,
* should modify srcMAC but response would be MAC/IP of host
* I'm tempted to blackhole this packet, but for now I will just modify
* the eth MAC and allow it to be sent
* -- for now, presuming that lookup done on second srcMAC, but packet
* sent to first srcMAC (in other words, like IARP). This may
* be more correct / interoperable, but we still won't be able to
* handle RARP replies properly unless they are broadcasted.
* 4 RARP reply: <srcMAC, srcIP, dstMAC, dstIP> store source <MAC,IP>,
* modify source MAC in eth & ARP
*
* IARP: host knows a peer's MAC and wants to determine its IP address
* 8 IARP request: <srcMAC, srcIP, dstMAC, 0 > store source <MAC,IP>,
* modify source MAC in eth & ARP
* 9 IARP reply: <srcMAC, srcIP, dstMAC, dstIP> store source <MAC,IP>,
* modify source MAC in eth & ARP
*/
if (arpHeaderWord2 == HTONL(0x06040001) ||
arpHeaderWord2 == HTONL(0x06040002) ||
arpHeaderWord2 == HTONL(0x06040003) ||
arpHeaderWord2 == HTONL(0x06040004) ||
arpHeaderWord2 == HTONL(0x06040008) ||
arpHeaderWord2 == HTONL(0x06040009)) {
if (arpHeaderWord2 != HTONL(0x06040003)) { // don't store MAC/IP for RARP req.
uint32 IPaddr;
uint8 packetMac[ETH_ALEN];
W_VNETKdPrint((MODULE_NAME " ToHostARP: adding MAC/IP "
"combo to lookup table\n"));
/* read IP */
if (!GetPacketData(packet, ethHeaderLen + ARP_SENDER_IP_OFFSET,
sizeof IPaddr, &IPaddr)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't read IP\n"));
return PacketStatusTooShort;
}
/* read MAC */
if (!GetPacketData(packet, ethHeaderLen + ARP_SENDER_MAC_OFFSET,
sizeof packetMac, packetMac)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't read MAC\n"));
return PacketStatusTooShort;
}
/*
* Store <IP,MAC> combo in table. For MAC we could also use
* (char*)(packet)+ETH_ALEN, but it's more consistent with the ARP
* protocol to use the MAC located within the packet
*/
if (!AddIPv4andMACcombo(state, IPaddr, packetMac)) {
return PacketStatusDropPacket;
}
}
/*
* First we need to duplicate the packet so that other VMs don't
* get confused about MACs that arbitrarily change between the
* VM's MAC and the host's MAC.
*/
{
uint32 offset = ethHeaderLen + ARP_SENDER_MAC_OFFSET;
if (!ClonePacket(packets)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't "
"clone packet\n"));
return PacketStatusDropPacket;
}
/*
* Substitute sender ethernet MAC with the wireless hardware's MAC
*/
W_VNETKdPrint((MODULE_NAME " ToHostARP: modifying ETH MAC "
"%02x.%02x.%02x.%02x.%02x.%02x source address to match"
" wireless hardware %02x.%02x.%02x.%02x.%02x.%02x \n",
eh->srcAddr[0]&0xff, eh->srcAddr[1]&0xff,
eh->srcAddr[2]&0xff, eh->srcAddr[3]&0xff,
eh->srcAddr[4]&0xff, eh->srcAddr[5]&0xff,
state->macAddress[0]&0xff, state->macAddress[1]&0xff,
state->macAddress[2]&0xff, state->macAddress[3]&0xff,
state->macAddress[4]&0xff, state->macAddress[5]&0xff));
CopyDataToClonedPacket(packets, state->macAddress, ETH_ALEN /* offset */,
ETH_ALEN /* length */);
/*
* Modify ARP source MAC
*/
#if defined(_WIN32) && NDIS_SUPPORT_NDIS6
/*
* In normal case, we will substitute ARP source MAC with the
* wireless hardware's MAC in the ARP payload.
*
* However if the target IP address is host IP address,
* the arp protocol payload should not be touched. Otherwise
* the Windows TCP/IP stack will not respond for the request.
* (This behavior is seen in Vista and after.)
* Since the ARP payload is not touched, We should change the
* destination MAC in the MAC header to wireless hardware MAC
* so that no other host will receive and process the ARP packet
*/
{
ULONG ipAddr;
if (!GetPacketData(packet, ethHeaderLen + ARP_TARGET_IP_OFFSET,
sizeof ipAddr, &ipAddr)) {
VNETKdPrint((MODULE_NAME " ToHostARP: couldn't get target "
"IP address\n"));
return PacketStatusTooShort;
}
if (BridgeIPv4MatchAddrMAC(ipAddr, state->macAddress)) {
offset = 0;
}
}
#endif
#ifdef DBG
{
uint8 macAddr[ETH_ALEN];
if (!GetPacketData(packet, offset, sizeof macAddr, macAddr)) {
VNETKdPrint((MODULE_NAME " ToHostARP: "
"couldn't read data at offset %u\n", offset));
return PacketStatusTooShort;
}
W_VNETKdPrint((MODULE_NAME " ToHostARP: modifying %s from "
"%02x.%02x.%02x.%02x.%02x.%02x to "
"wireless hardware address "
"%02x.%02x.%02x.%02x.%02x.%02x \n",
(offset == 0) ? "destination address of MAC header"
: "ARP payload source MAC address",
macAddr[0], macAddr[1], macAddr[2],
macAddr[3], macAddr[4], macAddr[5],
state->macAddress[0], state->macAddress[1],
state->macAddress[2], state->macAddress[3],
state->macAddress[4], state->macAddress[5]));
}
#endif
CopyDataToClonedPacket(packets, state->macAddress, offset, ETH_ALEN);
return PacketStatusForwardPacket;
}
}
VNETKdPrint((MODULE_NAME " ToHostARP: unrecognized ARP type %08x\n",
arpHeaderWord2));
return PacketStatusDropPacket;
} else { // if EAPOL packet: typeClass == EthClassEAPOL
if (!ClonePacket(packets)) {
VNETKdPrint((MODULE_NAME " ToHostEapol: couldn't clone packet\n"));
return PacketStatusDropPacket;
}
/* For wireless, send EAPOL packets to host side. */
CopyDataToClonedPacket(packets, state->macAddress,
ETH_ALEN /* offset for source MAC */,
ETH_ALEN /* length */);
return PacketStatusForwardPacket;
}
}
/*
*----------------------------------------------------------------------
*
* ProcessOutgoingIPv4Packet --
*
* This function is used to examine IPv4 packets, and make any
* adjustments (if necessary) to the IP header and/or payload.
* The caller should have determined that the packet is IPv4
* before calling this function.
*
* Currently this function just makes sure that the broadcast
* bit is set on outgoing client DHCP packets that are being
* sent to a server.
*
* Results:
* None.
*
* Side effects:
* May modify the contents of the packet. Length should be
* unchanged.
*
*----------------------------------------------------------------------
*/
static void
ProcessOutgoingIPv4Packet(SMACPacket *packet, // IN: cloned packet to process
uint32 ethHeaderLen) // IN: length of ethernet header
{
uint8 proto; // protocol within ip
uint16 ipFlags; // flags and offset
/*
* Should certain checks, e.g., IP version field, be checked here,
* or assume that caller has already performed this check?
*/
// check should have been performed by caller
ASSERT(GetPacketLength(packet) >= IP_HEADER_LEN + ethHeaderLen);
if (!GetPacketData(packet, ethHeaderLen + IP_HEADER_PROTO_OFFSET,
sizeof proto, &proto)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get protocol\n"));
ASSERT(0); // should never occur
return;
}
if (!GetPacketData(packet, ethHeaderLen + IP_HEADER_FLAGS_OFFSET,
sizeof ipFlags, &ipFlags)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get flags\n"));
ASSERT(0); // should never occur
return;
}
/*
* Verify offset=0 and M=0: mask off "fragment" flag,
* all others should be zero. This is to ensure that we're processing
* only the front of the actual packet (i.e., validate that what we
* think if offset 0 is really offset 0).
*/
if (ipFlags & HTONS((uint16)(0xbfff))) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got a fragmented IP "
"(ipFlags %04x), so not performing higher-level processing\n",
ipFlags));
return;
}
/*
* NOTE: the following switch statement can be replaced with a single
* check for proto==17, since UDP is the only non-debug case
*/
switch (proto) {
case 1: { // ICMP
#ifdef DBG
uint8 ipHeader[IP_HEADER_LEN];
uint32 ipHeaderLen = 0;
uint32 ipVer = 0;
uint16 ipLen = 0;
uint8 typeField = 0;
uint8 codeField = 0;
/* Verify that we have at least a whole, minimal IP header */
if (!GetPacketData(packet, ethHeaderLen, sizeof ipHeader, ipHeader)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get IP\n"));
return;
}
ipHeaderLen = 4 * (ipHeader[0] & 0xf); // version of IP header
ipVer = ipHeader[0] >> 4; // length of IP header
ipLen = NTOHS(*(uint16*)(ipHeader + 2)); // total datagram len
if (ipVer == 4 && GetPacketLength(packet) >=
ipHeaderLen + ethHeaderLen + 2 * 4 &&
GetPacketData(packet, ethHeaderLen + ipHeaderLen,
sizeof typeField, &typeField) &&
GetPacketData(packet, ethHeaderLen + ipHeaderLen + 1,
sizeof codeField, &codeField)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got ICMP, "
"type = %u, code = %u,\n", typeField, codeField));
} else {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got ICMP, "
"but couldn't process\n"));
}
#endif /* DBG */
return;
}
case 2: // IGMP
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got IGMP\n"));
return;
case 4: // IP in IP
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got IP in IP\n"));
return;
case 6: // TCP
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got TCP\n"));
return;
case 17: { // UDP
uint8 ipHeaderFirstByte;
uint32 ipHeaderLen; // length of IP header
uint16 srcPort; // UDP source port
uint16 destPort; // UDP dest port
if (!GetPacketData(packet, ethHeaderLen,
sizeof ipHeaderFirstByte, &ipHeaderFirstByte)) {
VNETKdPrint((MODULE_NAME "FromHostIP: Failed to get IP length\n"));
return;
}
ipHeaderLen = 4 * (ipHeaderFirstByte & 0xf); // version of IP header
W_VNETKdPrint((MODULE_NAME "ProcessOutgoing: got UDP\n"));
/*
* Verify that sufficient data for UDP packet header is present
*/
if (GetPacketLength(packet) < ethHeaderLen + ipHeaderLen + UDP_HEADER_LEN) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: UDP header not present\n"));
return;
}
/*
* decode the source and destination ports in UDP header
*/
if (!GetPacketData(packet, ethHeaderLen + ipHeaderLen,
sizeof srcPort, &srcPort) ||
!GetPacketData(packet, ethHeaderLen + ipHeaderLen + 2,
sizeof destPort, &destPort)) {
VNETKdPrint((MODULE_NAME "FromHostIP: Failed to get UDP ports\n"));
return;
}
srcPort = NTOHS(srcPort);
destPort = NTOHS(destPort);
#ifdef DBG
{
uint8 srcAddr[4];
uint8 destAddr[4];
if (!GetPacketData(packet, ethHeaderLen + IP_HEADER_SRC_ADDR_OFFSET,
sizeof srcAddr, srcAddr) ||
!GetPacketData(packet, ethHeaderLen + IP_HEADER_DEST_ADDR_OFFSET,
sizeof destAddr, destAddr)) {
VNETKdPrint((MODULE_NAME "FromHostIP: Failed to get IP addrs\n"));
ASSERT(0); // should never happen
} else {
W_VNETKdPrint((MODULE_NAME "ProcessOutgoing: got UDP, "
"src %d.%d.%d.%d %d dest %d.%d.%d.%d %d\n",
srcAddr[0], srcAddr[1], srcAddr[2], srcAddr[3], srcPort,
destAddr[0], destAddr[1], destAddr[2], destAddr[3],
destPort));
}
}
#endif
if (destPort == 67) { // if destined for DHCP server port
uint32 firstDHCPword;
uint16 dhcpFlags;
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got client "
"DHCP packet destined for a server\n"));
/*
* The minimum length of a DHCP packet must be 243 bytes:-
* 240 (DHCP header including magic cookie) + 3 (message type option)
* RFC 2131 mandates having the 'message type' option in every DHCP packet.
*/
if (GetPacketLength(packet) < ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 243) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: packet "
"too small for DHCP\n"));
return;
}
/*
* Verify that the first word in the DHCP header matches
* out expectations
*/
if (!GetPacketData(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN, sizeof firstDHCPword, &firstDHCPword)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get first "
"DHCP word\n"));
return;
}
firstDHCPword = NTOHL(firstDHCPword);
if ((firstDHCPword & 0xffffff00) != 0x01010600) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: DHCP "
"first word appears incorrect\n"));
return;
}
/*
* Turn on the broadcast flag, just in case. This
* instructs/requests that the server broadcast the reply
* back, which is a nice way to avoid the issue about
* whether to set chaddr to the VM MAC or the wireless MAC.
*
* If chaddr is set to wireless MAC then we'd receive the reply,
* but the DHCP server might confuse us with the host and send
* us a copy of the host's current IP address assignment. This
* confusion should be occur if all OSes used the client identifier
* extension, but we can't rely on this.
*
* If chaddr is set to VM MAC then no alias confusion should occur,
* but the reply might be unicast to the VM's MAC (in which case
* we might not , or will not, receive the reply). To avoid the
* receive issue, it's easiest to just set the broadcast packet
* if it's not already set.
*
* One potential downside is that the VM might be expecting a unicast
* reply, and it's unclear whether it can or will handling a
* broadcast reply properly. We can special case this support, if
* necessary.
*/
if (!GetPacketData(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 10, sizeof dhcpFlags, &dhcpFlags)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get "
"DHCP flags\n"));
return;
}
dhcpFlags = NTOHS(dhcpFlags);
if (dhcpFlags & 0x8000) { // if flag already set, then no work to do
VNETKdPrint((MODULE_NAME "ProcessOutgoing: DHCP broadcast "
"bit is already set\n"));
} else {
/*
* Get the original checksum
*/
uint16 oriChecksum;
if (!GetPacketData(packet, ethHeaderLen + ipHeaderLen + 6,
sizeof oriChecksum, &oriChecksum)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get "
"UDP checksum\n"));
return;
}
oriChecksum = NTOHS(oriChecksum);
VNETKdPrint((MODULE_NAME "ProcessOutgoing: DHCP broadcast bit "
"NOT set, need to do so\n"));
if (oriChecksum) { // if checksum specified
/*
* Compute the checksum over the word that we're about to change
*/
uint32 sumBefore; // checksum before
uint32 sumAfter; // checksum after changes
uint32 sumDiff; // difference in checksum
uint16 newChecksum; // new checksum
/* checksum bytes 8-11 */
if (!GetPacketData(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 8,
sizeof sumBefore, &sumBefore)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get "
"UDP checksum before\n"));
return;
}
sumBefore = SUM32(sumBefore); // checksum before
VNETKdPrint((MODULE_NAME "ProcessOutgoing: DHCP UDP checksum "
"is present, changing flags and checksum\n"));
/*
* Set the actual flag in bytes 10 and 11
*/
dhcpFlags |= 0x8000;
if (!SetPacketByte(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 11, dhcpFlags & 0xff) ||
!SetPacketByte(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 10, dhcpFlags >> 8)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't set "
"new UDP flags\n"));
return;
}
/*
* Compute the checksum over the word that we have just changed
*/
/* checksum (again) bytes 8-11 */
if (!GetPacketData(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 8,
sizeof sumAfter, &sumAfter)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get "
"UDP checksum after\n"));
ASSERT(0); // should never occur
return;
}
sumAfter = SUM32(sumAfter);
/*
* Compute difference/delta between old and new checksums
*/
sumDiff = CalcChecksumDiff(sumBefore, sumAfter);
/*
* Compute new checksum, based on delta from existing checksum
*/
newChecksum = UpdateSum(oriChecksum, sumDiff);
/*
* Verify that the new checksum isn't 0 (in which case make it ~0)
*/
if (!newChecksum) {
newChecksum = 0xffff;
}
/*
* Write back the new checksum
*/
if (!SetPacketByte(packet, ethHeaderLen + ipHeaderLen + 6,
newChecksum >> 8) ||
!SetPacketByte(packet, ethHeaderLen + ipHeaderLen + 7,
newChecksum & 0xff)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't set "
"new UDP checksum\n"));
return;
}
} else {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: DHCP UDP "
"checksum is NOT present, changing only flags\n"));
/*
* Set the actual flag without modifying checksum
*/
dhcpFlags |= 0x8000;
if (!SetPacketByte(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 11, dhcpFlags & 0xff) ||
!SetPacketByte(packet, ethHeaderLen + ipHeaderLen +
UDP_HEADER_LEN + 10, dhcpFlags >> 8)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't set "
"new UDP flags, non-checksum case\n"));
return;
}
}
}
#ifdef DBG
{
uint32 IPDestAddr;
if (!GetPacketData(packet, ethHeaderLen + IP_HEADER_DEST_ADDR_OFFSET,
sizeof IPDestAddr, &IPDestAddr)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get "
"IP dest addr\n"));
return;
}
if (IPDestAddr == IP_ADDR_BROADCAST) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: server-bound DHCP "
"packet is broadcast\n"));
} else {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: server-bound DHCP "
"packet is unicast\n"));
}
}
#endif
return;
} else if (destPort == 68) { // if packet for destined DHCP client port
#ifdef DBG
uint32 IPDestAddr = 0;
if (!GetPacketData(packet, ethHeaderLen + IP_HEADER_DEST_ADDR_OFFSET,
sizeof IPDestAddr, &IPDestAddr)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: couldn't get "
"IP dest addr\n"));
return;
}
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got server "
"DHCP packet destined for a client\n"));
if (IPDestAddr == IP_ADDR_BROADCAST) {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: client-bound "
"DHCP packet is broadcast\n"));
} else {
VNETKdPrint((MODULE_NAME "ProcessOutgoing: client-bound "
"DHCP packet is unicast\n"));
}
#endif /* DBG */
return;
} else {
W_VNETKdPrint((MODULE_NAME "ProcessOutgoing: got UDP srcPort "
"%d destPort %d\n", srcPort, destPort));
return;
}
}
case 27: // RDP
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got RDP\n"));
return;
case 41: // IPv6
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got IPv6\n"));
return;
case 51: // Authentication Header
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got Authentication Header\n"));
return;
case 55: // Mobile IP
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got Mobile IP\n"));
return;
case 103: // PIM
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got PIM\n"));
return;
case 111: // IPX in IP
VNETKdPrint((MODULE_NAME "ProcessOutgoing: got IPX in IP\n"));
return;
default:
VNETKdPrint((MODULE_NAME "ProcessOutgoing: Unknown/unhandled "
"service reported by IP packet\n"));
return;
}
}
/*
*----------------------------------------------------------------------
*
* PatchMacAddrFixChecksum --
*
* This function takes in a packet and patches the MAC address at
* a given offset in the packet with a given MAC address. It also
* updates the 2byte checksum field in the packet at a given offset.
*
* Results:
* TRUE on success, FALSE on failure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Bool
PatchMacAddrFixChecksum(SMACPacket *packet, // IN:
const uint32 packetLen, // IN:
const uint32 checksumOffset, // IN:
const uint32 patchMacOffset, // IN:
const uint8 *macAddress, // IN:
const char *logPrefix) // IN:
{
uint16 oriChecksum;
uint32 sumBefore[2];
uint32 sumAfter[2];
uint32 sumDiff;
uint16 newChecksum;
int i;
UNREFERENCED_PARAMETER(logPrefix);
if (!packet || checksumOffset + sizeof oriChecksum > packetLen ||
patchMacOffset + ETH_ALEN > packetLen) {
VNETKdPrint((MODULE_NAME "%s: patching MAC address got invalid input: "
"packet = %p, packetLen = %u, checksumOffset = %u, "
"patchMacOffset = %u.\n", logPrefix, packet, packetLen,
checksumOffset, patchMacOffset));
return FALSE;
}
if (!GetPacketData(packet, checksumOffset, sizeof oriChecksum,
&oriChecksum)) {
VNETKdPrint((MODULE_NAME "%s: couldn't get original checksum value.\n",
logPrefix));
return FALSE;
}
oriChecksum = NTOHS(oriChecksum);
sumBefore[1] = 0; /* Clear out any garbage. */
if (!GetPacketData(packet, patchMacOffset, sizeof sumBefore[0],
&sumBefore[0]) ||
!GetPacketData(packet, patchMacOffset + sizeof sumBefore[0], ETH_ALEN -
sizeof sumBefore[0], &sumBefore[1])) {
VNETKdPrint((MODULE_NAME "%s: couldn't get original MAC address in "
"packet\n", logPrefix));
return FALSE;
}
sumAfter[0] = *(uint32 *)macAddress;
sumAfter[1] = *(uint16 *)(macAddress + sizeof sumAfter[0]);
sumBefore[0] = SUM32(sumBefore[0]);
sumAfter[0] = SUM32(sumAfter[0]);
sumDiff = CalcChecksumDiff(sumBefore[0], sumAfter[0]);
newChecksum = UpdateSum(oriChecksum, sumDiff);
sumDiff = CalcChecksumDiff(sumBefore[1], sumAfter[1]);
newChecksum = UpdateSum(newChecksum, sumDiff);
for (i = 0; i < ETH_ALEN; i++) {
if (!SetPacketByte(packet, patchMacOffset + i, macAddress[i])) {
VNETKdPrint((MODULE_NAME "%s: couldn't patch MAC address.\n",
logPrefix));
return FALSE;
}
}
if (!SetPacketByte(packet, checksumOffset, newChecksum >> 8) ||
!SetPacketByte(packet, checksumOffset + 1, newChecksum & 0xff)) {
VNETKdPrint((MODULE_NAME "%s: couldn't set new checksum in packet.\n",
logPrefix));
return FALSE;
}
return TRUE;
}
#if defined(_WIN32) && NDIS_SUPPORT_NDIS6
/*
*----------------------------------------------------------------------
*
* IPv6IsMulticast --
*
* Determines if a given IPv6 address is a multicast address or not.
*
* Results:
* TRUE/FALSE.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE_SINGLE_CALLER Bool
IPv6IsMulticast(const IPv6Addr *ipv6Addr) // IN:
{
return *(uint8 *)ipv6Addr == 0xff;
}
/*
*----------------------------------------------------------------------
*
* IsPacketDestinationTheHost --
*
* Given an IPv6 packet determines if it is destined to a unicast IPv6
* address associated with the host interface.
*
* Results:
* TRUE/FALSE.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE_SINGLE_CALLER Bool
IsPacketDestinationTheHost(const uint8 *dstMACAddr, // IN:
const uint8 *smacAddress, // IN:
const IPv6Addr *ipv6Addr) // IN:
{
if (IS_MULTICAST(dstMACAddr)) { /* Multicast or broadcast. */
return IPv6IsMulticast(ipv6Addr) ? FALSE :
BridgeIPv6MatchAddrMAC(ipv6Addr,
smacAddress);
} else { /* Unicast. */
return MAC_EQ(dstMACAddr, smacAddress);
}
}
#endif
#define NEXTHDR_HOP 0
#define NEXTHDR_ROUTING 43
#define NEXTHDR_AUTH 51
#define NEXTHDR_DEST 60
#define NEXTHDR_MOBILITY 135
/*
*----------------------------------------------------------------------
*
* SmacWalkIPv6ExtensionHeaders --
*
* Walks (some) IPv6 extension headers in a given packet. The packet's
* length and offset within packet of start of IPv6 header & payload
* is input to this function. There are two outputs: the next header
* type of the PDU contained (when it does not match any of the
* extension headers that we walk or a higher level PDU type such as
* TCP or UDP), and the offset of this PDU within the packet.
*
* Results:
* TRUE on success, FALSE on failure.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Bool
SmacWalkIPv6ExtensionHeaders(SMACPacket *packet, // IN:
const uint32 packetLen, // IN:
const uint32 ethHeaderLen, // IN:
uint8 *nextHeaderPtr, // OUT:
uint32 *nextHeaderOffset) // OUT:
{
uint8 nextHeader;
uint32 offset; /* Bytes inspected. */
uint32 headerLen; /* Length of current header. */
ASSERT(packet && packetLen >= IPv6_HEADER_LEN + ethHeaderLen &&
nextHeaderPtr && nextHeaderOffset);
if (!GetPacketData(packet, ethHeaderLen + IPv6_NEXT_HEADER_OFFSET,
sizeof nextHeader, &nextHeader)) {
return FALSE;
}
headerLen = IPv6_HEADER_LEN;
offset = ethHeaderLen + headerLen;
while (offset < packetLen - 1) { /* Subtract 1 b/c we lookahead 2 bytes. */
switch (nextHeader) {
case NEXTHDR_HOP:
case NEXTHDR_ROUTING:
case NEXTHDR_AUTH:
case NEXTHDR_DEST:
case NEXTHDR_MOBILITY:
if (!GetPacketData(packet, offset + 1, sizeof(uint8), &headerLen)) {
return FALSE;
}
if (nextHeader == NEXTHDR_AUTH) {
headerLen = (headerLen + 2) << 2;
} else {
headerLen = (headerLen + 1) << 3;
}
if (!GetPacketData(packet, offset, sizeof nextHeader, &nextHeader)) {
return FALSE;
}
offset += headerLen;
break;
default:
goto out;
}
}
out:
*nextHeaderOffset = offset;
*nextHeaderPtr = nextHeader;
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* ProcessOutgoingIPv6Packet --
*
* This function is used to examine IPv6 packets, and make any
* modifications (if necessary). Currently it assumes no IPv6
* extension options are present, and processes ICMPv6 payloads
* only. If the source link layer option in an NDP (ICMPv6) packet
* is present, this function replaces that MAC address with the
* MAC address of the wireless PNIC so that replies (neighbour
* advertisements) would be addressed to the host. The ICMPv6
* checksums are updated accordingly.
* The caller should have determined that the packet is IPv6
* before calling this function.
*
* Results:
* TRUE on success, FALSE on failure.
*
* Side effects:
* May modify the contents of the packet.
*
*----------------------------------------------------------------------
*/
static Bool
ProcessOutgoingIPv6Packet(SMACPacket *packet, // IN: cloned packet
uint32 ethHeaderLen, // IN:
const uint8 *smacAddress, // IN: wireless PNIC's MAC
Bool *toHost) // OUT: destined to host?
{
uint8 nextHeader = 0; /* Silence compiler warnings. */
uint32 nextHeaderOffset = 0; /* Silence compiler warnings. */
const uint32 packetLen = GetPacketLength(packet);
#if defined(_WIN32) && NDIS_SUPPORT_NDIS6
uint8 dstMACAddr[ETH_ALEN];
#endif
UNREFERENCED_PARAMETER(toHost);
/*
* Assume caller has done certain checks.
*/
ASSERT(packetLen >= IPv6_HEADER_LEN + ethHeaderLen);
ASSERT(toHost && !*toHost);
#if defined(_WIN32) && NDIS_SUPPORT_NDIS6
{
IPv6Addr dstAddr;
if (!GetPacketData(packet, 0, sizeof dstMACAddr, &dstMACAddr)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get "
"destination MAC address.\n"));
return FALSE;
}
if (!GetPacketData(packet, ethHeaderLen + IPv6_HEADER_DST_ADDR_OFFSET,
sizeof dstAddr, &dstAddr)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get "
"destination IPv6 address.\n"));
return FALSE;
}
*toHost = IsPacketDestinationTheHost(dstMACAddr, smacAddress, &dstAddr);
if (*toHost) {
return TRUE;
}
}
#endif
if (!SmacWalkIPv6ExtensionHeaders(packet, packetLen, ethHeaderLen,
&nextHeader, &nextHeaderOffset)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get "
"next header.\n"));
return FALSE;
}
switch (nextHeader) {
case 58: { /* ICMPv6. */
uint8 type;
uint8 optionToFind;
uint32 ndpMsgLen;
const uint32 icmpv6ChecksumOffset = nextHeaderOffset +
ICMPv6_CHECKSUM_OFFSET;
if (!GetPacketData(packet, nextHeaderOffset + ICMPv6_TYPE_OFFSET,
sizeof type, &type)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get ICMPv6 "
"type value.\n"));
return FALSE;
}
switch (type) {
case ICMPv6_NDP_RTR_SOLICITATION:
optionToFind = ICMPv6_NDP_OPTION_SRC_MAC;
ndpMsgLen = ICMPv6_NDP_RTR_SOL_LEN;
break;
case ICMPv6_NDP_RTR_ADVERTISEMENT:
optionToFind = ICMPv6_NDP_OPTION_SRC_MAC;
ndpMsgLen = ICMPv6_NDP_RTR_ADV_LEN;
break;
case ICMPv6_NDP_NBR_SOLICITATION:
optionToFind = ICMPv6_NDP_OPTION_SRC_MAC;
ndpMsgLen = ICMPv6_NDP_NBR_LEN;
break;
case ICMPv6_NDP_NBR_ADVERTISEMENT:
optionToFind = ICMPv6_NDP_OPTION_TARGET_MAC;
ndpMsgLen = ICMPv6_NDP_NBR_LEN;
break;
default: /* Nothing left to do. */
return TRUE;
}
#if defined(_WIN32) && NDIS_SUPPORT_NDIS6
if (type == ICMPv6_NDP_NBR_SOLICITATION) {
IPv6Addr targetAddr;
if (!GetPacketData(packet, nextHeaderOffset + ICMPv6_TARGET_IP_OFFSET,
sizeof targetAddr, &targetAddr)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get "
"target IPv6 address in ICMPv6 NDP packet.\n"));
return FALSE;
}
*toHost = IsPacketDestinationTheHost(dstMACAddr, smacAddress,
&targetAddr);
if (*toHost) {
return TRUE;
}
}
#endif
nextHeaderOffset += ndpMsgLen; /* Start of NDP options. */
/*
* Walk through all NDP options searching for the option we are
* interested in.
*/
while (nextHeaderOffset < packetLen) {
uint8 option;
uint8 optionLen;
if (!GetPacketData(packet, nextHeaderOffset +
ICMPv6_NDP_OPTION_TYPE_OFFSET, sizeof option,
&option)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get "
"ICMPv6 NDP option type value.\n"));
return FALSE;
}
/*
* Replace the source/target MAC address option with the MAC address
* of the wireless PNIC when we find the option we are looking for.
*/
if (option == optionToFind) {
return PatchMacAddrFixChecksum(packet, packetLen,
icmpv6ChecksumOffset,
nextHeaderOffset +
ICMPv6_NDP_MAC_OFFSET, smacAddress,
"Outgoing IPv6 packet");
}
if (!GetPacketData(packet, nextHeaderOffset +
ICMPv6_NDP_OPTION_LEN_OFFSET, sizeof optionLen,
&optionLen)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: couldn't get "
"ICMPv6 NDP option length value.\n"));
return FALSE;
}
/*
* Option length is in units of 8 bytes. Option length of 0 is
* invalid and such packets must be discarded.
*/
if (UNLIKELY(!optionLen)) {
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: got invalid "
"option length (0).\n"));
return FALSE;
}
nextHeaderOffset += (uint32)optionLen << 3;
}
break;
}
default:
VNETKdPrint((MODULE_NAME "ProcessOutgoingIPv6: Unknown/unhandled "
"next header type %u in IPv6 packet.\n", nextHeader));
break;
}
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* ProcessIncomingIPv4Packet --
*
* This function is used to examine IPv4 packets, and make any
* adjustments (if necessary) to the IP header and/or payload.
* The third paramter is optional (specify NULL if not provided).
*
* Currently **this function does nothing** but print out debug
* information about the contents of the IP packet
*
* Results:
* None.
*
* Side effects:
* May modify the contents of the packet. Length should be
* unchanged.
*
*----------------------------------------------------------------------
*/
/*
* NOTE: this abstraction doesn't match reality because we may only have
* a partial packet received so far. But, since we're not modifying the
* packet (and only trying to ID/classify it) then this shouldn't matter.
*/
#ifdef DBG
static void
ProcessIncomingIPv4Packet(SMACPacket *packet, // IN/OUT: incoming packet
Bool knownMacForIp) // IN: IP is present in table?
{
uint8 proto; // protocol within ip
uint16 ipFlags; // offset and flags
/*
* Should certain checks, e.g., IP version field, be checked here,
* or assume that caller has already performed this check?
*/
/*
* Presuming that caller has performed checks, and that IP header
* comes exactly ETH_HLEN bytes into the packet
*/
if (GetPacketLength(packet) < ETH_HLEN + IP_HEADER_LEN) {
return;
}
if (!GetPacketData(packet, ETH_HLEN + IP_HEADER_PROTO_OFFSET, sizeof proto,
&proto)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: couldn't get protocol\n"));
return;
}
if (!GetPacketData(packet, ETH_HLEN + IP_HEADER_FLAGS_OFFSET, sizeof ipFlags,
&ipFlags)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: couldn't get ip flags\n"));
return;
}
/*
* Verify offset=0 and M=0: mask off "fragment" flag,
* all others should be zero
*/
if (ipFlags & HTONS((uint16)~(0x4000))) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: got a fragmented IP "
"(ipFlags %04x), so not performing higher-level processing\n",
ipFlags));
return;
}
switch (proto) {
case 1: { // ICMP
uint32 ipHeaderLen = 0; // length of IP header
uint32 ipVer = 0; // version of IP header
uint16 ipLen = 0; // total length of datagram
uint8 firstByte = '\0'; // used to safely read from packet
if (!GetPacketData(packet, ETH_HLEN, sizeof firstByte, &firstByte)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: ICMP read ver error\n"));
return;
}
ipHeaderLen = 4 * (firstByte & 0xf); // length of IP header
ipVer = firstByte>>4; // version of IP header
if (!GetPacketData(packet, ETH_HLEN + 2, sizeof ipLen, &ipLen)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: ICMP read len error\n"));
return;
}
ipLen = NTOHS(ipLen);
if (ipVer == 4 && GetPacketLength(packet) >=
ipHeaderLen + 2 * 4 + ETH_HLEN) {
uint8 typeField = 0;
uint8 codeField = 0;
if (!GetPacketData(packet, ETH_HLEN + ipHeaderLen,
sizeof typeField, &typeField) ||
!GetPacketData(packet, ETH_HLEN + ipHeaderLen + 1,
sizeof codeField, &codeField)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: ICMP "
"read ICMP error\n"));
return;
}
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got ICMP, "
"type = %d, code = %d,\n", typeField, codeField));
} else {
VNETKdPrint((MODULE_NAME "ProcessIncoming: got ICMP, "
"but couldn't process\n"));
}
return;
}
case 2: // IGMP
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got IGMP\n"));
return;
case 4: // IP in IP
VNETKdPrint((MODULE_NAME "ProcessIncoming: got IP in IP\n"));
return;
case 6: // TCP
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got TCP\n"));
return;
case 17: { // UDP
uint32 ipHeaderLen = 0; // length of IP header
uint32 ipVer = 0; // version of IP header
uint16 ipLen = 0; // total length of datagram
uint8 firstByte; // used to safely read from packet
if (!GetPacketData(packet, ETH_HLEN, sizeof firstByte, &firstByte)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: ICMP read ver error\n"));
ASSERT(0);
return;
}
ipHeaderLen = 4 * (firstByte&0xf); // length of IP header
ipVer = firstByte >> 4; // version of IP header
/*
* verifify that IP fragment offset is 0
*/
if (!GetPacketData(packet, ETH_HLEN + 2, sizeof ipLen, &ipLen)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: ICMP read len error\n"));
ASSERT(0);
return;
}
ipLen = NTOHS(ipLen);
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got UDP, "
"IP ver %d, header len %d,"
"overall len %d\n", ipVer, ipHeaderLen, ipLen));
if (ipVer == 4 && GetPacketLength(packet) >=
ipHeaderLen + 2 * 4 + ETH_HLEN) {
uint16 srcPort = 0;
uint16 destPort = 0;
uint8 srcAddr[4];
uint8 destAddr[4];
if (!GetPacketData(packet, ipHeaderLen + ETH_HLEN,
sizeof srcPort, &srcPort) ||
!GetPacketData(packet, ipHeaderLen + ETH_HLEN + 2,
sizeof destPort, &destPort)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: read "
"UDP header error\n"));
ASSERT(0);
return;
}
srcPort = NTOHS(srcPort);
destPort = NTOHS(destPort);
if (!GetPacketData(packet, ETH_HLEN + IP_HEADER_SRC_ADDR_OFFSET,
sizeof srcAddr, srcAddr) ||
!GetPacketData(packet, ETH_HLEN + IP_HEADER_DEST_ADDR_OFFSET,
sizeof destAddr, destAddr)) {
VNETKdPrint((MODULE_NAME "ProcessIncoming: couldn't read "
"source and/or dest IP addr\n"));
return;
}
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got UDP, src "
"%d.%d.%d.%d %d dest %d.%d.%d.%d %d\n",
srcAddr[0], srcAddr[1], srcAddr[2], srcAddr[3],
srcPort, destAddr[0], destAddr[1], destAddr[2],
destAddr[3], destPort));
if (destPort == 67) {
if (knownMacForIp) {
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got client DHCP "
"packet destined for THIS server\n"));
} else {
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got client DHCP "
"packet destined for a server\n"));
}
} else if (destPort == 68) {
if (knownMacForIp) {
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got server DHCP "
"packet destined for THIS client\n"));
} else {
W_VNETKdPrint((MODULE_NAME "ProcessIncoming: got server DHCP "
"packet destined for a client\n"));
}
}
}
return;
}
case 27: // RDP
VNETKdPrint((MODULE_NAME "ProcessIncoming: got RDP\n"));
return;
case 41: // IPv6
VNETKdPrint((MODULE_NAME "ProcessIncoming: got IPv6\n"));
return;
case 51: // Authentication Header
VNETKdPrint((MODULE_NAME "ProcessIncoming: got Authentication Header\n"));
return;
case 55: // Mobile IP
VNETKdPrint((MODULE_NAME "ProcessIncoming: got Mobile IP\n"));
return;
case 103: // PIM
VNETKdPrint((MODULE_NAME "ProcessIncoming: got PIM\n"));
return;
case 111: // IPX in IP
VNETKdPrint((MODULE_NAME "ProcessIncoming: got IPX in IP\n"));
return;
default:
VNETKdPrint((MODULE_NAME "ProcessIncoming: Unknown/unhandled service "
"reported by IP packet\n"));
return;
}
}
#endif /* DBG */
/*
*----------------------------------------------------------------------
*
* SMAC_InitState --
*
* Initialize adapter SMAC state. Presumes that the
* supplied adapter object has already been initialized to zero.
*
* Results:
* None.
*
* Side effects:
* Initializes adapter SMAC state.
*
*----------------------------------------------------------------------
*/
void SMACINT
SMAC_InitState(SMACState **ptr) // OUT: pointer to alloced/inited state
{
SMACState * state;
VNETKdPrintCall(("SMAC_InitState"));
ASSERT(ptr);
state = ALLOCATEMEMORY(sizeof *state, REORDER_TAG('SMAC'));
if (state == NULL) {
*ptr = NULL;
return;
}
MEMSET(state, 0, sizeof *state);
VNETKdPrint((MODULE_NAME "SMAC_InitState: state %p\n", state));
INITSPINLOCK(&(state->smacSpinLock));
#ifndef _WIN32
if (state->smacSpinLock == NULL) {
VNETKdPrint((MODULE_NAME "SMAC_InitState: coudln't initialize spinlock."
"Freeing state.\n"));
FREEMEMORY(state);
state = NULL;
}
#endif
VNETKdPrintReturn(("SMAC_InitState"));
*ptr = state;
}
/*
*----------------------------------------------------------------------
*
* SMAC_SetMac --
*
* Set MAC stored in SMAC state. Presumes that the
* supplied SMAC state has already been initialized.
*
* Results:
* None.
*
* Side effects:
* Sets MAC in SMAC state.
*
*----------------------------------------------------------------------
*/
void SMACINT
SMAC_SetMac(SMACState *state, // IN: state to update
uint8 *mac) // IN: pointer to host adapter's MAC
{
VNETKdPrintCall(("SMAC_SetMac"));
ASSERT(state);
VNETKdPrint((MODULE_NAME "SMAC_SetMac: state %p mac %p\n",
state, mac));
#ifdef DBG
if (mac) {
VNETKdPrint((MODULE_NAME
"mac 0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
}
#endif
/*
* There's likely some atomicity issues here, but grabbing a lock
* here won't help since the readers won't be grabbing a lock.
* The only time an update can occur with traffic being processed
* is on Linux, which I don't see as a big deal given the lack
* of demand for this feature on that OS.
*/
if (mac) {
MEMCPY(state->macAddress, mac, ETH_ALEN);
} else {
MEMSET(state->macAddress, 0, ETH_ALEN);
}
VNETKdPrintReturn(("SMAC_SetMac"));
}
/*
*----------------------------------------------------------------------
*
* SMAC_SetForwardUnknownPackets --
*
* Initialize adapter SMAC state. Presumes that the
* supplied adapter object has already been initialized to zero.
*
* Results:
* None.
*
* Side effects:
* Initializes adapter SMAC state.
*
*----------------------------------------------------------------------
*/
void SMACINT
SMAC_SetForwardUnknownPackets(SMACState *state, // IN: pointer to smac state
Bool forwardUnknown) // IN: T/F to forward
{
VNETKdPrintCall(("SMAC_SetForwardUnknownPackets"));
ASSERT(state);
state->smacForwardUnknownPackets = forwardUnknown;
VNETKdPrintReturn(("SMAC_SetForwardUnknownPackets"));
}
/*
*----------------------------------------------------------------------
*
* SMAC_CleanupState --
*
* Deallocates adapter SMAC state.
*
* Results:
* None
*
* Side effects:
* Deallocates adapter SMAC state.
*
*----------------------------------------------------------------------
*/
void SMACINT
SMAC_CleanupState(SMACState **ptr) // IN: state to dealloc
{
uint32 i = 0;
SMACState *state;
#ifdef _WIN32
KIRQL irql;
#endif
SPINLOCKINIT();
VNETKdPrintCall(("SMAC_CleanupState"));
ASSERT(ptr);
state = *ptr;
if (state == NULL) {
return;
}
*ptr = NULL;
RAISEIRQL();
ACQUIRESPINLOCK(&state->smacSpinLock);
for (i = 0; i < SMAC_HASH_TABLE_SIZE; ++i) {
IPmacLookupEntry * entry = state->IPlookupTable[i];
while (entry) {
IPmacLookupEntry * next = entry->ipNext;
VNETKdPrintCall(("--deleted entry\n"));
FREEMEMORY(entry);
--state->numberOfIPandMACEntries;
entry = next;
}
}
if (state->numberOfIPandMACEntries != 0) {
VNETKdPrint((MODULE_NAME "SMAC_CleanupState: "
"entry count is non-zero: %u\n",
state->numberOfIPandMACEntries));
ASSERT(state->numberOfIPandMACEntries == 0);
}
RELEASESPINLOCK(&state->smacSpinLock);
FREESPINLOCK(&state->smacSpinLock);
LOWERIRQL();
FREEMEMORY(state);
VNETKdPrintReturn(("SMAC_CleanupState"));
}
/*
*----------------------------------------------------------------------
*
* LookupTypeClass --
*
* Examines and classifies the protocol type of an ethernet frame
*
* Results:
* Returns the appropriate EthClass classification for the
* specified ethernet media type.
*
*
* Side effects:
* May modify the contents of the packet. Length should be
* unchanged.
*
*----------------------------------------------------------------------
*/
EthClass
LookupTypeClass(unsigned short typeValue) // IN: ethernet type
{
if (typeValue <=1500) {
return EthClassCommon;
}
if (typeValue >= 0x600) {
switch (typeValue) {
case 0x0800: // IPv4
return EthClassIPv4;
case 0x0806: // ARP
return EthClassARP;
case 0x0BAD: // Banyan Vines
return EthClassUncommon;
case 0x2000: // Cisco CDP
return EthClassCommon;
case 0x6002: // DEC MOP Remote Console
case 0x6558: // Trans Ether Bridging [RFC1701]
case 0x6559: // Raw Frame Relay [RFC1701]
return EthClassUncommon;
case 0x8035: // Reverse ARP
return EthClassARP;
case 0x809B: // AppleTalk
case 0x80F3: // AppleTalk AARP
return EthClassUncommon;
case 0x8100: // VLAN special type
return EthClassVLAN;
case 0x8137: // Novell 8137
case 0x8138: // Novell 8138
return EthClassUncommon;
case 0x86DD: // IPv6
return EthClassIPv6;
case 0x876B: // TCP/IP Compression [RFC1144]
return EthClassUncommon;
case 0x886f: // Microsoft 886f
return EthClassCommon;
case 0x888e: // 802.1x (aka EAPOL)
case 0x88c7: // 802.11i pre-authentication (treated as EAPOL)
return EthClassEAPOL;
default:
return EthClassUnknown;
}
} else {
return EthClassUnknown;
}
}
#ifdef DBG
/*
*----------------------------------------------------------------------
*
* LookupTypeName --
*
* Examines and classifies the protocol type of an ethernet frame
*
* Results:
* Provides textual name of type in 'type' pointer.
*
*
* Side effects:
* May modify the contents of the packet. Length should be
* unchanged.
*
*----------------------------------------------------------------------
*/
#if _WIN32
_Use_decl_annotations_
#endif
void
LookupTypeName(unsigned short typeValue, // IN: ethernet type
char * type, // IN/OUT: string to store type name
size_t typeLen) // IN: size of out buffer
{
if (!type) {
return;
}
if (typeValue <=1500) {
SNPRINTF((type, typeLen, "length %u", typeValue));
return;
}
#ifdef _WIN32
#define STRCPY_S(a,b,c) RtlStringCbCopyA(a,b,c)
#else
#define STRCPY_S(a,b,c) MEMCPY(a, c, sizeof c)
#endif
if (typeValue >= 0x600) {
switch (typeValue) {
case 0x0800:
STRCPY_S(type, typeLen, "IPv4");
return;
case 0x0806:
STRCPY_S(type, typeLen, "ARP");
return;
case 0x0BAD:
STRCPY_S(type, typeLen, "Banyan VINES");
return;
case 0x2000:
STRCPY_S(type, typeLen, "Cisco CDP");
return;
case 0x6002:
STRCPY_S(type, typeLen, "DEC MOP Remote Console");
return;
case 0x6558:
STRCPY_S(type, typeLen, "Trans Ether Bridging [RFC1701]");
return;
case 0x6559:
STRCPY_S(type, typeLen, "Raw Frame Relay [RFC1701]");
return;
case 0x8035:
STRCPY_S(type, typeLen, "Reverse ARP");
return;
case 0x809B:
STRCPY_S(type, typeLen, "AppleTalk");
return;
case 0x80F3:
STRCPY_S(type, typeLen, "AppleTalk AARP");
return;
case 0x8100:
STRCPY_S(type, typeLen, "VLAN special type");
return;
case 0x8137:
STRCPY_S(type, typeLen, "Novell 8137");
return;
case 0x8138:
STRCPY_S(type, typeLen, "Novell 8138");
return;
case 0x86DD:
STRCPY_S(type, typeLen, "IPv6");
return;
case 0x876B:
STRCPY_S(type, typeLen, "TCP/IP Compression [RFC1144]");
return;
case 0x886f:
STRCPY_S(type, typeLen, "Microsoft 886f");
return;
case 0x888e:
STRCPY_S(type, typeLen, "EAPOL");
return;
case 0x88c7:
STRCPY_S(type, typeLen, "802.11i pre-auth");
return;
default:
SNPRINTF((type, typeLen, "unknown type 0x%04x", typeValue));
return;
}
} else {
SNPRINTF((type, typeLen, "invalid value 0x%04x", typeValue));
return;
}
}
#endif
/*
* Checksum-related functions. In certain cases the payload of a UDP
* packet needs to be modified. The following functions are used to
* calculate the new checksum based on the old checksum and an offset
* of the changes.
*/
/*
*----------------------------------------------------------------------
*
* SUM32 --
*
* performs 2's complement sum of the high bits and low bits
* in a 32-bit word, resulting in a 16-bit number (with
* potentially additional bits containing overflow)
*
* Results:
* Returns result described above
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE uint32
SUM32(uint32 in) { // IN: 32-bit number
return (in & 0xffff) + (in >> 16);
}
/*
*----------------------------------------------------------------------
*
* CalcChecksumDiff --
*
* Computes the differences between two checksums
*
* Results:
* Returns result described above
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static uint32
CalcChecksumDiff(uint32 sumBefore, // IN: checksum before modification
uint32 sumAfter) // IN: checksum after modification
{
uint32 diff; // stores resume to return
sumBefore = SUM32(sumBefore); // Convert sum to 16-bit + overflow
sumBefore = SUM32(sumBefore); // Convert sum to 16-bit
sumAfter = SUM32(sumAfter); // Convert sum to 16-bit + overflow
sumAfter = SUM32(sumAfter); // Convert sum to 16-bit
/*
* 2's complement versus 1's complement arthmetic requires the
* following piece of code. I don't completely understand
* why it's needed, but my sources (and my own testing) say it
* needs to be here.
*/
if (sumBefore > sumAfter) {
--sumAfter;
}
diff = sumAfter - sumBefore; // subtrace to get delta
diff = SUM32(diff); // incorporate overflow to get 16-bits
return diff;
}
/*
*----------------------------------------------------------------------
*
* UpdateSum --
*
* Computes a new internet checksum by using an existing
* checksum and a delta of changes to that checksum.
*
* Results:
* Returns new checksum
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static uint16
UpdateSum(uint16 oricheck, // IN: old checksum
uint32 sumDiff) // IN: delta of changes
{
uint32 sum;
uint16 sumShort;
if (sumDiff == 0) {
return oricheck;
}
sum = ( ~NTOHS(oricheck) & 0xffff); // undo net order & bit complement
sum += sumDiff; // add in difference
sum = SUM32(sum); // wrap any overflow
sum = SUM32(sum); // wrap any overflow (again; last wrap may cause overflow)
sumShort = ~(uint16)(sum); // truncate to 16-bits and complement bits
return HTONS(sumShort); // return result in network order
}
/*
*----------------------------------------------------------------------
*
* GetSystemUptime --
*
* Return current uptime of system, this is essentially a wrapper
* that checks for overflow. When
* overflow occurs the existing last used times reduced by half.
* As part of the solution, the returned value always has the
* highest bit turned on.
*
* Previously, this was a wrapper for NdisGetSystemUptime().
* Recent updates to the DDK/WDK have confused this function - there
* is now a depricated 32-bit version and a 64-bit version only
* available on newer version of the OS. Rather than trying to be
* clever, we will use KeQueryTickCount instead.
*
* This function should only be called with the lock held.
*
* Results:
* Returns uptime of system (skewed so always in upper half
* of numeric range for 32-bits)
*
* Side effects:
* Updates the "last uptime" stored in adapter struct, and may
* modify the uptimes of all entries in the hash table.
*
*----------------------------------------------------------------------
*/
static SmacLastAccess
GetSystemUptime(SMACState *state) // IN: smac state
{
SmacLastAccess currentUptime;
#ifdef _WIN32
{
LARGE_INTEGER arg;
KeQueryTickCount(&arg);
currentUptime = arg.QuadPart;
}
#else
currentUptime = SMACL_GetUptime();
#endif
/*
* Force on the highest bit. This basically means that recent
* values have the high bit turned on, while values that were
* obtained prior to the last overflow will have the highest bit
* (and potentially other neighboring bits) turned off.
*/
currentUptime |= (SmacLastAccess)1 << (sizeof currentUptime * 8 - 1);
/* If overflow occurred, then reduce all existing values by half */
if (currentUptime < state->lastUptimeRead) {
uint32 i;
VNETKdPrint((MODULE_NAME "GetSystemUptime: overflow detected, "
"adjusting counters\n"));
for (i = 0; i < SMAC_HASH_TABLE_SIZE; ++i) {
IPmacLookupEntry *entry = state->IPlookupTable[i];
while (entry) {
entry->lastAccess >>= 1; /* reduce value by half */
entry = entry->ipNext;
}
}
}
state->lastUptimeRead = currentUptime;
return currentUptime;
}
/*
*----------------------------------------------------------------------
*
* GetPacketLength --
*
* Returns the total length of data in a packet
*
* Results:
* returns length of packet
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static INLINE uint32
GetPacketLength(SMACPacket *packet) // IN: packet
{
ASSERT(packet);
#ifdef _WIN32
return packet->buf1Len + packet->buf2Len;
#elif defined __linux__
ASSERT(packet->skb);
return (uint32)packet->len;
#else /* __APPLE__ */
ASSERT(packet->m);
return SMACL_PacketLength(packet->m);
#endif
}
/*
*----------------------------------------------------------------------
*
* GetPacketData --
*
* Copies select portion of packet
*
* Results:
* TRUE if data was safely copied, otherwise FALSE (e.g., offset
* too large, packet too small, etc).
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Bool
GetPacketData(SMACPacket *packet, // IN: packet to copy from
uint32 offset, // IN: offset in packet to copy from
uint32 length, // IN: length to copy
void *data) // OUT: data (caller must pass in buffer >= length)
{
#ifdef _WIN32
uint32 copyOffset = 0;
uint32 copyLength = 0;
#endif
ASSERT(packet);
ASSERT(data);
ASSERT(length > 0);
/* check length, be sure to handle case where offset = -1, length > 0 */
if (length == 0 || offset + length > GetPacketLength(packet) ||
offset + length < offset) {
/* packet not long enough for data */
return FALSE;
}
#ifdef _WIN32
/* if offset starts in the first buffer, then copy from first buffer */
if (offset < packet->buf1Len) {
copyOffset = offset;
copyLength = (offset + length > packet->buf1Len)?
(packet->buf1Len - copyOffset):(length);
MEMCPY(data, ((uint8*)packet->buf1) + copyOffset, copyLength);
offset = packet->buf1Len; /* advance offset to start of second buffer */
data = ((uint8*)data) + copyLength;
length -= copyLength;
}
/* copy any remaining data from second buffer */
if (length) {
copyOffset = offset - packet->buf1Len;
copyLength = length;
MEMCPY(data, ((uint8*)packet->buf2) + copyOffset, copyLength);
}
#elif __linux__
MEMCPY(data, packet->startOfData + offset, length);
#else /* __APPLE__ */
SMACL_CopyDataFromPkt(packet->m, offset, data, length);
#endif /* _WIN32 */
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* ClonePacket --
*
* Makes a private copy of the incoming packet. This modified
* copy is private and can be modified at will. Caller is
* responsible for freeing the cloned packet.
*
* Results:
* TRUE if clone was successful, otherwise FALSE.
*
* Side effects:
* Duplicates a packet.
*
*----------------------------------------------------------------------
*/
static Bool
ClonePacket(SMACPackets *packets) // IN: struct in which to clone packet
{
#ifdef _WIN32
VNetPacket * packetClone = NULL;
ASSERT(packets);
packetClone = VNet_PacketAllocate(packets->orig.buf1Len +
packets->orig.buf2Len,
"SMAC_CheckPacketToHost");
if (!packetClone) {
VNETKdPrint((MODULE_NAME " ToHost: couldn't clone packet\n"));
return FALSE;
}
if (packets->orig.buf1Len) {
MEMCPY(packetClone->data, packets->orig.buf1, packets->orig.buf1Len);
}
if (packets->orig.buf2Len) {
MEMCPY(packetClone->data + packets->orig.buf1Len, packets->orig.buf2,
packets->orig.buf2Len);
}
packets->clone.buf1 = packetClone->data;
packets->clone.buf1Len = packetClone->len;
packets->clone.buf2 = NULL;
packets->clone.buf2Len = 0;
packets->clonedPacket = packetClone;
return TRUE;
#elif defined __linux__
packets->clone.skb = SMACL_DupPacket(packets->orig.skb);
if (packets->clone.skb) {
packets->clone.startOfData = (packets->orig.startOfData -
SMACL_PacketData(packets->orig.skb)) +
SMACL_PacketData(packets->clone.skb);
packets->clone.len = packets->orig.len;
}
return packets->clone.skb != NULL;
#else /* __APPLE__ */
/*
* Don't need to clone packet again. We could even get rid of "clone" from
* the SMACPackets struct, but this minimizes differences from other OSes.
*/
packets->clone.m = packets->orig.m;
return packets->clone.m != NULL;
#endif /* _WIN32 */
}
/*
*----------------------------------------------------------------------
*
* CopyDataToClonedPacket --
*
* Makes changes to the private clone copy of a packet.
*
* Results:
* TRUE if modification was successful, otherwise FALSE.
*
* Side effects:
* Modifies a packet.
*
*----------------------------------------------------------------------
*/
static Bool
CopyDataToClonedPacket(SMACPackets *packets, // IN: packets
const void * source, // IN: data to copy to packet
uint32 offset, // IN: dest offset at which to copy
uint32 length) // IN: length of data to copy
{
#ifdef _WIN32
ASSERT(packets);
ASSERT(packets->clone.buf1);
ASSERT(packets->clone.buf1Len > offset + length);
ASSERT(packets->clone.buf2 == NULL);
ASSERT(packets->clone.buf2Len == 0);
/*
* For windows this code presumes that all of the clone packet's data is
* in the first buffer.
*/
if (!packets || !packets->clone.buf1 || packets->clone.buf1Len
<= offset + length) {
ASSERT(0); // should never occur
return FALSE;
}
MEMCPY((uint8 *)(packets->clone.buf1) + offset, source, length);
#elif defined __linux__
ASSERT(packets);
ASSERT(packets->clone.skb);
MEMCPY((uint8 *)(packets->clone.startOfData) + offset,
source, length);
#else /* __APPLE __ */
ASSERT(packets);
ASSERT(packets->clone.m);
SMACL_CopyDataToPkt(packets->clone.m, offset, source, length);
#endif
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* SetPacketByte --
*
* Makes changes to a packet, either public or private
*
* Results:
* TRUE if modification was successful, otherwise FALSE.
*
* Side effects:
* Modifies a packet.
*
*----------------------------------------------------------------------
*/
static Bool
SetPacketByte(SMACPacket *packet, // IN: packet
uint32 offset, // IN: offset to change
uint8 data) // IN: data to set
{
ASSERT(packet);
#ifdef _WIN32
/* check length, be sure to handle case where offset = -1, length > 0 */
if (offset > GetPacketLength(packet)) {
/* packet not long enough for data */
return FALSE;
}
/* if offset starts in the first buffer, then copy from first buffer */
if (offset < packet->buf1Len) {
((uint8*)packet->buf1)[offset] = data;
} else {
offset -= packet->buf1Len;
((uint8*)packet->buf2)[offset] = data;
}
#elif __linux__
ASSERT(packet);
ASSERT(packet->skb);
((uint8*)packet->startOfData)[offset] = data;
#else
ASSERT(packet);
ASSERT(packet->m);
SMACL_CopyDataToPkt(packet->m, offset, &data, 1);
#endif /* _WIN32 */
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* CopyDataForPacketFromHost --
*
* When receiving data from the host, this function is used
* to make changes to the packet. Since the only changes we
* ever make are to replace one Ethernet MAC with another, this
* function currently just does MAC replacement.
*
* Results:
* TRUE if modification was successful, otherwise FALSE.
*
* Side effects:
* Modifies a packet.
*
*----------------------------------------------------------------------
*/
Bool
CopyDataForPacketFromHost(SMACPackets *packets, // IN/OUT: packets struct
uint32 changeNum, // IN: serialized # of change
uint32 offset, // IN: byte offset for change
const uint8 *macAddress) // IN: new MAC to add to packet
{
#ifdef _WIN32
MacReplacementTable *table = NULL;
ASSERT(packets);
ASSERT(macAddress);
table = packets->table;
ASSERT(table);
ASSERT(changeNum == table->numOfOffsets);
UNREFERENCED_PARAMETER(changeNum);
if (table->numOfOffsets == 0) {
MEMCPY(table->mac, macAddress, ETH_ALEN);
}
table->offsets[table->numOfOffsets++] = offset;
#else /* _WIN32 */
/* clone packet if this is the first change */
if (changeNum == 0 && !ClonePacket(packets)) {
VNETKdPrint((MODULE_NAME "FromHostIP: couldn't clone packet\n"));
return FALSE;
}
CopyDataToClonedPacket(packets, macAddress, offset, ETH_ALEN);
#endif /* _WIN32 */
return TRUE;
}