4364 lines
140 KiB
C
4364 lines
140 KiB
C
/*++
|
|
|
|
Copyright(c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
brdgcomp.c
|
|
|
|
Abstract:
|
|
|
|
Ethernet MAC level bridge.
|
|
Compatibility-Mode section
|
|
|
|
Author:
|
|
|
|
Mark Aiken
|
|
|
|
Environment:
|
|
|
|
Kernel mode driver
|
|
|
|
Revision History:
|
|
|
|
September 2000 - Original version
|
|
|
|
Notes
|
|
|
|
Currently, this code only works with traditional Ethernet framing (dest, src, ethertype).
|
|
Much of the code would need to be changed to support IEEE 802.3-style framing
|
|
(dest, src, size, LLC DSAP, LLC SSAP, LLC type).
|
|
|
|
--*/
|
|
|
|
#define NDIS_MINIPORT_DRIVER
|
|
#define NDIS50_MINIPORT 1
|
|
#define NDIS_WDM 1
|
|
|
|
#pragma warning( push, 3 )
|
|
#include <ndis.h>
|
|
|
|
// TCPIP.SYS structure definitions
|
|
#include <ipinfo.h>
|
|
#include <tdiinfo.h>
|
|
#include <ntddtcp.h>
|
|
#include <ntddip.h>
|
|
#pragma warning( pop )
|
|
|
|
#include "bridge.h"
|
|
#include "brdgcomp.h"
|
|
|
|
#include "brdgfwd.h"
|
|
#include "brdgbuf.h"
|
|
|
|
// ===========================================================================
|
|
//
|
|
// TYPES
|
|
//
|
|
// ===========================================================================
|
|
|
|
// An IPv4 address
|
|
typedef UINT32 IPADDRESS;
|
|
typedef PUINT32 PIPADDRESS;
|
|
|
|
// Types of ARP packets
|
|
typedef enum
|
|
{
|
|
ArpRequest,
|
|
ArpReply
|
|
} ARP_TYPE;
|
|
|
|
// ===========================================================================
|
|
//
|
|
// CONSTANTS
|
|
//
|
|
// ===========================================================================
|
|
|
|
// Size of the payload of an IPv4 ARP packet
|
|
#define SIZE_OF_ARP_DATA 28 // bytes
|
|
|
|
// Total size of an IPv4 ARP packet, including framing
|
|
#define SIZE_OF_ARP_PACKET (SIZE_OF_ARP_DATA + ETHERNET_HEADER_SIZE)
|
|
|
|
// Size of a basic IPv4 header, not including options
|
|
#define SIZE_OF_BASIC_IP_HEADER 20 // bytes
|
|
|
|
// Minimum amount of frame data we need to parse the IP header
|
|
#define MINIMUM_SIZE_FOR_IP (ETHERNET_HEADER_SIZE + SIZE_OF_BASIC_IP_HEADER)
|
|
|
|
// Size of a basic UDP header
|
|
#define SIZE_OF_UDP_HEADER 8 // bytes
|
|
|
|
// Minimum size of the payload of a BOOTP packet
|
|
#define SIZE_OF_BASIC_BOOTP_PACKET 236 // bytes
|
|
|
|
// The IP Ethertype
|
|
const USHORT IP_ETHERTYPE = 0x0800;
|
|
|
|
// The ARP Ethertype
|
|
const USHORT ARP_ETHERTYPE = 0x0806;
|
|
|
|
// The UDP IP protocol type
|
|
const UCHAR UDP_PROTOCOL = 0x11;
|
|
|
|
// Number of hash buckets in the IP and pending-ARP tables. This must
|
|
// be a power of 2 for our hash function to work propery.
|
|
#define NUM_HASH_BUCKETS 256
|
|
|
|
// Number of hash buckets for the pending-DHCP table. This
|
|
// must be a power of 2 for our hash function to work properly.
|
|
#define NUM_DHCP_HASH_BUCKETS 32
|
|
|
|
// The "shift factor" for our IP next-hop cache. The number of entries
|
|
// in the cache is 2 ^ (this number)
|
|
#define HOP_CACHE_SHIFT_FACTOR 8 // 256 cache entries
|
|
|
|
// Default size cap for the IP forwarding table
|
|
#define DEFAULT_MAX_IP_TABLE_SIZE (500 * 1024) // 500K in bytes
|
|
|
|
// Name of the registry parameter that optionally specifies the max table size
|
|
const PWCHAR gMaxIPTableSizeParameterName = L"MaxIPTableSize";
|
|
|
|
// Default size cap for the pending-ARP table
|
|
#define DEFAULT_MAX_ARP_TABLE_SIZE (100 * 1024) // 100K in bytes
|
|
|
|
// Name of the registry parameter that optionally specifies the max table size
|
|
const PWCHAR gMaxARPTableSizeParameterName = L"MaxARPTableSize";
|
|
|
|
// Default size cap for the pending-DHCP table
|
|
#define DEFAULT_MAX_DHCP_TABLE_SIZE (50 * 1024) // 50K in bytes
|
|
|
|
// Name of the registry parameter that optionally specifies the max table size
|
|
const PWCHAR gMaxDHCPTableSizeParameterName = L"MaxDHCPTableSize";
|
|
|
|
//
|
|
// Timeout length for IP forwarding table entries
|
|
//
|
|
// This should be somewhat longer than the time it takes hosts to age out
|
|
// their ARP table entries, since we learn the location of IP hosts
|
|
// by ARP traffic. Current Windows implementations age out their
|
|
// ARP table entries after 2 minutes if there has been no traffic from
|
|
// the station corresponding to the entry.
|
|
//
|
|
// We keep our forwarding table entries alive indefinitely as long as we
|
|
// continue to see IP traffic from the hosts we have information about.
|
|
// Windows implementations will age out their ARP entries under those
|
|
// conditions after 20mins or so.
|
|
//
|
|
#define MAX_IP_ENTRY_AGE (5 * 60 * 1000) // 5 minutes in ms
|
|
|
|
//
|
|
// Timeout length for pending-ARP table entries
|
|
//
|
|
// This should be somewhat longer than the maximum time hosts will wait to
|
|
// hear the results of an ARP discovery before timing out. Windows boxes
|
|
// have a giveup time of 1s.
|
|
//
|
|
// Note that it is not destructive to deliver ARP reply packets to a station
|
|
// after it has given up or even after its initial discovery was
|
|
// satisfied.
|
|
//
|
|
#define MAX_ARP_ENTRY_AGE (10 * 1000) // 10 seconds
|
|
|
|
//
|
|
// Timeout length for pending-DHCP table entries
|
|
//
|
|
// RFC 2131 mentions that clients may wait as long as 60 seconds for an
|
|
// ACK. Have the timeout be somewhat longer than even that.
|
|
//
|
|
#define MAX_DHCP_ENTRY_AGE (90 * 1000) // 1 1/2 minutes
|
|
|
|
// ===========================================================================
|
|
//
|
|
// DECLARATIONS
|
|
//
|
|
// ===========================================================================
|
|
|
|
// Structure to express the information carried in ARP packets
|
|
typedef struct _ARPINFO
|
|
{
|
|
|
|
ARP_TYPE type;
|
|
IPADDRESS ipSource, ipTarget;
|
|
UCHAR macSource[ETH_LENGTH_OF_ADDRESS];
|
|
UCHAR macTarget[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
} ARPINFO, *PARPINFO;
|
|
|
|
// Structure to express the information carried in an IP header
|
|
typedef struct _IP_HEADER_INFO
|
|
{
|
|
|
|
UCHAR protocol;
|
|
IPADDRESS ipSource, ipTarget;
|
|
USHORT headerSize;
|
|
|
|
} IP_HEADER_INFO, *PIP_HEADER_INFO;
|
|
|
|
// Structure of our IP forwarding hash table entries
|
|
typedef struct _IP_TABLE_ENTRY
|
|
{
|
|
|
|
HASH_TABLE_ENTRY hte; // Required for hash table use
|
|
|
|
// Protects the following fields
|
|
NDIS_SPIN_LOCK lock;
|
|
|
|
PADAPT pAdapt;
|
|
UCHAR macAddr[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
} IP_TABLE_ENTRY, *PIP_TABLE_ENTRY;
|
|
|
|
//
|
|
// Structure of the pending-ARP table keys. We want this to get
|
|
// packet into 8 bytes.
|
|
//
|
|
typedef struct _ARP_TABLE_KEY
|
|
{
|
|
IPADDRESS ipTarget;
|
|
IPADDRESS ipReqestor;
|
|
} ARP_TABLE_KEY, *PARP_TABLE_KEY;
|
|
|
|
// Structure of our pending-ARP hash table entries
|
|
typedef struct _ARP_TABLE_ENTRY
|
|
{
|
|
|
|
HASH_TABLE_ENTRY hte; // Required for hash table use
|
|
|
|
// Protects the following fields
|
|
NDIS_SPIN_LOCK lock;
|
|
|
|
// Information on the station that was trying to discover this host
|
|
// The discovering station's IP address is part of the entry key.
|
|
PADAPT pOriginalAdapt;
|
|
UCHAR originalMAC[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
} ARP_TABLE_ENTRY, *PARP_TABLE_ENTRY;
|
|
|
|
// Structure of our DHCP-relay table entries
|
|
typedef struct _DHCP_TABLE_ENTRY
|
|
{
|
|
HASH_TABLE_ENTRY hte; // Required for hash table use
|
|
|
|
// Protects the following fields
|
|
NDIS_SPIN_LOCK lock;
|
|
|
|
UCHAR requestorMAC[ETH_LENGTH_OF_ADDRESS];
|
|
PADAPT pRequestorAdapt;
|
|
} DHCP_TABLE_ENTRY, *PDHCP_TABLE_ENTRY;
|
|
|
|
// Structure for deferring an ARP packet transmission
|
|
typedef struct _DEFERRED_ARP
|
|
{
|
|
ARPINFO ai;
|
|
PADAPT pTargetAdapt;
|
|
} DEFERRED_ARP, *PDEFERRED_ARP;
|
|
|
|
// Per-adapter rewriting function
|
|
typedef VOID (*PPER_ADAPT_EDIT_FUNC)(PUCHAR, PADAPT, PVOID);
|
|
|
|
// ===========================================================================
|
|
//
|
|
// GLOBALS
|
|
//
|
|
// ===========================================================================
|
|
|
|
//
|
|
// Whether or not there are *any* compatibility-mode adapters in our list
|
|
// at the moment. Is updated in the protocol module with a write lock
|
|
// held on the global adapter list
|
|
//
|
|
BOOLEAN gCompatAdaptersExist = FALSE;
|
|
|
|
//
|
|
// Our list of the bridge machine's IP addresses (passed down through an
|
|
// OID). The list is allocated on the heap and is protected by
|
|
// gLocalIPAddressLock.
|
|
//
|
|
PIPADDRESS gLocalIPAddressList = NULL;
|
|
ULONG gLocalIPAddressListLength = 0L;
|
|
NDIS_RW_LOCK gLocalIPAddressListLock;
|
|
|
|
//
|
|
// The IP address-based forwarding table
|
|
//
|
|
PHASH_TABLE gIPForwardingTable;
|
|
|
|
//
|
|
// Our table to hold pending ARP requests so we can proxy back replies
|
|
//
|
|
PHASH_TABLE gPendingARPTable;
|
|
|
|
//
|
|
// Our table to hold pending DHCP requests so we can translate DHCP packets
|
|
// appropriately (the MAC address of the requesting station is carried
|
|
// in a DHCP request and has to be edited when we relay it)
|
|
//
|
|
PHASH_TABLE gPendingDHCPTable;
|
|
|
|
//
|
|
// A cache of IP next-hop information to avoid hammering the IP drivers's
|
|
// route table
|
|
//
|
|
CACHE gNextHopCache;
|
|
|
|
// Special IP address indicating a negative cache entry (we tried previously
|
|
// and got no answer)
|
|
const IPADDRESS NO_ADDRESS = 0xFFFFFFFF;
|
|
|
|
// Whether we have an overall MAC address for the bridge miniport yet
|
|
BOOLEAN gCompHaveMACAddress = FALSE;
|
|
|
|
// Our overall MAC address (cached here instead of calling the miniport
|
|
// section all the time to increase perf)
|
|
UCHAR gCompMACAddress[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
// Pointers and handles for interacting with TCP
|
|
HANDLE gTCPFileHandle = NULL;
|
|
PFILE_OBJECT gTCPFileObject = NULL;
|
|
PDEVICE_OBJECT gTCPDeviceObject = NULL;
|
|
|
|
// Pointers and handles for interacting with IP
|
|
HANDLE gIPFileHandle = NULL;
|
|
PFILE_OBJECT gIPFileObject = NULL;
|
|
PDEVICE_OBJECT gIPDeviceObject = NULL;
|
|
|
|
// Lock to protect the references above
|
|
NDIS_SPIN_LOCK gTCPIPLock;
|
|
|
|
// IRP posted to TCPIP for notifications of when the route table changes.
|
|
// Manipulated with InterlockedExchange.
|
|
PIRP gIPRouteChangeIRP = NULL;
|
|
|
|
// Refcount to allow us to block and wait when people are using the TCP
|
|
// driver
|
|
WAIT_REFCOUNT gTCPIPRefcount;
|
|
|
|
// ===========================================================================
|
|
//
|
|
// PRIVATE PROTOTYPES
|
|
//
|
|
// ===========================================================================
|
|
|
|
BOOLEAN
|
|
BrdgCompDecodeARPPacket(
|
|
IN PUCHAR pPacketData,
|
|
IN UINT dataLen,
|
|
OUT PARPINFO pARPInfo
|
|
);
|
|
|
|
VOID
|
|
BrdgCompTransmitARPPacket(
|
|
IN PADAPT pAdapt,
|
|
IN PARPINFO pARPInfo
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompDecodeIPHeader(
|
|
IN PUCHAR pHeader,
|
|
OUT PIP_HEADER_INFO piphi
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessOutboundARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PADAPT pTargetAdapt
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessOutboundNonARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PADAPT pTargetAdapt
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundNonARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen
|
|
);
|
|
|
|
VOID
|
|
BrdgCompTransmitDeferredARP(
|
|
IN PVOID pData
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundBootPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PIP_HEADER_INFO piphi,
|
|
IN PUCHAR pBootPData
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessOutboundBootPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PADAPT pTargetAdapt,
|
|
IN PUCHAR pBootPData,
|
|
IN PIP_HEADER_INFO piphi
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompIsUnicastIPAddress(
|
|
IN IPADDRESS ip
|
|
);
|
|
|
|
VOID
|
|
BrdgCompAttachToTCPIP(
|
|
IN PVOID ignored
|
|
);
|
|
|
|
VOID
|
|
BrdgCompDetachFromTCPIP(
|
|
IN PVOID ignored
|
|
);
|
|
|
|
BOOLEAN
|
|
BrdgCompIsLocalIPAddress(
|
|
IN IPADDRESS ipAddr
|
|
);
|
|
|
|
// ===========================================================================
|
|
//
|
|
// INLINES / MACROS
|
|
//
|
|
// ===========================================================================
|
|
|
|
//
|
|
// This retrieves the Ethertype of an Ethernet frame from a pointer
|
|
// to its header.
|
|
//
|
|
__forceinline
|
|
USHORT
|
|
BrdgCompGetEtherType(
|
|
IN PUCHAR pEtherHeader
|
|
)
|
|
{
|
|
USHORT retVal;
|
|
|
|
// The two bytes immediately following the source and destination addresses
|
|
// encode the Ethertype, most significant byte first.
|
|
retVal = 0L;
|
|
retVal |= (pEtherHeader[2 * ETH_LENGTH_OF_ADDRESS]) << 8;
|
|
retVal |= pEtherHeader[2 * ETH_LENGTH_OF_ADDRESS + 1];
|
|
|
|
return retVal;
|
|
}
|
|
|
|
//
|
|
// Transmits a packet on an adapter after rewriting the source MAC address
|
|
// to be the adapter's own MAC address.
|
|
//
|
|
// The caller relinquishes ownership of the packet with this call.
|
|
//
|
|
__forceinline
|
|
VOID
|
|
BrdgCompSendPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN PADAPT pAdapt
|
|
)
|
|
{
|
|
// Rewrite the source MAC address to be our address
|
|
ETH_COPY_NETWORK_ADDRESS(pPacketData + ETH_LENGTH_OF_ADDRESS, pAdapt->MACAddr);
|
|
BrdgFwdSendPacketForCompat(pPacket, pAdapt);
|
|
}
|
|
|
|
//
|
|
// Transmits a packet, dealing with an optional editing function if one is
|
|
// present
|
|
//
|
|
__forceinline
|
|
VOID
|
|
BrdgCompEditAndSendPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN PADAPT pAdapt,
|
|
IN PPER_ADAPT_EDIT_FUNC pFunc,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
if( pFunc != NULL )
|
|
{
|
|
(*pFunc)(pPacketData, pAdapt, pData);
|
|
}
|
|
|
|
BrdgCompSendPacket( pPacket, pPacketData, pAdapt );
|
|
}
|
|
|
|
//
|
|
// Transmits a packet, dealing with the possibility that we are not allowed to
|
|
// retain the packet and setting the destination MAC address to a specified
|
|
// value
|
|
//
|
|
// Returns whether the input packet was retained
|
|
//
|
|
__inline
|
|
BOOLEAN
|
|
BrdgCompEditAndSendPacketOrPacketCopy(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pDestMAC,
|
|
IN PADAPT pAdapt,
|
|
IN PPER_ADAPT_EDIT_FUNC pFunc,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
UINT dataLen;
|
|
|
|
SAFEASSERT( (pPacket != NULL) && (pPacketData != NULL) );
|
|
|
|
if( !bCanRetain )
|
|
{
|
|
// We aren't allowed to use the original packet. Make a copy.
|
|
pPacket = BrdgFwdMakeCompatCopyPacket(pPacket, &pPacketData, &dataLen, FALSE);
|
|
}
|
|
|
|
if( (pPacket != NULL) && (pPacketData != NULL) )
|
|
{
|
|
// Poke the destination MAC address
|
|
ETH_COPY_NETWORK_ADDRESS(pPacketData, pDestMAC);
|
|
BrdgCompEditAndSendPacket(pPacket, pPacketData, pAdapt, pFunc, pData);
|
|
}
|
|
|
|
// If we were allowed to retain the packet, we did.
|
|
return bCanRetain;
|
|
}
|
|
|
|
//
|
|
// Indicates a packet to the local machine. If the target MAC address was previously
|
|
// the adapter's hardware MAC address, it is rewritten to the bridge adapter's
|
|
// overall MAC address.
|
|
//
|
|
// The caller relinquishes ownership of the packet with this call.
|
|
//
|
|
__inline
|
|
VOID
|
|
BrdgCompIndicatePacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN PADAPT pAdapt // Receiving adapter
|
|
)
|
|
{
|
|
// No packet indications can occur until we have a MAC address
|
|
if( gCompHaveMACAddress )
|
|
{
|
|
UINT Result;
|
|
|
|
// See if this frame was targeted at this adapter's MAC address.
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ(pPacketData, pAdapt->MACAddr, &Result);
|
|
|
|
if( Result == 0 )
|
|
{
|
|
ETH_COPY_NETWORK_ADDRESS( pPacketData, gCompMACAddress );
|
|
}
|
|
else
|
|
{
|
|
// We expect to only be indicating frames unicast to this machine
|
|
// or sent to bcast / multicast hardware addresses.
|
|
SAFEASSERT( ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData) );
|
|
}
|
|
|
|
BrdgFwdIndicatePacketForCompat( pPacket );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Indicates a packet to the local machine, making a copy of the packet if
|
|
// necessary.
|
|
//
|
|
__inline
|
|
BOOLEAN
|
|
BrdgCompIndicatePacketOrPacketCopy(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PADAPT pAdapt,
|
|
IN PPER_ADAPT_EDIT_FUNC pEditFunc,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
if( bCanRetain )
|
|
{
|
|
if( pEditFunc != NULL )
|
|
{
|
|
(*pEditFunc)(pPacketData, LOCAL_MINIPORT, pData);
|
|
}
|
|
|
|
BrdgCompIndicatePacket( pPacket, pPacketData, pAdapt );
|
|
}
|
|
else
|
|
{
|
|
UINT packetLen;
|
|
|
|
// Make our own copy of the packet for indication
|
|
pPacket = BrdgFwdMakeCompatCopyPacket( pPacket, &pPacketData, &packetLen, FALSE );
|
|
|
|
if( pPacket != NULL )
|
|
{
|
|
if( pEditFunc != NULL )
|
|
{
|
|
(*pEditFunc)(pPacketData, LOCAL_MINIPORT, pData);
|
|
}
|
|
|
|
BrdgCompIndicatePacket( pPacket, pPacketData, pAdapt );
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(COMPAT, ("Failed to acquire a packet for indication in BrdgCompIndicatePacketOrPacketCopy\n"));
|
|
}
|
|
}
|
|
|
|
// If we were allowed to retain the packet, we did.
|
|
return bCanRetain;
|
|
}
|
|
|
|
//
|
|
// The IP and UDP checksums treat the data they are checksumming as a
|
|
// sequence of 16-bit words. The checksum is carried as the bitwise
|
|
// inverse of the actual checksum (~C). The formula for calculating
|
|
// the new checksum as transmitted, ~C', given that a 16-bit word of
|
|
// the checksummed data has changed from w to w' is
|
|
//
|
|
// ~C' = ~C + w + ~w' (addition in ones-complement)
|
|
//
|
|
// This function returns the updated checksum given the original checksum
|
|
// and the original and new values of a word in the checksummed data.
|
|
//
|
|
__forceinline
|
|
USHORT
|
|
BrdgCompRecalcChecksum(
|
|
IN USHORT oldChecksum,
|
|
IN USHORT oldWord,
|
|
IN USHORT newWord
|
|
)
|
|
{
|
|
ULONG sum;
|
|
|
|
sum = oldChecksum + oldWord + ((~(newWord)) & 0xFFFF);
|
|
return (USHORT)((sum & 0xFFFF) + (sum >> 16));
|
|
}
|
|
|
|
//
|
|
// Rewrites a BootP packet so the client MAC address in the packet payload
|
|
// is the given new MAC address
|
|
//
|
|
__inline
|
|
BrdgCompRewriteBootPClientAddress(
|
|
IN PUCHAR pPacketData,
|
|
IN PIP_HEADER_INFO piphi,
|
|
IN PUCHAR newMAC
|
|
)
|
|
{
|
|
USHORT checkSum;
|
|
PUCHAR pBootPData, pCheckSum, pDestMAC, pSrcMAC;
|
|
UINT i;
|
|
|
|
// The BOOTP packet lives right after the UDP header
|
|
pBootPData = pPacketData + ETHERNET_HEADER_SIZE + piphi->headerSize + SIZE_OF_UDP_HEADER;
|
|
|
|
// The checksum lives at offset 7 in the UDP packet.
|
|
pCheckSum = pPacketData + ETHERNET_HEADER_SIZE + piphi->headerSize + 6;
|
|
checkSum = 0;
|
|
checkSum = pCheckSum[0] << 8;
|
|
checkSum |= pCheckSum[1];
|
|
|
|
// Replace the client's hardware address, updating the checksum as we go.
|
|
// The client's hardware address lives at offset 29 in the BOOTP packet
|
|
pSrcMAC = newMAC;
|
|
pDestMAC = &pBootPData[28];
|
|
|
|
for( i = 0 ; i < ETH_LENGTH_OF_ADDRESS / 2; i++ )
|
|
{
|
|
checkSum = BrdgCompRecalcChecksum( checkSum,
|
|
(USHORT)(pDestMAC[0] << 8 | pDestMAC[1]),
|
|
(USHORT)(pSrcMAC[0] << 8 | pSrcMAC[1]) );
|
|
|
|
pDestMAC[0] = pSrcMAC[0];
|
|
pDestMAC[1] = pSrcMAC[1];
|
|
|
|
pDestMAC += 2;
|
|
pSrcMAC += 2;
|
|
}
|
|
|
|
// Write the new checksum back out
|
|
pCheckSum[0] = (UCHAR)(checkSum >> 8);
|
|
pCheckSum[1] = (UCHAR)(checkSum & 0xFF);
|
|
}
|
|
|
|
//
|
|
// Rewrites an oubound ARP packet so the source MAC address carried in the payload
|
|
// matches the MAC address of the outbound adapter
|
|
//
|
|
VOID
|
|
BrdgCompRewriteOutboundARPPacket(
|
|
IN PUCHAR pPacketData,
|
|
IN PADAPT pAdapt,
|
|
IN PVOID ignored
|
|
)
|
|
{
|
|
//
|
|
// Rewrite the source MAC address so it is the MAC address of the adapter the
|
|
// request is going out on.
|
|
//
|
|
pPacketData[22] = pAdapt->MACAddr[0];
|
|
pPacketData[23] = pAdapt->MACAddr[1];
|
|
pPacketData[24] = pAdapt->MACAddr[2];
|
|
pPacketData[25] = pAdapt->MACAddr[3];
|
|
pPacketData[26] = pAdapt->MACAddr[4];
|
|
pPacketData[27] = pAdapt->MACAddr[5];
|
|
|
|
// Leave the rewriting of the MAC address in the actual Ethernet header to
|
|
// BrdgCompSendPacket(), which always overwrites the source MAC address
|
|
// with the adapter's MAC address.
|
|
}
|
|
|
|
//
|
|
// Provides a PDEVICE_OBJECT and a PFILE_OBJECT that can be used to talk to
|
|
// TCPIP.SYS. Returns TRUE if a channel is open and the pointers can be used,
|
|
// FALSE otherwise.
|
|
//
|
|
__inline
|
|
BOOLEAN
|
|
BrdgCompAcquireTCPIP(
|
|
OUT PDEVICE_OBJECT OPTIONAL *pTCPpdo,
|
|
OUT PFILE_OBJECT OPTIONAL *pTCPpfo,
|
|
OUT PDEVICE_OBJECT OPTIONAL *pIPpdo,
|
|
OUT PFILE_OBJECT OPTIONAL *pIPpfo
|
|
)
|
|
{
|
|
BOOLEAN rc = FALSE;
|
|
|
|
if( BrdgIncrementWaitRef(&gTCPIPRefcount) )
|
|
{
|
|
NdisAcquireSpinLock( &gTCPIPLock );
|
|
|
|
SAFEASSERT( gTCPDeviceObject != NULL );
|
|
SAFEASSERT( gTCPFileHandle != NULL );
|
|
SAFEASSERT( gTCPFileObject != NULL );
|
|
SAFEASSERT( gIPFileHandle != NULL );
|
|
SAFEASSERT( gIPDeviceObject != NULL );
|
|
SAFEASSERT( gIPFileObject != NULL );
|
|
|
|
if( pTCPpdo != NULL )
|
|
{
|
|
*pTCPpdo = gTCPDeviceObject;
|
|
}
|
|
|
|
if( pTCPpfo != NULL )
|
|
{
|
|
*pTCPpfo = gTCPFileObject;
|
|
}
|
|
|
|
if( pIPpdo != NULL )
|
|
{
|
|
*pIPpdo = gIPDeviceObject;
|
|
}
|
|
|
|
if( pIPpfo != NULL )
|
|
{
|
|
*pIPpfo = gIPFileObject;
|
|
}
|
|
|
|
rc = TRUE;
|
|
NdisReleaseSpinLock( &gTCPIPLock );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Releases the refcount on our connection to the TCPIP driver after a
|
|
// previous call to BrdgCompAcquireTCPIP().
|
|
//
|
|
__inline
|
|
VOID
|
|
BrdgCompReleaseTCPIP()
|
|
{
|
|
BrdgDecrementWaitRef( &gTCPIPRefcount );
|
|
}
|
|
|
|
// ====================================================================
|
|
//
|
|
// These small helper functions would be inline except we need to pass
|
|
// pointers to them
|
|
//
|
|
// ====================================================================
|
|
|
|
//
|
|
// Rewrites a BootP packet for a particular adapter
|
|
//
|
|
VOID
|
|
BrdgCompRewriteBootPPacketForAdapt(
|
|
IN PUCHAR pPacketData,
|
|
IN PADAPT pAdapt,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
PIP_HEADER_INFO piphi = (PIP_HEADER_INFO)pData;
|
|
|
|
//
|
|
// pAdapt can be LOCAL_MINIPORT if we're being used to edit a packet
|
|
// for indication to the local machine. No rewriting is necessary
|
|
// in that case.
|
|
//
|
|
if( pAdapt != LOCAL_MINIPORT )
|
|
{
|
|
SAFEASSERT( pAdapt != NULL );
|
|
BrdgCompRewriteBootPClientAddress( pPacketData, piphi, pAdapt->MACAddr );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Hashes an IP address. Used for the IP forwarding table as well as
|
|
// the pending-ARP table, which uses an extended key made up of
|
|
// the target IP address and the requesting station's IP address
|
|
//
|
|
ULONG
|
|
BrdgCompHashIPAddress(
|
|
IN PUCHAR pKey
|
|
)
|
|
{
|
|
// Our hash function consists of taking the lower portion of the IP
|
|
// address. The number of hash buckets has to be a power of 2 for
|
|
// this to work propery.
|
|
return (*(PULONG)pKey) & (NUM_HASH_BUCKETS - 1);
|
|
}
|
|
|
|
//
|
|
// Hashes a DHCP transaction id
|
|
//
|
|
ULONG
|
|
BrdgCompHashXID(
|
|
IN PUCHAR pXid
|
|
)
|
|
{
|
|
// Our hash function consists of taking the lower portion of the
|
|
// XID. The number of hash buckets has to be a power of 2 for
|
|
// this to work propery.
|
|
return (*(PULONG)pXid) & (NUM_DHCP_HASH_BUCKETS - 1);
|
|
}
|
|
|
|
//
|
|
// Returns true if the given IP table entry refers to a certain
|
|
// adapter
|
|
BOOLEAN
|
|
BrdgCompIPEntriesMatchAdapter(
|
|
IN PHASH_TABLE_ENTRY phte,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
PADAPT pAdapt = (PADAPT)pData;
|
|
PIP_TABLE_ENTRY pipte = (PIP_TABLE_ENTRY)phte;
|
|
|
|
// Don't take the spin lock since we're doing a single read,
|
|
// which we ASSUME to be atomic.
|
|
return (BOOLEAN)(pipte->pAdapt == pAdapt);
|
|
}
|
|
|
|
//
|
|
// Returns true if the given ARP table entry refers to a certain
|
|
// adapter
|
|
//
|
|
BOOLEAN
|
|
BrdgCompARPEntriesMatchAdapter(
|
|
IN PHASH_TABLE_ENTRY phte,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
PADAPT pAdapt = (PADAPT)pData;
|
|
PARP_TABLE_ENTRY pate = (PARP_TABLE_ENTRY)phte;
|
|
|
|
// Don't take the spin lock since we're doing a single read,
|
|
// which we ASSUME to be atomic.
|
|
return (BOOLEAN)(pate->pOriginalAdapt == pAdapt);
|
|
}
|
|
|
|
//
|
|
// Returns true if the given DHCP table entry refers to a certain
|
|
// adapter
|
|
//
|
|
BOOLEAN
|
|
BrdgCompDHCPEntriesMatchAdapter(
|
|
IN PHASH_TABLE_ENTRY phte,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
PADAPT pAdapt = (PADAPT)pData;
|
|
PDHCP_TABLE_ENTRY pdhcpte = (PDHCP_TABLE_ENTRY)phte;
|
|
|
|
// Don't take the spin lock since we're doing a single read,
|
|
// which we ASSUME to be atomic.
|
|
return (BOOLEAN)(pdhcpte->pRequestorAdapt == pAdapt);
|
|
}
|
|
|
|
//
|
|
// Completion function for route lookup IRPs. Returns
|
|
// STATUS_MORE_PROCESSING_REQUIRED to prevent the IO manager
|
|
// from mucking with the IRP (which we free ourselves).
|
|
//
|
|
NTSTATUS
|
|
BrdgCompCompleteRouteLookupIRP(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pirp,
|
|
IN PVOID ignored
|
|
)
|
|
{
|
|
IoFreeIrp( pirp );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
// ===========================================================================
|
|
//
|
|
// PUBLIC FUNCTIONS
|
|
//
|
|
// ===========================================================================
|
|
|
|
NTSTATUS
|
|
BrdgCompDriverInit()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Driver-initialization function for the compatibility module
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status. Anything other than STATUS_SUCCESS aborts the driver load.
|
|
|
|
--*/
|
|
{
|
|
ULONG MaxSize, MaxEntries;
|
|
|
|
// Initialize the next-hop cache
|
|
if( BrdgInitializeCache(&gNextHopCache, HOP_CACHE_SHIFT_FACTOR) != NDIS_STATUS_SUCCESS )
|
|
{
|
|
DBGPRINT(COMPAT, ("FAILED TO ALLOCATE NEXT-HOPE CACHE!\n"));
|
|
NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_INIT_MALLOC_FAILED, 0L, 0L, NULL, 0L, NULL );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// See if the registry specifies a max table size for the IP table
|
|
if( BrdgReadRegDWord(&gRegistryPath, gMaxIPTableSizeParameterName, &MaxSize) != STATUS_SUCCESS )
|
|
{
|
|
MaxSize = DEFAULT_MAX_IP_TABLE_SIZE;
|
|
}
|
|
|
|
MaxEntries = MaxSize / sizeof(IP_TABLE_ENTRY);
|
|
DBGPRINT(COMPAT, ("Capping IP forwarding table at %i entries (%i bytes of memory)\n", MaxEntries, MaxSize));
|
|
|
|
gIPForwardingTable = BrdgHashCreateTable( BrdgCompHashIPAddress, NUM_HASH_BUCKETS, sizeof(IP_TABLE_ENTRY),
|
|
MaxEntries, MAX_IP_ENTRY_AGE, MAX_IP_ENTRY_AGE, sizeof(IPADDRESS) );
|
|
|
|
if( gIPForwardingTable == NULL )
|
|
{
|
|
DBGPRINT(COMPAT, ("FAILED TO ALLOCATE IP TABLE!\n"));
|
|
NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_INIT_MALLOC_FAILED, 0L, 0L, NULL, 0L, NULL );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Our Pending-ARP table uses ARP_TABLE_KEY structures as a key, but we still use the BrdgCompHashIPAddress
|
|
// routine to hash the keys. This will result in the hash being based on the first part of the key alone (the
|
|
// target IP address), which is what we want, since all the entries for a single target must end up in the
|
|
// same bucket for our multi-match retrieval to work.
|
|
//
|
|
|
|
// See if the registry specifies a max table size for the ARP table
|
|
if( BrdgReadRegDWord(&gRegistryPath, gMaxARPTableSizeParameterName, &MaxSize) != STATUS_SUCCESS )
|
|
{
|
|
MaxSize = DEFAULT_MAX_ARP_TABLE_SIZE;
|
|
}
|
|
|
|
MaxEntries = MaxSize / sizeof(ARP_TABLE_ENTRY);
|
|
DBGPRINT(COMPAT, ("Capping Pending-ARP table at %i entries (%i bytes of memory)\n", MaxEntries, MaxSize));
|
|
gPendingARPTable = BrdgHashCreateTable( BrdgCompHashIPAddress, NUM_HASH_BUCKETS, sizeof(ARP_TABLE_ENTRY),
|
|
MaxEntries, MAX_ARP_ENTRY_AGE, MAX_ARP_ENTRY_AGE, sizeof(ARP_TABLE_KEY) );
|
|
|
|
if( gPendingARPTable == NULL )
|
|
{
|
|
BrdgHashFreeHashTable( gIPForwardingTable );
|
|
DBGPRINT(COMPAT, ("FAILED TO ALLOCATE ARP TABLE!\n"));
|
|
NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_INIT_MALLOC_FAILED, 0L, 0L, NULL, 0L, NULL );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// See if the registry specifies a max table size for the DHCP table
|
|
if( BrdgReadRegDWord(&gRegistryPath, gMaxDHCPTableSizeParameterName, &MaxSize) != STATUS_SUCCESS )
|
|
{
|
|
MaxSize = DEFAULT_MAX_DHCP_TABLE_SIZE;
|
|
}
|
|
|
|
MaxEntries = MaxSize / sizeof(DHCP_TABLE_ENTRY);
|
|
DBGPRINT(COMPAT, ("Capping Pending-DHCP table at %i entries (%i bytes of memory)\n", MaxEntries, MaxSize));
|
|
gPendingDHCPTable = BrdgHashCreateTable( BrdgCompHashXID, NUM_DHCP_HASH_BUCKETS, sizeof(DHCP_TABLE_ENTRY),
|
|
MaxEntries, MAX_DHCP_ENTRY_AGE, MAX_DHCP_ENTRY_AGE, sizeof(ULONG) );
|
|
|
|
if( gPendingDHCPTable == NULL )
|
|
{
|
|
BrdgHashFreeHashTable( gIPForwardingTable );
|
|
BrdgHashFreeHashTable( gPendingARPTable );
|
|
DBGPRINT(COMPAT, ("FAILED TO ALLOCATE DHCP TABLE!\n"));
|
|
NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_INIT_MALLOC_FAILED, 0L, 0L, NULL, 0L, NULL );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Initialize synchronization objects
|
|
NdisInitializeReadWriteLock( &gLocalIPAddressListLock );
|
|
NdisAllocateSpinLock( &gTCPIPLock );
|
|
BrdgInitializeWaitRef( &gTCPIPRefcount, FALSE );
|
|
|
|
// We start out with no connection to TCPIP so the waitref needs to be in the shutdown state
|
|
BrdgShutdownWaitRefOnce( &gTCPIPRefcount );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
BrdgCompCleanup()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
One-time cleanup for the compatibility module
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LOCK_STATE LockState;
|
|
|
|
// Detach from TCPIP
|
|
BrdgCompDetachFromTCPIP(NULL);
|
|
|
|
// Dump the next-hop cache
|
|
BrdgFreeCache( &gNextHopCache );
|
|
|
|
// Dump the forwarding hash table
|
|
BrdgHashFreeHashTable( gIPForwardingTable );
|
|
gIPForwardingTable = NULL;
|
|
|
|
// Dump the pending-ARP hash table
|
|
BrdgHashFreeHashTable( gPendingARPTable );
|
|
gPendingARPTable = NULL;
|
|
|
|
// Dump the pending-DHCP table
|
|
BrdgHashFreeHashTable( gPendingDHCPTable );
|
|
gPendingDHCPTable = NULL;
|
|
|
|
// Clean up the list of network addresses.
|
|
NdisAcquireReadWriteLock( &gLocalIPAddressListLock, TRUE /*Read-Write*/, &LockState );
|
|
|
|
if( gLocalIPAddressListLength > 0L )
|
|
{
|
|
NdisFreeMemory( gLocalIPAddressList, gLocalIPAddressListLength, 0 );
|
|
gLocalIPAddressList = NULL;
|
|
gLocalIPAddressListLength = 0L;
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gLocalIPAddressListLock, &LockState );
|
|
}
|
|
|
|
VOID
|
|
BrdgCompScrubAdapter(
|
|
IN PADAPT pAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes all table entries that refer to a given adapter; called when that
|
|
adapter is being removed (future references to this adapter are illegal)
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DBGPRINT(COMPAT, ("Scrubbing Adapter %p from the compatibility tables...\n", pAdapt));
|
|
|
|
// Remove all entries referencing this adapter from the IP table
|
|
BrdgHashRemoveMatching( gIPForwardingTable, BrdgCompIPEntriesMatchAdapter, pAdapt );
|
|
|
|
// Remove all entries referencing this adapter from the pending-ARP table
|
|
BrdgHashRemoveMatching( gPendingARPTable, BrdgCompARPEntriesMatchAdapter, pAdapt );
|
|
|
|
// Remove all entries referencing this adapter from the DHCP table
|
|
BrdgHashRemoveMatching( gPendingDHCPTable, BrdgCompDHCPEntriesMatchAdapter, pAdapt );
|
|
}
|
|
|
|
VOID BrdgCompScrubAllAdapters()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function cleans all the adapters from the IP tables (this is in the case of a GPO changing
|
|
our bridging settings)
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PADAPT pAdapt = NULL;
|
|
LOCK_STATE LockStateAdapterList;
|
|
|
|
//
|
|
// We don't want an adapter to go away while we're running through the list of adapters.
|
|
//
|
|
NdisAcquireReadWriteLock(&gAdapterListLock, FALSE /* Read Only */, &LockStateAdapterList);
|
|
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
// Scrub the adapter from the Compatibility tables.
|
|
BrdgCompScrubAdapter(pAdapt);
|
|
}
|
|
|
|
NdisReleaseReadWriteLock(&gAdapterListLock, &LockStateAdapterList);
|
|
}
|
|
|
|
|
|
VOID
|
|
BrdgCompNotifyMACAddress(
|
|
IN PUCHAR pBridgeMACAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the miniport module to notify us of the MAC address of the miniport
|
|
|
|
Arguments:
|
|
|
|
pBridgeMACAddr Our MAC address
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ETH_COPY_NETWORK_ADDRESS( &gCompMACAddress, pBridgeMACAddr );
|
|
gCompHaveMACAddress = TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompRequiresCompatWork(
|
|
IN PADAPT pAdapt,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT dataSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called during the processing of inbound packets to determine whether a
|
|
packet will require compatibility-mode work.
|
|
|
|
The compatibility code requires that its packets be flat, whereas packets
|
|
indicated from underlying miniports can be arbitrarily fragmented. The
|
|
forwarding engine uses the result of this call to determine whether an
|
|
inbound packet must be copied to a flat data buffer in a copy packet that
|
|
we own or whether it can be handled along fast-track paths that don't
|
|
care about packet fragmentation.
|
|
|
|
Arguments:
|
|
|
|
pAdapt Adapter on which the packet was received
|
|
pPacketDataq A pointer to the beginning of the packet data
|
|
dataSize The amount of data pointed to
|
|
|
|
Return Value:
|
|
|
|
TRUE: The forwarding engine should call BrdgCompProcessInboundPacket at
|
|
a later time to process this packet
|
|
|
|
FALSE: BrdgCompProcessInboundPacket should never be called for this packet
|
|
|
|
--*/
|
|
{
|
|
UINT result;
|
|
USHORT etherType;
|
|
|
|
// Weird runty packets are of no use to anyone
|
|
if( dataSize < ETHERNET_HEADER_SIZE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// No compatibility-mode work is required if there are no compatibility-mode
|
|
// adapters.
|
|
//
|
|
if( !gCompatAdaptersExist )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// All frames that arrive on a compatibility adapter are processed
|
|
if( pAdapt->bCompatibilityMode )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Broadcast or multicast frames always require compatibility processing
|
|
if( ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The packet was unicast. If it wasn't sent to the adapter's MAC address,
|
|
// it does not require compatibility-mode processing.
|
|
//
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ( pPacketData, pAdapt->MACAddr, &result );
|
|
|
|
if( result != 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The packet is only of interest if it is ARP or IP (on a non-compat
|
|
// adapter)
|
|
//
|
|
etherType = BrdgCompGetEtherType( pPacketData );
|
|
return (BOOLEAN)( (etherType == ARP_ETHERTYPE) || (etherType == IP_ETHERTYPE) );
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to hand an inbound packet to the compatibility module for processing.
|
|
|
|
If the packet arrived on a non-compatibility adapter, the compatibility
|
|
code should NEVER indicate the packet, as that will be done by the
|
|
regular forwarding engine code. On the other hand, if the packet
|
|
arrived on a compatibility-mode adapter, the compatibility code MUST
|
|
indicate the packet if appropriate. Why the disparity? A packet
|
|
arriving on a compatibility adapter will likely require editing before
|
|
indication, whereas a packet arriving on a non-compatibility adapter
|
|
will not.
|
|
|
|
The compatibility module may retain the packet if bCanRetain is TRUE
|
|
(in which case we must return TRUE). If bCanRetain is FALSE, the
|
|
compatibility code may NOT retain the packet. If it needs to forward
|
|
the packet data or indicate the packet, it must make a copy
|
|
packet and use that instead of the original.
|
|
|
|
Arguments:
|
|
|
|
pPacket The received packet
|
|
pAdapt The adapter the packet was received on
|
|
bCanRetain Whether we can hang on to the packet
|
|
|
|
Return Value:
|
|
|
|
TRUE: The packet was retained (should never be returned if bCanRetain == FALSE)
|
|
The caller should not use this packet or attempt to free it.
|
|
|
|
FALSE: The packet was not retained. The caller still has ownership of the
|
|
packet and should arrange for it to be freed when appropriate.
|
|
|
|
--*/
|
|
{
|
|
PNDIS_BUFFER pBuffer;
|
|
PUCHAR pBufferData;
|
|
UINT bufferLen = 0;
|
|
UINT totLen;
|
|
USHORT etherType;
|
|
BOOLEAN bRetained;
|
|
|
|
NdisGetFirstBufferFromPacketSafe( pPacket, &pBuffer, &pBufferData, &bufferLen,
|
|
&totLen, NormalPagePriority );
|
|
|
|
if( pBufferData == NULL )
|
|
{
|
|
// The packet was empty or the system is under severe memory pressure
|
|
// We didn't retain the packet.
|
|
return FALSE;
|
|
}
|
|
|
|
if( totLen < ETHERNET_HEADER_SIZE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// The packet should be flat
|
|
SAFEASSERT( totLen == bufferLen );
|
|
etherType = BrdgCompGetEtherType( pBufferData );
|
|
|
|
if( etherType == ARP_ETHERTYPE )
|
|
{
|
|
bRetained = BrdgCompProcessInboundARPPacket( pPacket, pAdapt, bCanRetain, pBufferData, bufferLen );
|
|
}
|
|
else
|
|
{
|
|
bRetained = BrdgCompProcessInboundNonARPPacket( pPacket, pAdapt, bCanRetain, pBufferData, bufferLen );
|
|
}
|
|
|
|
if( !bCanRetain )
|
|
{
|
|
SAFEASSERT( !bRetained );
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
|
|
|
|
VOID
|
|
BrdgCompProcessOutboundPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pTargetAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to hand an outbound packet to the compatibility module for processing.
|
|
|
|
Because the packet passed to us is from an overlying protocol driver, we
|
|
are not allowed to do anything with it. The packet may be arbitrarily
|
|
fragmented, and its data buffers must be treated as read-only.
|
|
|
|
This function is only called if a packet is bound for an adapter in
|
|
compatibility mode (so we can do any necessary packet editing) or for a packet
|
|
for which we have no known outbound adapter (i.e., it is a packet we are
|
|
flooding).
|
|
|
|
In the case that pTargetAdapt == NULL (a flood), the compatibility code is
|
|
responsible for sending the packet out all *compatibility mode* adapters.
|
|
Sending the packet out regular-mode adapters is the job of the regular
|
|
code in the forwarding engine.
|
|
|
|
Arguments:
|
|
|
|
pPacket The outbound packet
|
|
pTargetAdapt The target adapter, as determined by a previous lookup in
|
|
the MAC forwarding table. This can be NULL to indicate
|
|
a flood.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PNDIS_PACKET pCopyPacket;
|
|
PUCHAR pCopyPacketData;
|
|
UINT copyPacketSize;
|
|
|
|
// There's no point in calling us for a packet that is bound for a MAC
|
|
// address which is known to be reachable on a non-compat adapter
|
|
SAFEASSERT( (pTargetAdapt == NULL) || (pTargetAdapt->bCompatibilityMode) );
|
|
|
|
// There is no work to do if there are no compatibility adapters
|
|
if( !gCompatAdaptersExist )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Prepare the flattened copy packet so our functions can edit
|
|
// the packet as appropriate. The packet will be counted as a local-source
|
|
// transmission when / if it is used.
|
|
pCopyPacket = BrdgFwdMakeCompatCopyPacket( pPacket, &pCopyPacketData, ©PacketSize, TRUE );
|
|
|
|
if( pCopyPacket != NULL )
|
|
{
|
|
BOOLEAN bRetained = FALSE;
|
|
|
|
if( copyPacketSize >= ETHERNET_HEADER_SIZE )
|
|
{
|
|
USHORT etherType;
|
|
|
|
etherType = BrdgCompGetEtherType(pCopyPacketData);
|
|
|
|
if( etherType == ARP_ETHERTYPE )
|
|
{
|
|
bRetained = BrdgCompProcessOutboundARPPacket(pCopyPacket, pCopyPacketData, copyPacketSize, pTargetAdapt);
|
|
}
|
|
else
|
|
{
|
|
bRetained = BrdgCompProcessOutboundNonARPPacket(pCopyPacket, pCopyPacketData, copyPacketSize, pTargetAdapt);
|
|
}
|
|
}
|
|
// else the packet was really small!
|
|
|
|
if( ! bRetained )
|
|
{
|
|
// The functions above decided not to hang on to the packet after all.
|
|
// Release it.
|
|
BrdgFwdReleaseCompatPacket( pCopyPacket );
|
|
}
|
|
}
|
|
// Else we didn't get a packet
|
|
}
|
|
|
|
|
|
VOID
|
|
BrdgCompNotifyNetworkAddresses(
|
|
IN PNETWORK_ADDRESS_LIST pAddressList,
|
|
IN ULONG infoLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the miniport code when we get an OID indicating our network-layer
|
|
addresses to us. We copy out the list of our IP addresses. The buffer
|
|
passed to us can also be formatted in such a way as to indicate that we
|
|
should dump our list of network addresses.
|
|
|
|
Arguments:
|
|
|
|
pAddressList The data buffer passed down in the OID
|
|
infoLength The size of the buffer
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PIPADDRESS pOldList;
|
|
UINT oldListLength;
|
|
LOCK_STATE LockState;
|
|
|
|
if( infoLength < sizeof(NETWORK_ADDRESS_LIST) - sizeof(NETWORK_ADDRESS) )
|
|
{
|
|
// The structure is too small to hold anything interesting.
|
|
return;
|
|
}
|
|
|
|
if( pAddressList->AddressCount > 0 )
|
|
{
|
|
USHORT i, numAddresses, copiedAddresses = 0;
|
|
NETWORK_ADDRESS UNALIGNED *pNetAddress;
|
|
NDIS_STATUS Status;
|
|
PIPADDRESS pNewList;
|
|
|
|
//
|
|
// Make sure the structure can hold the number of addresses it claims to.
|
|
// NETWORK_ADDRESS_LIST is defined with one NETWORK_ADDRESS at its tail,
|
|
// so knock one off pAddressList->AddressCount when calculating the
|
|
// size of the total structure.
|
|
//
|
|
if( infoLength < sizeof(NETWORK_ADDRESS_LIST) +
|
|
( sizeof(NETWORK_ADDRESS) * (pAddressList->AddressCount - 1) ) )
|
|
{
|
|
// The structure is too small to contain the number of addresses
|
|
// it claims to.
|
|
|
|
SAFEASSERT( FALSE );
|
|
return;
|
|
}
|
|
|
|
// Make a first pass to count the number of IP addresses in the list
|
|
pNetAddress = pAddressList->Address;
|
|
|
|
for( i = 0, numAddresses = 0; i < pAddressList->AddressCount; i++ )
|
|
{
|
|
if( pNetAddress->AddressType == NDIS_PROTOCOL_ID_TCP_IP )
|
|
{
|
|
numAddresses++;
|
|
}
|
|
|
|
pNetAddress = (NETWORK_ADDRESS UNALIGNED*)(((PUCHAR)pNetAddress) + pNetAddress->AddressLength);
|
|
}
|
|
|
|
if( numAddresses == 0 )
|
|
{
|
|
// There are no IP addresses in this list. Nothing to do.
|
|
return;
|
|
}
|
|
|
|
// Allocate enough room to hold the addresses
|
|
Status = NdisAllocateMemoryWithTag( &pNewList, sizeof(IPADDRESS) * numAddresses, 'gdrB' );
|
|
|
|
if( Status != NDIS_STATUS_SUCCESS )
|
|
{
|
|
DBGPRINT(COMPAT, ("NdisAllocateMemoryWithTag failed while recording IP address list\n"));
|
|
|
|
// Clobber the old list with a NULL, since we know that the old list is outdated,
|
|
// but we failed to record the new info
|
|
pNewList = NULL;
|
|
}
|
|
else
|
|
{
|
|
SAFEASSERT( pNewList != NULL );
|
|
|
|
// Copy the IP addresses to our list
|
|
pNetAddress = pAddressList->Address;
|
|
|
|
for( i = 0; i < pAddressList->AddressCount; i++ )
|
|
{
|
|
if( pNetAddress->AddressType == NDIS_PROTOCOL_ID_TCP_IP )
|
|
{
|
|
NETWORK_ADDRESS_IP UNALIGNED *pIPAddr;
|
|
PUCHAR pIPNetAddr;
|
|
|
|
SAFEASSERT( copiedAddresses < numAddresses );
|
|
|
|
pIPAddr = (NETWORK_ADDRESS_IP UNALIGNED*)&pNetAddress->Address[0];
|
|
pIPNetAddr = (PUCHAR)&pIPAddr->in_addr;
|
|
|
|
// IP passes down the IP address in the opposite byte order that we use
|
|
pNewList[copiedAddresses] = 0L;
|
|
pNewList[copiedAddresses] |= pIPNetAddr[3];
|
|
pNewList[copiedAddresses] |= pIPNetAddr[2] << 8;
|
|
pNewList[copiedAddresses] |= pIPNetAddr[1] << 16;
|
|
pNewList[copiedAddresses] |= pIPNetAddr[0] << 24;
|
|
|
|
DBGPRINT(COMPAT, ("Noted local IP address %i.%i.%i.%i\n",
|
|
pIPNetAddr[0], pIPNetAddr[1], pIPNetAddr[2], pIPNetAddr[3] ));
|
|
|
|
copiedAddresses++;
|
|
}
|
|
|
|
pNetAddress = (NETWORK_ADDRESS UNALIGNED*)(((PUCHAR)pNetAddress) + pNetAddress->AddressLength);
|
|
}
|
|
|
|
SAFEASSERT( copiedAddresses == numAddresses );
|
|
}
|
|
|
|
// Swap in the new list (even if it's NULL)
|
|
NdisAcquireReadWriteLock( &gLocalIPAddressListLock, TRUE /*Read-write*/, &LockState );
|
|
|
|
pOldList = gLocalIPAddressList;
|
|
oldListLength = gLocalIPAddressListLength;
|
|
|
|
gLocalIPAddressList = pNewList;
|
|
|
|
if( pNewList != NULL )
|
|
{
|
|
gLocalIPAddressListLength = sizeof(IPADDRESS) * numAddresses;
|
|
}
|
|
else
|
|
{
|
|
gLocalIPAddressListLength = 0L;
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gLocalIPAddressListLock, &LockState );
|
|
|
|
// Ditch the old list if there was one
|
|
if( pOldList != NULL )
|
|
{
|
|
SAFEASSERT( oldListLength > 0L );
|
|
NdisFreeMemory( pOldList, oldListLength, 0 );
|
|
}
|
|
|
|
// Only attach to TCPIP if we actually learned some IP addresses
|
|
if( numAddresses > 0 )
|
|
{
|
|
// We are at DISPATCH_LEVEL in this function. Defer the call to BrdgCompAttachToTCPIP
|
|
// so we open a channel of communication to the TCPIP driver.
|
|
BrdgDeferFunction( BrdgCompAttachToTCPIP, NULL );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is a request to clear out our list of network-layer
|
|
// addresses.
|
|
if( pAddressList->AddressType == NDIS_PROTOCOL_ID_TCP_IP )
|
|
{
|
|
DBGPRINT(COMPAT, ("Flushing list of IP addresses\n"));
|
|
|
|
// Dump our list of network addresses
|
|
NdisAcquireReadWriteLock( &gLocalIPAddressListLock, TRUE /*Read-write*/, &LockState );
|
|
|
|
pOldList = gLocalIPAddressList;
|
|
oldListLength = gLocalIPAddressListLength;
|
|
|
|
gLocalIPAddressList = NULL;
|
|
gLocalIPAddressListLength = 0L;
|
|
|
|
NdisReleaseReadWriteLock( &gLocalIPAddressListLock, &LockState );
|
|
|
|
if( oldListLength > 0L )
|
|
{
|
|
SAFEASSERT( pOldList != NULL );
|
|
NdisFreeMemory( pOldList, oldListLength, 0 );
|
|
}
|
|
|
|
// Detach from the TCPIP driver at lower IRQL
|
|
BrdgDeferFunction( BrdgCompDetachFromTCPIP, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===========================================================================
|
|
//
|
|
// PRIVATE UTILITY FUNCTIONS
|
|
//
|
|
// ===========================================================================
|
|
|
|
NTSTATUS
|
|
BrdgCompRouteChangeCompletion(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP pirp,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the IRP we post to TCPIP.SYS completes, indicating a change
|
|
in the IP routing table
|
|
|
|
Arguments:
|
|
|
|
DeviceObject Unused
|
|
pirp The completed IRP
|
|
Context Unused
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS, indicating we are done with this IRP
|
|
STATUS_MORE_PROCESSING_REQUIRED when we reuse this IRP by reposting it
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDEVICE_OBJECT pdo;
|
|
PFILE_OBJECT pfo;
|
|
|
|
DBGPRINT(COMPAT, ("IP route table changed; flushing route cache.\n"));
|
|
|
|
// Flush the route cache
|
|
BrdgClearCache( &gNextHopCache );
|
|
|
|
//
|
|
// If gIPRouteChangeIRP != pirp, it indicates that we are either detached
|
|
// from TCPIP (gIPRouteChangeIRP == NULL) or we have detached and
|
|
// reattached (gIPRouteChangeIRP != NULL && gIPRouteChangeIRP != pirp).
|
|
// In either case, we should stop reusing this IRP to post route-change
|
|
// notification requests.
|
|
//
|
|
if( (gIPRouteChangeIRP == pirp) && (BrdgCompAcquireTCPIP(NULL, NULL, &pdo, &pfo)) )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Reinitialize the IRP structure and submit it again
|
|
// for further notification.
|
|
//
|
|
|
|
pirp->Cancel = FALSE;
|
|
pirp->IoStatus.Status = 0;
|
|
pirp->IoStatus.Information = 0;
|
|
pirp->AssociatedIrp.SystemBuffer = NULL;
|
|
IoSetCompletionRoutine( pirp, BrdgCompRouteChangeCompletion,
|
|
NULL, TRUE, FALSE, FALSE );
|
|
|
|
IrpSp = IoGetNextIrpStackLocation(pirp);
|
|
IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_IP_RTCHANGE_NOTIFY_REQUEST;
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
|
|
status = IoCallDriver(pdo, pirp);
|
|
BrdgCompReleaseTCPIP();
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// We failed to call TCPIP. Release the IRP.
|
|
DBGPRINT(COMPAT, ("Failed to call TCPIP for route notification: %08x\n", status));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// We keep the IRP since we reposted it to TCPIP
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We must be detaching or detached from TCPIP. Don't repost the IRP.
|
|
DBGPRINT(COMPAT, ("Stopping our route change notifications...\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
BrdgCompAttachToTCPIP(
|
|
IN PVOID ignored
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Establishes a connection to TCPIP for sending future route-lookup requests.
|
|
Opens a connection to the TCPIP driver and posts an IRP for route change
|
|
notifications.
|
|
|
|
Arguments:
|
|
|
|
ignored ignored
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE TCPFileHandle, IPFileHandle;
|
|
PFILE_OBJECT pTCPFileObject, pIPFileObject;
|
|
PDEVICE_OBJECT pTCPDeviceObject, pIPDeviceObject;
|
|
BOOLEAN bAbort = FALSE;
|
|
|
|
// Check if there is already a connection open to TCP
|
|
if( BrdgCompAcquireTCPIP(NULL, NULL, NULL, NULL) )
|
|
{
|
|
BrdgCompReleaseTCPIP();
|
|
return;
|
|
}
|
|
|
|
// There doesn't appear to currently be a connection to the TCPIP driver.
|
|
// Open one.
|
|
status = BrdgOpenDevice( DD_TCP_DEVICE_NAME, &pTCPDeviceObject, &TCPFileHandle, &pTCPFileObject );
|
|
|
|
if( ! NT_SUCCESS(status) )
|
|
{
|
|
DBGPRINT(ALWAYS_PRINT, ("Couldn't open TCP device: %08x\n", status));
|
|
return;
|
|
}
|
|
|
|
status = BrdgOpenDevice( DD_IP_DEVICE_NAME, &pIPDeviceObject, &IPFileHandle, &pIPFileObject );
|
|
|
|
if( ! NT_SUCCESS(status) )
|
|
{
|
|
DBGPRINT(ALWAYS_PRINT, ("Couldn't open IP device: %08x\n", status));
|
|
BrdgCloseDevice(TCPFileHandle, pTCPFileObject, pTCPDeviceObject);
|
|
return;
|
|
}
|
|
|
|
NdisAcquireSpinLock( &gTCPIPLock );
|
|
|
|
if( gTCPDeviceObject == NULL )
|
|
{
|
|
SAFEASSERT( gTCPFileHandle == NULL );
|
|
SAFEASSERT( gTCPFileObject == NULL );
|
|
SAFEASSERT( gIPDeviceObject == NULL );
|
|
SAFEASSERT( gIPFileHandle == NULL );
|
|
SAFEASSERT( gIPFileObject == NULL );
|
|
|
|
// Swap in the info we just obtained.
|
|
gTCPDeviceObject = pTCPDeviceObject;
|
|
gTCPFileHandle = TCPFileHandle;
|
|
gTCPFileObject = pTCPFileObject;
|
|
gIPDeviceObject = pIPDeviceObject;
|
|
gIPFileHandle = IPFileHandle;
|
|
gIPFileObject = pIPFileObject;
|
|
|
|
// Let people acquire the TCPIP driver
|
|
BrdgResetWaitRef( &gTCPIPRefcount );
|
|
}
|
|
else
|
|
{
|
|
// Someone else opened TCPIP.SYS between our initial call to BrdgCompAcquireTCPIP
|
|
// and now. This should be rather rare.
|
|
SAFEASSERT( gTCPFileHandle != NULL );
|
|
SAFEASSERT( gTCPFileObject != NULL );
|
|
SAFEASSERT( gIPDeviceObject != NULL );
|
|
SAFEASSERT( gIPFileHandle != NULL );
|
|
SAFEASSERT( gIPFileObject != NULL );
|
|
|
|
bAbort = TRUE;
|
|
}
|
|
|
|
NdisReleaseSpinLock( &gTCPIPLock );
|
|
|
|
if( bAbort )
|
|
{
|
|
// Need to back out of the attempt to open TCPIP.SYS
|
|
BrdgCloseDevice( TCPFileHandle, pTCPFileObject, pTCPDeviceObject );
|
|
BrdgCloseDevice( IPFileHandle, pIPFileObject, pIPDeviceObject );
|
|
}
|
|
else
|
|
{
|
|
if( BrdgCompAcquireTCPIP(NULL, NULL, &pIPDeviceObject, &pIPFileObject) )
|
|
{
|
|
NTSTATUS status;
|
|
PIRP pirp;
|
|
|
|
// Set up the route-change notification IRP
|
|
pirp = IoBuildDeviceIoControlRequest( IOCTL_IP_RTCHANGE_NOTIFY_REQUEST, pIPDeviceObject,
|
|
NULL, 0, NULL, 0, FALSE, NULL, NULL );
|
|
|
|
if( pirp == NULL )
|
|
{
|
|
DBGPRINT(COMPAT, ("Failed to allocate an IRP for route-change notification!\n"));
|
|
}
|
|
else
|
|
{
|
|
if( InterlockedExchangePointer(&gIPRouteChangeIRP, pirp) != NULL )
|
|
{
|
|
//
|
|
// Oops; someone else created an IRP to post to TCPIP at the same time as us.
|
|
// Abort our attempt.
|
|
//
|
|
IoCompleteRequest( pirp, IO_NO_INCREMENT );
|
|
}
|
|
else
|
|
{
|
|
IoSetCompletionRoutine( pirp, BrdgCompRouteChangeCompletion, NULL, TRUE, FALSE, FALSE );
|
|
|
|
status = IoCallDriver( pIPDeviceObject, pirp );
|
|
|
|
if( ! NT_SUCCESS(status) )
|
|
{
|
|
DBGPRINT(COMPAT, ("Failed to post IRP to TCPIP for route-change notification: %08x\n", status));
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(COMPAT, ("Posted route-change notification request to TCPIP\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
BrdgCompReleaseTCPIP();
|
|
}
|
|
// else someone shut down the connection to TCPIP very quickly after we set it up
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgCompDetachFromTCPIP(
|
|
IN PVOID ignored
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Severs the current connection, if any, to TCPIP.SYS.
|
|
|
|
Arguments:
|
|
|
|
ignored ignored
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
HANDLE TCPFileHandle, IPFileHandle;
|
|
PFILE_OBJECT pTCPFileObject, pIPFileObject;
|
|
PDEVICE_OBJECT pTCPDeviceObject, pIPDeviceObject;
|
|
PIRP pRouteIRP;
|
|
|
|
// Wait for everyone to be done using the driver
|
|
// Ignore return value because we are multi-shutdown-safe.
|
|
BrdgShutdownWaitRef( &gTCPIPRefcount );
|
|
|
|
// Cancel the IRP we use for route change notifications.
|
|
pRouteIRP = InterlockedExchangePointer( &gIPRouteChangeIRP, NULL );
|
|
|
|
// pRouteIRP can be NULL if someone is shutting down the connection
|
|
// at the same time as us, or if the connection was already shut down
|
|
if( pRouteIRP != NULL )
|
|
{
|
|
IoCancelIrp( pRouteIRP );
|
|
}
|
|
|
|
// Flush the route cache
|
|
BrdgClearCache( &gNextHopCache );
|
|
|
|
// Copy out the pointers and NULL them
|
|
NdisAcquireSpinLock( &gTCPIPLock );
|
|
TCPFileHandle = gTCPFileHandle;
|
|
gTCPFileHandle = NULL;
|
|
pTCPFileObject = gTCPFileObject;
|
|
gTCPFileObject = NULL;
|
|
pTCPDeviceObject = gTCPDeviceObject;
|
|
gTCPDeviceObject = NULL;
|
|
IPFileHandle = gIPFileHandle;
|
|
gIPFileHandle = NULL;
|
|
pIPFileObject = gIPFileObject;
|
|
gIPFileObject = NULL;
|
|
pIPDeviceObject = gIPDeviceObject;
|
|
gIPDeviceObject = NULL;
|
|
NdisReleaseSpinLock( &gTCPIPLock );
|
|
|
|
// The global pointers can be NULL if someone else is shutting down the
|
|
// connection concurrently with us, or if the connection was already
|
|
// shut down.
|
|
if( pTCPFileObject != NULL )
|
|
{
|
|
SAFEASSERT( TCPFileHandle != NULL );
|
|
SAFEASSERT( pTCPDeviceObject != NULL );
|
|
SAFEASSERT( IPFileHandle != NULL );
|
|
SAFEASSERT( pIPFileObject != NULL );
|
|
SAFEASSERT( pIPDeviceObject != NULL );
|
|
|
|
BrdgCloseDevice( TCPFileHandle, pTCPFileObject, pTCPDeviceObject );
|
|
BrdgCloseDevice( IPFileHandle, pIPFileObject, pIPDeviceObject );
|
|
}
|
|
else
|
|
{
|
|
SAFEASSERT( TCPFileHandle == NULL );
|
|
SAFEASSERT( pTCPDeviceObject == NULL );
|
|
SAFEASSERT( IPFileHandle == NULL );
|
|
SAFEASSERT( pIPFileObject == NULL );
|
|
SAFEASSERT( pIPDeviceObject == NULL );
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompIsUnicastIPAddress(
|
|
IN IPADDRESS ip
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether a given IP address is a unicast address (i.e., one that
|
|
can reasonably designate a single station)
|
|
|
|
Arguments:
|
|
|
|
ip The IP address
|
|
|
|
Return Value:
|
|
|
|
TRUE: The address appears to be a unicast address
|
|
FALSE: The opposite is true
|
|
|
|
--*/
|
|
{
|
|
UCHAR highByte;
|
|
|
|
// The broadcast address is not cool
|
|
if( ip == 0xFFFFFFFF )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// The zero address is no good
|
|
if( ip == 0L )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Any class D (multicast) or class E (currently undefined) is similarly uncool
|
|
highByte = (UCHAR)(ip >> 24);
|
|
if( (highByte & 0xF0) == 0xE0 || (highByte & 0xF0) == 0xF0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check each address class to see if this is a net-directed (or all-subnets)
|
|
// broadcast
|
|
if( (highByte & 0x80) && ((ip & 0x00FFFFFF) == 0x00FFFFFFFF) )
|
|
{
|
|
// Class A net-directed or all-subnets broadcast.
|
|
return FALSE;
|
|
}
|
|
else if( ((highByte & 0xC0) == 0x80) && ((ip & 0x0000FFFF) == 0x0000FFFF) )
|
|
{
|
|
// Class B net-directed or all-subnets broadcast.
|
|
return FALSE;
|
|
}
|
|
else if( ((highByte & 0xE0) == 0xC) && ((UCHAR)ip == 0xFF) )
|
|
{
|
|
// Class C net-directed or all-subnets broadcast.
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This address appears to be OK, although note that since we have no way of
|
|
// knowing the subnet prefix in use on the local links, we cannot detect
|
|
// subnet-directed broadcasts.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompGetNextHopForTarget(
|
|
IN IPADDRESS ipTarget,
|
|
OUT PIPADDRESS pipNextHop
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calls into the TCPIP.SYS driver to determine the next-hop address for a
|
|
given target IP.
|
|
|
|
Arguments:
|
|
|
|
ipTarget The target address
|
|
pipNextHop Receives the next-hop address
|
|
|
|
Return Value:
|
|
|
|
TRUE if the next-hop lookup succeeded and *pipNextHop is valid, FALSE
|
|
otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN rc = FALSE;
|
|
|
|
// First look for the information in our next-hop cache
|
|
*pipNextHop = BrdgProbeCache( &gNextHopCache, (UINT32)ipTarget );
|
|
|
|
if( *pipNextHop != 0L )
|
|
{
|
|
if( *pipNextHop != NO_ADDRESS )
|
|
{
|
|
// The cache contained a valid next hop
|
|
rc = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// We asked TCPIP before about this target address and it
|
|
// told us it doesn't know.
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PDEVICE_OBJECT pdo;
|
|
PFILE_OBJECT pfo;
|
|
|
|
if( BrdgCompAcquireTCPIP(&pdo, &pfo, NULL, NULL) )
|
|
{
|
|
PIRP pirp;
|
|
|
|
pirp = IoAllocateIrp( pdo->StackSize, FALSE );
|
|
|
|
if( pirp != NULL )
|
|
{
|
|
TCP_REQUEST_QUERY_INFORMATION_EX trqiBuffer;
|
|
IPRouteLookupData *pRtLookupData;
|
|
TDIObjectID *lpObject;
|
|
IPRouteEntry routeEntry;
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
RtlZeroMemory (&trqiBuffer, sizeof (trqiBuffer));
|
|
|
|
pRtLookupData = (IPRouteLookupData *)trqiBuffer.Context;
|
|
pRtLookupData->SrcAdd = 0;
|
|
|
|
// IP uses the opposite byte ordering from us.
|
|
((PUCHAR)&pRtLookupData->DestAdd)[0] = ((PUCHAR)&ipTarget)[3];
|
|
((PUCHAR)&pRtLookupData->DestAdd)[1] = ((PUCHAR)&ipTarget)[2];
|
|
((PUCHAR)&pRtLookupData->DestAdd)[2] = ((PUCHAR)&ipTarget)[1];
|
|
((PUCHAR)&pRtLookupData->DestAdd)[3] = ((PUCHAR)&ipTarget)[0];
|
|
|
|
lpObject = &trqiBuffer.ID;
|
|
lpObject->toi_id = IP_MIB_SINGLE_RT_ENTRY_ID;
|
|
lpObject->toi_class = INFO_CLASS_PROTOCOL;
|
|
lpObject->toi_type = INFO_TYPE_PROVIDER;
|
|
lpObject->toi_entity.tei_entity = CL_NL_ENTITY;
|
|
lpObject->toi_entity.tei_instance = 0;
|
|
|
|
irpSp = IoGetNextIrpStackLocation(pirp);
|
|
SAFEASSERT( irpSp != NULL );
|
|
|
|
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_TCP_QUERY_INFORMATION_EX;
|
|
irpSp->DeviceObject = pdo;
|
|
irpSp->FileObject = pfo;
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = &trqiBuffer;
|
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(trqiBuffer);
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(routeEntry);
|
|
|
|
pirp->UserBuffer = &routeEntry;
|
|
pirp->RequestorMode = KernelMode;
|
|
IoSetCompletionRoutine( pirp, BrdgCompCompleteRouteLookupIRP, NULL, TRUE, TRUE, TRUE );
|
|
|
|
status = IoCallDriver( pdo, pirp );
|
|
|
|
// STATUS_PENDING will bugcheck the machine since we passed buffers that
|
|
// are on the stack.
|
|
SAFEASSERT( status != STATUS_PENDING );
|
|
|
|
if( status == STATUS_SUCCESS )
|
|
{
|
|
//
|
|
// TCPIP signals failure by setting the interface designator
|
|
// on the reply to 0xFFFFFFFF
|
|
//
|
|
if( routeEntry.ire_index != 0xFFFFFFFF )
|
|
{
|
|
// IP uses the opposite byte ordering from us.
|
|
((PUCHAR)pipNextHop)[3] = ((PUCHAR)&routeEntry.ire_nexthop)[0];
|
|
((PUCHAR)pipNextHop)[2] = ((PUCHAR)&routeEntry.ire_nexthop)[1];
|
|
((PUCHAR)pipNextHop)[1] = ((PUCHAR)&routeEntry.ire_nexthop)[2];
|
|
((PUCHAR)pipNextHop)[0] = ((PUCHAR)&routeEntry.ire_nexthop)[3];
|
|
|
|
if( ! BrdgCompIsLocalIPAddress(*pipNextHop) )
|
|
{
|
|
// Poke the new data into the cache
|
|
BrdgUpdateCache( &gNextHopCache, ipTarget, *pipNextHop );
|
|
rc = TRUE;
|
|
}
|
|
else
|
|
{
|
|
THROTTLED_DBGPRINT(COMPAT, ("TCPIP gave a bridge IP address as next hop for %i.%i.%i.%i\n",
|
|
((PUCHAR)&ipTarget)[3], ((PUCHAR)&ipTarget)[2], ((PUCHAR)&ipTarget)[1],
|
|
((PUCHAR)&ipTarget)[0] ));
|
|
|
|
BrdgUpdateCache( &gNextHopCache, ipTarget, NO_ADDRESS );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Poke a negative entry into the cache so we don't keep trying to look this up.
|
|
THROTTLED_DBGPRINT(COMPAT, ("TCPIP found no route entry for %i.%i.%i.%i\n", ((PUCHAR)&ipTarget)[3], ((PUCHAR)&ipTarget)[2],
|
|
((PUCHAR)&ipTarget)[1], ((PUCHAR)&ipTarget)[0] ));
|
|
|
|
BrdgUpdateCache( &gNextHopCache, ipTarget, NO_ADDRESS );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(COMPAT, ("TPCIP failed route lookup IRP: %08x\n", status));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(COMPAT, ("Failed to allocate an IRP in BrdgCompGetNextHopForTarget!\n"));
|
|
}
|
|
|
|
// We are done talking to TCPIP
|
|
BrdgCompReleaseTCPIP();
|
|
}
|
|
// else no open channel to TCPIP
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompIsLocalIPAddress(
|
|
IN IPADDRESS ipAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether a given IP address is one of our local addresses.
|
|
|
|
Arguments:
|
|
|
|
ipAddr The address
|
|
|
|
Return Value:
|
|
|
|
TRUE if the given address is on our list of local addresses, FALSE
|
|
otherwise
|
|
|
|
--*/
|
|
{
|
|
LOCK_STATE LockState;
|
|
ULONG i;
|
|
PIPADDRESS pAddr = (PIPADDRESS)gLocalIPAddressList;
|
|
BOOLEAN bFound = FALSE;
|
|
|
|
NdisAcquireReadWriteLock( &gLocalIPAddressListLock, FALSE/*Read only*/, &LockState );
|
|
|
|
// There should be an integral number of IP addresses in the list!
|
|
SAFEASSERT( (gLocalIPAddressListLength % sizeof(IPADDRESS)) == 0 );
|
|
SAFEASSERT( (gLocalIPAddressListLength == 0) || (gLocalIPAddressList != NULL) );
|
|
|
|
for( i = 0L; i < gLocalIPAddressListLength / sizeof(IPADDRESS); i++ )
|
|
{
|
|
if( pAddr[i] == ipAddr )
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gLocalIPAddressListLock, &LockState );
|
|
|
|
return bFound;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
BrdgCompSendToMultipleAdapters(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pOriginalAdapt,
|
|
IN PUCHAR pPacketData,
|
|
IN BOOLEAN bCanRetain,
|
|
IN BOOLEAN bAllAdapters,
|
|
IN PPER_ADAPT_EDIT_FUNC pEditFunc,
|
|
IN PVOID pData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a packet (or a copy thereof) to multiple adapters. Usually used to send around
|
|
a broadcast packet.
|
|
|
|
Arguments:
|
|
|
|
pPacket The packet to send (or to send a copy of)
|
|
pOriginalAdapt The adapter the packet was originally received on (so
|
|
we can skip it). This can be NULL
|
|
pPacketData A pointer to the packet's data buffer
|
|
bCanRetain Whether we can retain the packet
|
|
bAllAdapters TRUE: Send to all adapters FALSE: send only to
|
|
adapters in compatibility mode
|
|
pEditFunc Optional function that gets called before sending to
|
|
each adapter (to edit the packet)
|
|
pData Cookie to pass to pEditFunc as context
|
|
|
|
Return Value:
|
|
|
|
TRUE if pPacket was retained, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
UINT numTargets = 0L, i;
|
|
PADAPT pAdapt;
|
|
PADAPT SendList[MAX_ADAPTERS];
|
|
LOCK_STATE LockState;
|
|
BOOLEAN bSentOriginal = FALSE; // Whether we have sent the packet we were given yet
|
|
|
|
//
|
|
// First we need a list of the adapters we intend to send this packet to
|
|
//
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*Read only*/, &LockState );
|
|
|
|
// Note each adapter to send to
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
// Don't need to acquire the global adapter characteristics lock to read the
|
|
// media state because we don't care about the global consistency of the
|
|
// adapters' characteristics here
|
|
if( (pAdapt != pOriginalAdapt) &&
|
|
(pAdapt->MediaState == NdisMediaStateConnected) && // Don't send to disconnected adapters
|
|
(pAdapt->State == Forwarding) && // Adapter must be in relaying state
|
|
(! pAdapt->bResetting) ) // Adapter must not be resetting
|
|
{
|
|
// If we're not trying to send to every single adapter, make sure
|
|
// this one is in compatibility mode
|
|
if( bAllAdapters || (pAdapt->bCompatibilityMode) )
|
|
{
|
|
if( numTargets < MAX_ADAPTERS )
|
|
{
|
|
// We will use this adapter outside the list lock; bump its refcount
|
|
BrdgAcquireAdapterInLock(pAdapt);
|
|
SendList[numTargets] = pAdapt;
|
|
numTargets++;
|
|
}
|
|
else
|
|
{
|
|
// Too many copies to send!
|
|
SAFEASSERT( FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Can let go of the adapter list now; we have copied out all the target adapters
|
|
// and incremented the refcount for the adapters we will be using.
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
|
|
for( i = 0; i < numTargets; i++ )
|
|
{
|
|
PNDIS_PACKET pPacketToSend;
|
|
PUCHAR pPacketToSendData;
|
|
|
|
if( bCanRetain && (! bSentOriginal) && (i == (numTargets - 1)) )
|
|
{
|
|
//
|
|
// Use the packet we were given.
|
|
// We must do this only with the last adapter since we need to be
|
|
// able to copy from it for every adapter before the last one.
|
|
//
|
|
pPacketToSend = pPacket;
|
|
pPacketToSendData = pPacketData;
|
|
bSentOriginal = TRUE;
|
|
}
|
|
else
|
|
{
|
|
UINT pPacketToSendSize;
|
|
|
|
// Duplicate the original packet yet another time so we have an editable
|
|
// copy for this target adapter
|
|
pPacketToSend = BrdgFwdMakeCompatCopyPacket(pPacket, &pPacketToSendData, &pPacketToSendSize, FALSE);
|
|
}
|
|
|
|
if( pPacketToSend != NULL )
|
|
{
|
|
BrdgCompEditAndSendPacket( pPacketToSend, pPacketToSendData, SendList[i], pEditFunc, pData );
|
|
}
|
|
|
|
// Done with this adapter
|
|
BrdgReleaseAdapter( SendList[i] );
|
|
}
|
|
|
|
return bSentOriginal;
|
|
}
|
|
|
|
VOID
|
|
BrdgCompRefreshOrInsertIPEntry(
|
|
IN IPADDRESS IPAddr,
|
|
IN PADAPT pAdapt,
|
|
IN PUCHAR pMACAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inserts a new entry into the IP forwarding table or refreshes an existing entry
|
|
|
|
Arguments:
|
|
|
|
IPAddr The address to insert
|
|
pAdapt The adapter to associate with the IP address
|
|
pMACAddr The MAC address to associate with the IP address
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PIP_TABLE_ENTRY pEntry;
|
|
BOOLEAN bIsNewEntry;
|
|
LOCK_STATE LockState;
|
|
|
|
if( BrdgCompIsUnicastIPAddress(IPAddr) )
|
|
{
|
|
pEntry = (PIP_TABLE_ENTRY)BrdgHashRefreshOrInsert( gIPForwardingTable, (PUCHAR)&IPAddr,
|
|
&bIsNewEntry, &LockState );
|
|
|
|
if( pEntry != NULL )
|
|
{
|
|
if( bIsNewEntry )
|
|
{
|
|
// This is a brand new table entry. Initialize it.
|
|
NdisAllocateSpinLock( &pEntry->lock );
|
|
pEntry->pAdapt = pAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( pEntry->macAddr, pMACAddr );
|
|
|
|
DBGPRINT(COMPAT, ("Learned the location of %i.%i.%i.%i\n", ((PUCHAR)&IPAddr)[3], ((PUCHAR)&IPAddr)[2],
|
|
((PUCHAR)&IPAddr)[1], ((PUCHAR)&IPAddr)[0]));
|
|
}
|
|
else
|
|
{
|
|
// This is an existing entry and we may only have a read lock
|
|
// held on the hash table. Use the entry's spin lock to protect
|
|
// us while we monkey with the contents
|
|
NdisAcquireSpinLock( &pEntry->lock );
|
|
|
|
pEntry->pAdapt = pAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( pEntry->macAddr, pMACAddr );
|
|
|
|
NdisReleaseSpinLock( &pEntry->lock );
|
|
}
|
|
|
|
// Since we got a non-NULL result we must release the table lock
|
|
NdisReleaseReadWriteLock( &gIPForwardingTable->tableLock, &LockState );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We shouldn't be getting called with non-unicast source IP addresses
|
|
//
|
|
THROTTLED_DBGPRINT(COMPAT, ("WARNING: Not noting non-unicast source IP address %i.%i.%i.%i from adapter %p!\n",
|
|
((PUCHAR)&IPAddr)[3], ((PUCHAR)&IPAddr)[2], ((PUCHAR)&IPAddr)[1], ((PUCHAR)&IPAddr)[0],
|
|
pAdapt ));
|
|
}
|
|
}
|
|
|
|
PUCHAR
|
|
BrdgCompIsBootPPacket(
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PIP_HEADER_INFO piphi
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether a given packet is a BOOTP packet
|
|
|
|
Arguments:
|
|
|
|
pPacketData Pointer to the packet's data buffer
|
|
packetLen Amount of data at pPacketDaa
|
|
piphi Info about the IP header of this packet
|
|
|
|
Return Value:
|
|
|
|
A pointer to the BOOTP payload within the packet, or NULL if the packet was not
|
|
a BOOTP Packet.
|
|
|
|
--*/
|
|
{
|
|
// After the IP header, there must be enough room for a UDP header and
|
|
// a basic BOOTP packet
|
|
if( packetLen < ETHERNET_HEADER_SIZE + (UINT)piphi->headerSize + SIZE_OF_UDP_HEADER +
|
|
SIZE_OF_BASIC_BOOTP_PACKET)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Protocol must be UDP
|
|
if( piphi->protocol != UDP_PROTOCOL )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Jump to the beginning of the UDP packet by skipping the IP header
|
|
pPacketData += ETHERNET_HEADER_SIZE + piphi->headerSize;
|
|
|
|
// The first two bytes are the source port and should be the
|
|
// BOOTP Client port (0x0044) or the BOOTP Server port (0x0043)
|
|
if( (pPacketData[0] != 00) ||
|
|
((pPacketData[1] != 0x44) && (pPacketData[1] != 0x43)) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// The next two bytes are the destination port and should be the BOOTP
|
|
// server port (0x0043) or the BOOTP client port (0x44)
|
|
if( (pPacketData[2] != 00) ||
|
|
((pPacketData[3] != 0x43) && (pPacketData[3] != 0x44)) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Skip ahead to the beginning of the BOOTP packet
|
|
pPacketData += SIZE_OF_UDP_HEADER;
|
|
|
|
// The first byte is the op code and should be 0x01 for a request
|
|
// or 0x02 for a reply
|
|
if( pPacketData[0] > 0x02 )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// The next byte is the hardware type and should be 0x01 for Ethernet
|
|
if( pPacketData[1] != 0x01 )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// The next byte is the address length and should be 0x06 for Ethernet
|
|
if( pPacketData[2] != 0x06 )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Everything checks out; this looks like a BOOTP request packet.
|
|
return pPacketData;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompDecodeIPHeader(
|
|
IN PUCHAR pHeader,
|
|
OUT PIP_HEADER_INFO piphi
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decodes basic information from the IP header (no options)
|
|
|
|
Arguments:
|
|
|
|
pHeader Pointer to an IP header
|
|
piphi Receives the info
|
|
|
|
Return Value:
|
|
|
|
TRUE: header was valid
|
|
FALSE: packet is not an IP packet
|
|
|
|
--*/
|
|
{
|
|
// First nibble of the header encodes the packet version, which must be 4.
|
|
if( (*pHeader >> 4) != 0x04 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Next nibble of the header encodes the length of the header in 32-bit words.
|
|
// This length must be at least 20 bytes or something is amiss.
|
|
piphi->headerSize = (*pHeader & 0x0F) * 4;
|
|
if( piphi->headerSize < 20 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Retrieve the protocol byte (offset 10)
|
|
piphi->protocol = pHeader[9];
|
|
|
|
// The source IP address begins at the 12th byte (most significant byte first)
|
|
piphi->ipSource = 0L;
|
|
piphi->ipSource |= pHeader[12] << 24;
|
|
piphi->ipSource |= pHeader[13] << 16;
|
|
piphi->ipSource |= pHeader[14] << 8;
|
|
piphi->ipSource |= pHeader[15];
|
|
|
|
// The destination IP address is next
|
|
piphi->ipTarget = 0L;
|
|
piphi->ipTarget |= pHeader[16] << 24;
|
|
piphi->ipTarget |= pHeader[17] << 16;
|
|
piphi->ipTarget |= pHeader[18] << 8;
|
|
piphi->ipTarget |= pHeader[19];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompDecodeARPPacket(
|
|
IN PUCHAR pPacketData,
|
|
IN UINT dataLen,
|
|
OUT PARPINFO pARPInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decodes an ARP packet
|
|
|
|
Arguments:
|
|
|
|
pPacketData Pointer to a packet's data buffer
|
|
dataLen Amount of data at pPacketData
|
|
pARPInfo Receives the info
|
|
|
|
Return Value:
|
|
|
|
TRUE: packet was valid
|
|
FALSE: packet is not an ARP packet
|
|
|
|
--*/
|
|
{
|
|
SAFEASSERT( pPacketData != NULL );
|
|
SAFEASSERT( pARPInfo != NULL );
|
|
|
|
// We can't process this if it's too small
|
|
if( dataLen < SIZE_OF_ARP_PACKET )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the ethertype for consistency (0x0806 is ARP)
|
|
if( (pPacketData[12] != 0x08) || (pPacketData[13] != 0x06) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the hardware type for consistency (0x0001 is classic Ethernet;
|
|
// 802 has a seperate value)
|
|
if( (pPacketData[14] != 0x00) || (pPacketData[15] != 0x01) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the protocol type for consistency (0x0800 is IPv4)
|
|
if( (pPacketData[16] != 0x08) || (pPacketData[17] != 0x00) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the length of the hardware address for consistency (must be 6 bytes)
|
|
if( pPacketData[18] != 0x06 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the length of the protocol address for consistency (must be 4 bytes)
|
|
if( pPacketData[19] != 0x04 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Next two bytes are the operation (0x0001 == request, 0x0002 == reply)
|
|
if( pPacketData[20] != 0x00 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if( pPacketData[21] == 0x01 )
|
|
{
|
|
pARPInfo->type = ArpRequest;
|
|
}
|
|
else if( pPacketData[21] == 0x02 )
|
|
{
|
|
pARPInfo->type = ArpReply;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Next 6 bytes are the sender's MAC address
|
|
pARPInfo->macSource[0] = pPacketData[22];
|
|
pARPInfo->macSource[1] = pPacketData[23];
|
|
pARPInfo->macSource[2] = pPacketData[24];
|
|
pARPInfo->macSource[3] = pPacketData[25];
|
|
pARPInfo->macSource[4] = pPacketData[26];
|
|
pARPInfo->macSource[5] = pPacketData[27];
|
|
|
|
// Next 4 bytes are the sender's protocol address (most significant byte first)
|
|
pARPInfo->ipSource = 0;
|
|
pARPInfo->ipSource |= pPacketData[28] << 24;
|
|
pARPInfo->ipSource |= pPacketData[29] << 16;
|
|
pARPInfo->ipSource |= pPacketData[30] << 8;
|
|
pARPInfo->ipSource |= pPacketData[31];
|
|
|
|
//
|
|
// Next 6 bytes are the target's MAC address. For a request, these bytes are
|
|
// meaningless.
|
|
//
|
|
pARPInfo->macTarget[0] = pPacketData[32];
|
|
pARPInfo->macTarget[1] = pPacketData[33];
|
|
pARPInfo->macTarget[2] = pPacketData[34];
|
|
pARPInfo->macTarget[3] = pPacketData[35];
|
|
pARPInfo->macTarget[4] = pPacketData[36];
|
|
pARPInfo->macTarget[5] = pPacketData[37];
|
|
|
|
// Next 4 bytes are the sender's protocol address (most significant byte first)
|
|
pARPInfo->ipTarget = 0;
|
|
pARPInfo->ipTarget |= pPacketData[38] << 24;
|
|
pARPInfo->ipTarget |= pPacketData[39] << 16;
|
|
pARPInfo->ipTarget |= pPacketData[40] << 8;
|
|
pARPInfo->ipTarget |= pPacketData[41];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
BrdgCompTransmitDeferredARP(
|
|
IN PVOID pData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmits an ARP packet whose transmission was deferred
|
|
|
|
Arguments:
|
|
|
|
pData Info on the deferred ARP packet to
|
|
be transmitted
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PDEFERRED_ARP pda = (PDEFERRED_ARP)pData;
|
|
|
|
BrdgCompTransmitARPPacket( pda->pTargetAdapt, &pda->ai );
|
|
|
|
// We incremented this adapter's refcount when setting up the
|
|
// function deferral
|
|
BrdgReleaseAdapter( pda->pTargetAdapt );
|
|
|
|
// Free the memory for this request
|
|
NdisFreeMemory( pda, sizeof(DEFERRED_ARP), 0 );
|
|
}
|
|
|
|
VOID
|
|
BrdgCompTransmitARPPacket(
|
|
IN PADAPT pAdapt,
|
|
IN PARPINFO pARPInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmits an ARP packet
|
|
|
|
Arguments:
|
|
|
|
pAdapt Adapter to transmit on
|
|
pARPInfo The info to transmit as an ARP packet
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
UCHAR ARPPacket[SIZE_OF_ARP_PACKET];
|
|
|
|
SAFEASSERT( pAdapt != NULL );
|
|
SAFEASSERT( pARPInfo != NULL );
|
|
SAFEASSERT( (pARPInfo->type == ArpRequest) || (pARPInfo->type == ArpReply) );
|
|
|
|
//
|
|
// Fill in the destination MAC address. If the operation is a discovery,
|
|
// the target MAC address is the broadcast address. If it is a reply, the
|
|
// target MAC address is the target machine's MAC address.
|
|
//
|
|
if( pARPInfo->type == ArpRequest )
|
|
{
|
|
ARPPacket[0] = ARPPacket[1] = ARPPacket[2] = ARPPacket[3] =
|
|
ARPPacket[4] = ARPPacket[5] = 0xFF;
|
|
}
|
|
else
|
|
{
|
|
ARPPacket[0] = pARPInfo->macTarget[0];
|
|
ARPPacket[1] = pARPInfo->macTarget[1];
|
|
ARPPacket[2] = pARPInfo->macTarget[2];
|
|
ARPPacket[3] = pARPInfo->macTarget[3];
|
|
ARPPacket[4] = pARPInfo->macTarget[4];
|
|
ARPPacket[5] = pARPInfo->macTarget[5];
|
|
}
|
|
|
|
// Fill in the source MAC address
|
|
ARPPacket[6] = pARPInfo->macSource[0];
|
|
ARPPacket[7] = pARPInfo->macSource[1];
|
|
ARPPacket[8] = pARPInfo->macSource[2];
|
|
ARPPacket[9] = pARPInfo->macSource[3];
|
|
ARPPacket[10] = pARPInfo->macSource[4];
|
|
ARPPacket[11] = pARPInfo->macSource[5];
|
|
|
|
// Next 2 bytes are the EtherType (0x0806 == ARP)
|
|
ARPPacket[12] = 0x08;
|
|
ARPPacket[13] = 0x06;
|
|
|
|
// Next 2 bytes are 0x0001 for classic Ethernet
|
|
// (802 has a seperate value)
|
|
ARPPacket[14] = 0x00;
|
|
ARPPacket[15] = 0x01;
|
|
|
|
// Next 2 bytes indicate that this is ARP for IPv4 traffic
|
|
ARPPacket[16] = 0x08;
|
|
ARPPacket[17] = 0x00;
|
|
|
|
// Next byte indicates the length of the hardware address (6 bytes)
|
|
ARPPacket[18] = 0x6;
|
|
|
|
// Next byte indicates the length of the protocol address (4 bytes)
|
|
ARPPacket[19] = 0x4;
|
|
|
|
// Next byte is the operation (1 == request, 2 == reply)
|
|
if( pARPInfo->type == ArpRequest )
|
|
{
|
|
ARPPacket[20] = 0x00;
|
|
ARPPacket[21] = 0x01;
|
|
}
|
|
else
|
|
{
|
|
ARPPacket[20] = 0x00;
|
|
ARPPacket[21] = 0x02;
|
|
}
|
|
|
|
// Next 6 bytes are the sender's MAC address (LSB first)
|
|
ARPPacket[22] = pARPInfo->macSource[0];
|
|
ARPPacket[23] = pARPInfo->macSource[1];
|
|
ARPPacket[24] = pARPInfo->macSource[2];
|
|
ARPPacket[25] = pARPInfo->macSource[3];
|
|
ARPPacket[26] = pARPInfo->macSource[4];
|
|
ARPPacket[27] = pARPInfo->macSource[5];
|
|
|
|
// Next 4 bytes are the sender's protocol address (most significant byte first)
|
|
ARPPacket[28] = (UCHAR)((pARPInfo->ipSource >> 24) & 0xFF);
|
|
ARPPacket[29] = (UCHAR)((pARPInfo->ipSource >> 16) & 0xFF);
|
|
ARPPacket[30] = (UCHAR)((pARPInfo->ipSource >> 8) & 0xFF);
|
|
ARPPacket[31] = (UCHAR)(pARPInfo->ipSource & 0xFF);
|
|
|
|
//
|
|
// Next 6 bytes are the target's MAC address. For a request, these bytes are
|
|
// ignored and set to zero.
|
|
//
|
|
if( pARPInfo->type == ArpRequest )
|
|
{
|
|
ARPPacket[32] = ARPPacket[33] = ARPPacket[34] = ARPPacket[35] =
|
|
ARPPacket[36] = ARPPacket[37] = 0x00;
|
|
}
|
|
else
|
|
{
|
|
// MAC address is transmitted LSB first.
|
|
ARPPacket[32] = pARPInfo->macTarget[0];
|
|
ARPPacket[33] = pARPInfo->macTarget[1];
|
|
ARPPacket[34] = pARPInfo->macTarget[2];
|
|
ARPPacket[35] = pARPInfo->macTarget[3];
|
|
ARPPacket[36] = pARPInfo->macTarget[4];
|
|
ARPPacket[37] = pARPInfo->macTarget[5];
|
|
}
|
|
|
|
// Next 4 bytes are the target's protocol address (most significant byte first)
|
|
ARPPacket[38] = (UCHAR)((pARPInfo->ipTarget >> 24) & 0xFF);
|
|
ARPPacket[39] = (UCHAR)((pARPInfo->ipTarget >> 16) & 0xFF);
|
|
ARPPacket[40] = (UCHAR)((pARPInfo->ipTarget >> 8) & 0xFF);
|
|
ARPPacket[41] = (UCHAR)(pARPInfo->ipTarget & 0xFF);
|
|
|
|
// Send the finished packet
|
|
Status = BrdgFwdSendBuffer( pAdapt, ARPPacket, sizeof(ARPPacket) );
|
|
|
|
if( Status != NDIS_STATUS_SUCCESS )
|
|
{
|
|
THROTTLED_DBGPRINT(COMPAT, ("ARP packet send failed: %08x\n", Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// pTargetAdapt comes back with incremented refcount if
|
|
// *pbIsRequest == FALSE and *pTargetAdapt != NULL
|
|
//
|
|
BOOLEAN
|
|
BrdgCompPreprocessBootPPacket(
|
|
IN PUCHAR pPacketData,
|
|
IN PIP_HEADER_INFO piphi,
|
|
IN PUCHAR pBootPData, // Actual BOOTP packet
|
|
IN PADAPT pAdapt, // Receiving adapt (or NULL for outbound from local machine)
|
|
OUT PBOOLEAN pbIsRequest,
|
|
OUT PADAPT *ppTargetAdapt, // Only if bIsRequest == FALSE
|
|
OUT PUCHAR targetMAC // Only if bIsRequest == FALSE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does preliminary processing of a BOOTP packet common to the inbound and outbound case
|
|
|
|
Arguments:
|
|
|
|
pPacketData Pointer to a packet's data buffer
|
|
piphi Info on the packet's IP header
|
|
pBootPData Pointer to the BOOTP payload within the packet
|
|
pAdapt Receiving adapter (or NULL if this packet is outbound from
|
|
the local machine)
|
|
pbIsRequest Receives a flag indicating if this is a BOOTP request
|
|
ppTargetAdapt Receives the target adapter this packet should be relayed to
|
|
(only valid if bIsRequest == FALSE and return == TRUE)
|
|
targetMAC The MAC address this packet should be relayed to (valid under
|
|
same conditions as ppTargetAdapt)
|
|
|
|
Return Value:
|
|
|
|
TRUE : packet was processed successfully
|
|
FALSE : an error occured or something is wrong with the packet
|
|
|
|
--*/
|
|
{
|
|
PDHCP_TABLE_ENTRY pEntry;
|
|
ULONG xid;
|
|
LOCK_STATE LockState;
|
|
|
|
SAFEASSERT( pbIsRequest != NULL );
|
|
SAFEASSERT( ppTargetAdapt != NULL );
|
|
SAFEASSERT( targetMAC != NULL );
|
|
|
|
// Decode the xid (bytes 5 through 8)
|
|
xid = 0L;
|
|
xid |= pBootPData[4] << 24;
|
|
xid |= pBootPData[5] << 16;
|
|
xid |= pBootPData[6] << 8;
|
|
xid |= pBootPData[7];
|
|
|
|
// Byte 0 is the operation; 1 for a request, 2 for a reply
|
|
if( pBootPData[0] == 0x01 )
|
|
{
|
|
BOOLEAN bIsNewEntry;
|
|
|
|
// This is a request. We need to note the correspondence betweeen
|
|
// this client's XID and its adapter and MAC address
|
|
pEntry = (PDHCP_TABLE_ENTRY)BrdgHashRefreshOrInsert( gPendingDHCPTable, (PUCHAR)&xid, &bIsNewEntry,
|
|
&LockState );
|
|
|
|
if( pEntry != NULL )
|
|
{
|
|
if( bIsNewEntry )
|
|
{
|
|
// Initialize the entry.
|
|
// The client's hardware address is at offset 29
|
|
NdisAllocateSpinLock( &pEntry->lock );
|
|
ETH_COPY_NETWORK_ADDRESS( pEntry->requestorMAC, &pBootPData[28] );
|
|
pEntry->pRequestorAdapt = pAdapt; // Can be NULL for local machine
|
|
|
|
DBGPRINT(COMPAT, ("Saw new DHCP XID: %x\n", xid));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// An entry already existed for this XID. This is fine if the existing information
|
|
// matches what we're trying to record, but it's also possible that two stations
|
|
// decided independently to use the same XID, or that the same station changed
|
|
// apparent MAC address and/or adapter due to topology changes. Our scheme breaks
|
|
// down under these circumstances.
|
|
//
|
|
// Either way, use the most recent information possible; clobber the existing
|
|
// information with the latest.
|
|
//
|
|
|
|
NdisAcquireSpinLock( &pEntry->lock );
|
|
|
|
#if DBG
|
|
{
|
|
UINT Result;
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ( pEntry->requestorMAC, &pBootPData[28], &Result );
|
|
|
|
// Warn if the data changed, as this probably signals a problem
|
|
if( Result != 0 )
|
|
{
|
|
DBGPRINT(COMPAT, ("[COMPAT] WARNING: Station with MAC address %02x:%02x:%02x:%02x:%02x:%02x is using DHCP XID %x at the same time as station %02x:%02x:%02x:%02x:%02x:%02x!\n",
|
|
pBootPData[28], pBootPData[29], pBootPData[30], pBootPData[31], pBootPData[32], pBootPData[33],
|
|
xid, pEntry->requestorMAC[0], pEntry->requestorMAC[1], pEntry->requestorMAC[2],
|
|
pEntry->requestorMAC[3], pEntry->requestorMAC[4], pEntry->requestorMAC[5] ));
|
|
}
|
|
else if( pEntry->pRequestorAdapt != pAdapt )
|
|
{
|
|
DBGPRINT(COMPAT, ("[COMPAT] WARNING: Station with MAC address %02x:%02x:%02x:%02x:%02x:%02x appeared to change from adapter %p to adapter %p during DHCP request!\n",
|
|
pBootPData[28], pBootPData[29], pBootPData[30],
|
|
pBootPData[31], pBootPData[32], pBootPData[33],
|
|
pEntry->pRequestorAdapt, pAdapt ));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ETH_COPY_NETWORK_ADDRESS( pEntry->requestorMAC, &pBootPData[28] );
|
|
pEntry->pRequestorAdapt = pAdapt; // Can be NULL for local machine
|
|
|
|
NdisReleaseSpinLock( &pEntry->lock );
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gPendingDHCPTable->tableLock, &LockState );
|
|
}
|
|
else
|
|
{
|
|
// This packet could not be processed
|
|
DBGPRINT(COMPAT, ("Couldn't create table entry for BOOTP packet!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
*pbIsRequest = TRUE;
|
|
// ppTargetAdapt and targetMAC are not defined for this case
|
|
return TRUE;
|
|
}
|
|
else if ( pBootPData[0] == 0x02 )
|
|
{
|
|
// Look up the xid for this transaction to recover the MAC address of the client
|
|
pEntry = (PDHCP_TABLE_ENTRY)BrdgHashFindEntry( gPendingDHCPTable, (PUCHAR)&xid, &LockState );
|
|
|
|
if( pEntry != NULL )
|
|
{
|
|
NdisAcquireSpinLock( &pEntry->lock );
|
|
ETH_COPY_NETWORK_ADDRESS( targetMAC, pEntry->requestorMAC );
|
|
*ppTargetAdapt = pEntry->pRequestorAdapt;
|
|
NdisReleaseSpinLock( &pEntry->lock );
|
|
|
|
//
|
|
// We will use this adapter outside the table lock. NULL is a permissible
|
|
// value that indicates that the local machine is the requestor for
|
|
// this xid.
|
|
//
|
|
if( *ppTargetAdapt != NULL )
|
|
{
|
|
BrdgAcquireAdapterInLock( *ppTargetAdapt );
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gPendingDHCPTable->tableLock, &LockState );
|
|
}
|
|
|
|
if( pEntry != NULL )
|
|
{
|
|
*pbIsRequest = FALSE;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(COMPAT, ("Couldn't find a table entry for XID %x!\n", xid));
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Someone passed us a crummy packet
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// ===========================================================================
|
|
//
|
|
// INBOUND PACKET PROCESSING
|
|
//
|
|
// ===========================================================================
|
|
|
|
VOID
|
|
BrdgCompSendProxyARPRequests(
|
|
IN PARPINFO pai,
|
|
IN PADAPT pOriginalAdapt,
|
|
IN BOOLEAN bSendToNonCompat
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Floods ARP requests out appropriate adapters in response to an ARP request
|
|
for which we did not have information about the target.
|
|
|
|
Arguments:
|
|
|
|
pai Info on the inbound request
|
|
pOriginalAdapt Adapter the request was indicated on
|
|
bSendToNonCompat Whether we need to send the request to all adapters
|
|
or just compatibility adapters
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
UINT numTargets = 0L, i;
|
|
PADAPT pAdapt;
|
|
PADAPT SendList[MAX_ADAPTERS];
|
|
LOCK_STATE LockState;
|
|
|
|
SAFEASSERT( pai->type == ArpRequest );
|
|
|
|
//
|
|
// First we need a list of the adapters we intend to send this packet to
|
|
//
|
|
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*Read only*/, &LockState );
|
|
|
|
// Note each adapter to send to
|
|
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
|
|
{
|
|
// Don't need to acquire the global adapter characteristics lock to read the
|
|
// media state because we don't care about the global consistency of the
|
|
// adapters' characteristics here
|
|
if( (pAdapt != pOriginalAdapt ) && // Don't send on the original adapter
|
|
(pAdapt->MediaState == NdisMediaStateConnected) && // Don't send to disconnected adapters
|
|
(pAdapt->State == Forwarding) && // Adapter must be in relaying state
|
|
(! pAdapt->bResetting) ) // Adapter must not be resetting
|
|
{
|
|
// If we're not trying to send to every single adapter, make sure
|
|
// this one is in compatibility mode
|
|
if( bSendToNonCompat || (pAdapt->bCompatibilityMode) )
|
|
{
|
|
if( numTargets < MAX_ADAPTERS )
|
|
{
|
|
// We will use this adapter outside the list lock; bump its refcount
|
|
BrdgAcquireAdapterInLock(pAdapt);
|
|
SendList[numTargets] = pAdapt;
|
|
numTargets++;
|
|
}
|
|
else
|
|
{
|
|
// Too many copies to send!
|
|
SAFEASSERT( FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Can let go of the adapter list now; we have copied out all the target adapters
|
|
// and incremented the refcount for the adapters we will be using.
|
|
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
|
|
|
|
for( i = 0; i < numTargets; i++ )
|
|
{
|
|
// For each adapter, the source MAC address is the adapter's MAC address
|
|
ETH_COPY_NETWORK_ADDRESS( pai->macSource, SendList[i]->MACAddr );
|
|
|
|
// Send the ARP request
|
|
BrdgCompTransmitARPPacket( SendList[i], pai );
|
|
|
|
// Done with this adapter
|
|
BrdgReleaseAdapter( SendList[i] );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BrdgCompAnswerPendingARP(
|
|
IN PHASH_TABLE_ENTRY pEntry,
|
|
IN PVOID pData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a reply to a station that is waiting for an ARP reply. Called when we find
|
|
an entry to this effect in our pending-ARP table.
|
|
|
|
We do not send an ARP reply to the discovering station if it turns out that the
|
|
station it is looking for is on the same segment as it.
|
|
|
|
Arguments:
|
|
|
|
pEntry The entry in the pending-ARP table telling us about
|
|
the station waiting for information
|
|
|
|
pData The adapter we received an ARP reply on that
|
|
triggered this operation
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PARP_TABLE_ENTRY pate = (PARP_TABLE_ENTRY)pEntry;
|
|
PADAPT pReceivedAdapt = (PADAPT)pData;
|
|
PARP_TABLE_KEY pKey;
|
|
|
|
pKey = (PARP_TABLE_KEY)pate->hte.key;
|
|
|
|
if( pKey->ipReqestor != 0L )
|
|
{
|
|
PADAPT pOriginalAdapt;
|
|
UCHAR originalMAC[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
// Copy the information out of the table entry
|
|
NdisAcquireSpinLock( &pate->lock );
|
|
pOriginalAdapt = pate->pOriginalAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( originalMAC, pate->originalMAC );
|
|
NdisReleaseSpinLock( &pate->lock );
|
|
|
|
//
|
|
// The station we just discovered must be on a different segment
|
|
// from the discovering station for us to send back a reply.
|
|
//
|
|
if( pOriginalAdapt != pReceivedAdapt )
|
|
{
|
|
PDEFERRED_ARP pda;
|
|
NDIS_STATUS Status;
|
|
|
|
// The adapters are different. We should send a reply.
|
|
// We need to defer the actual transmission of the reply so we
|
|
// don't perform it with a lock held on the pending ARP
|
|
// table.
|
|
Status = NdisAllocateMemoryWithTag( &pda, sizeof(DEFERRED_ARP), 'gdrB' );
|
|
|
|
if( Status == NDIS_STATUS_SUCCESS )
|
|
{
|
|
pda->pTargetAdapt = pOriginalAdapt;
|
|
|
|
// We will use the adapter pointer outside the table lock
|
|
BrdgAcquireAdapterInLock( pda->pTargetAdapt );
|
|
|
|
pda->ai.ipTarget = pKey->ipReqestor;
|
|
ETH_COPY_NETWORK_ADDRESS( pda->ai.macTarget, originalMAC );
|
|
|
|
// Pretend to be the IP address the requestor is looking for
|
|
pda->ai.ipSource = pKey->ipTarget;
|
|
ETH_COPY_NETWORK_ADDRESS( pda->ai.macSource, pda->pTargetAdapt->MACAddr );
|
|
|
|
pda->ai.type = ArpReply;
|
|
|
|
// Queue up the call to BrdgCompTransmitDeferredARP
|
|
BrdgDeferFunction( BrdgCompTransmitDeferredARP, pda );
|
|
}
|
|
else
|
|
{
|
|
// We failed the allocation. Not much we can do.
|
|
DBGPRINT(COMPAT, ("Memory allocation failed in BrdgCompAnswerPendingARP!\n"));
|
|
}
|
|
}
|
|
// else the discovering station and the station we discovered are on the same
|
|
// adapter; don't reply.
|
|
}
|
|
else
|
|
{
|
|
// This entry exists only to indicate that the local machine is also trying to discover
|
|
// this IP address. Ignore it.
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
BrdgCompIndicateInboundARPReply(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Indicates an ARP reply to the local machine
|
|
|
|
Arguments:
|
|
|
|
pPacket The ARP reply packet
|
|
pAdapt The receiving adapter
|
|
bCanRetain If we can retain the packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Size of data in buffer
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
PUCHAR pTargetMAC;
|
|
UINT Result;
|
|
|
|
if( ! bCanRetain )
|
|
{
|
|
// We're not allowed to use the packet we're given to indicate.
|
|
// Allocate a new one to hold the data.
|
|
pPacket = BrdgFwdMakeCompatCopyPacket( pPacket, &pPacketData, &packetLen, FALSE );
|
|
|
|
if( pPacket == NULL )
|
|
{
|
|
// We failed to get a packet.
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Rewrite the target MAC address in the ARP reply. This portion
|
|
// of the packet is at offset 32.
|
|
pTargetMAC = pPacketData + 32;
|
|
|
|
// Check to see if the target MAC address is the adapter's MAC address,
|
|
// as it should be
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ( pTargetMAC, pAdapt->MACAddr, &Result );
|
|
|
|
if( Result == 0 )
|
|
{
|
|
// Rewrite the target MAC address to the bridge's MAC address
|
|
ETH_COPY_NETWORK_ADDRESS( pTargetMAC, gCompMACAddress );
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(COMPAT, ("WARNING: Mismatch between frame MAC target and ARP payload target in ARP reply!\n"));
|
|
}
|
|
|
|
BrdgCompIndicatePacket( pPacket, pPacketData, pAdapt );
|
|
|
|
return bCanRetain;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundARPRequest(
|
|
IN PARPINFO pai,
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an inbound ARP request
|
|
|
|
Arguments:
|
|
|
|
pai The decoded info
|
|
pPacket The ARP request packet
|
|
pAdapt The receiving adapter
|
|
bCanRetain If we can retain the packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Size of data in buffer
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
PIP_TABLE_ENTRY pipte;
|
|
LOCK_STATE LockState;
|
|
BOOLEAN bSendReply = FALSE;
|
|
|
|
SAFEASSERT( pai->type == ArpRequest );
|
|
|
|
// See if we already have the target IP address in our table
|
|
pipte = (PIP_TABLE_ENTRY)BrdgHashFindEntry( gIPForwardingTable, (PUCHAR)&pai->ipTarget,
|
|
&LockState );
|
|
|
|
if( pipte != NULL )
|
|
{
|
|
//
|
|
// Compare the adapter the target is reachable on to the adapter that
|
|
// we got the request on while we still have the table lock.
|
|
//
|
|
// We should only send an ARP reply if the requesting station is on
|
|
// a different adapter than the station he is trying to discover.
|
|
//
|
|
bSendReply = (BOOLEAN)(pipte->pAdapt != pAdapt);
|
|
|
|
// Release the table lock.
|
|
NdisReleaseReadWriteLock( &gIPForwardingTable->tableLock, &LockState );
|
|
}
|
|
|
|
if( bSendReply )
|
|
{
|
|
IPADDRESS ipTransmitter = pai->ipSource;
|
|
|
|
DBGPRINT(COMPAT, ("ANSWERING ARP request for %i.%i.%i.%i\n",
|
|
((PUCHAR)&pai->ipTarget)[3], ((PUCHAR)&pai->ipTarget)[2],
|
|
((PUCHAR)&pai->ipTarget)[1], ((PUCHAR)&pai->ipTarget)[0] ));
|
|
|
|
// We found the target station. Use our ARPINFO structure to build a
|
|
// reply right back to the sending station.
|
|
pai->type = ArpReply;
|
|
|
|
// Pretend to be the IP station the transmitting station is asking for
|
|
pai->ipSource = pai->ipTarget;
|
|
|
|
// Send to the requesting station
|
|
ETH_COPY_NETWORK_ADDRESS( pai->macTarget, pai->macSource );
|
|
pai->ipTarget = ipTransmitter;
|
|
|
|
// Fill in the adapter's own MAC address as the source
|
|
ETH_COPY_NETWORK_ADDRESS( pai->macSource, pAdapt->MACAddr );
|
|
|
|
// Transmit the answer right now!
|
|
BrdgCompTransmitARPPacket( pAdapt, pai );
|
|
}
|
|
else
|
|
{
|
|
// We didn't find the address the transmitting station is asking for.
|
|
// We'll need to proxy the request onto other adapters to discover
|
|
// the target station.
|
|
|
|
// We need to proxy onto regular adapters too if the original adapter
|
|
// was compatibility-mode.
|
|
BOOLEAN bSendToNonCompat = pAdapt->bCompatibilityMode;
|
|
PARP_TABLE_ENTRY pEntry;
|
|
LOCK_STATE LockState;
|
|
BOOLEAN bIsNewEntry;
|
|
ARP_TABLE_KEY atk;
|
|
|
|
// Record the fact that we've proxied out this request
|
|
atk.ipReqestor = pai->ipSource;
|
|
atk.ipTarget = pai->ipTarget;
|
|
pEntry = (PARP_TABLE_ENTRY)BrdgHashRefreshOrInsert( gPendingARPTable, (PUCHAR)&atk,
|
|
&bIsNewEntry, &LockState );
|
|
|
|
if( pEntry != NULL )
|
|
{
|
|
if( bIsNewEntry )
|
|
{
|
|
// This is a new table entry, as expected. Initialize it.
|
|
NdisAllocateSpinLock( &pEntry->lock );
|
|
pEntry->pOriginalAdapt = pAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( pEntry->originalMAC, pai->macSource );
|
|
}
|
|
else
|
|
{
|
|
// There was already a pending-ARP entry for this source and target
|
|
// IP address. Refresh the information in the entry on the slim
|
|
// chance that the requesting machine has changed apparent MAC
|
|
// address or adapter due to topology changes or the like.
|
|
NdisAcquireSpinLock( &pEntry->lock );
|
|
pEntry->pOriginalAdapt = pAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( pEntry->originalMAC, pai->macSource );
|
|
NdisReleaseSpinLock( &pEntry->lock );
|
|
}
|
|
|
|
// We are responsible for releasing the table lock since
|
|
// BrdgHashRefreshOrInsert() came back non-NULL
|
|
NdisReleaseReadWriteLock( &gPendingARPTable->tableLock, &LockState );
|
|
}
|
|
|
|
// This function twiddles the ARPINFO structure you pass it,
|
|
// but that's OK by us.
|
|
BrdgCompSendProxyARPRequests( pai, pAdapt, bSendToNonCompat );
|
|
}
|
|
|
|
// Always indicate ARP requests to the local machine so it can note the
|
|
// information about the sender and reply if it wants.
|
|
return BrdgCompIndicatePacketOrPacketCopy( pPacket, pPacketData, bCanRetain, pAdapt, NULL, NULL );
|
|
}
|
|
|
|
// Returns whether the packet was retained
|
|
BOOLEAN
|
|
BrdgCompProcessInboundARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an inbound ARP packet
|
|
|
|
Arguments:
|
|
|
|
pPacket The ARP request packet
|
|
pAdapt The receiving adapter
|
|
bCanRetain If we can retain the packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Size of data in buffer
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
ARPINFO ai;
|
|
|
|
if( BrdgCompDecodeARPPacket(pPacketData, packetLen, &ai) )
|
|
{
|
|
BOOLEAN bRetained;
|
|
|
|
// Regardless of what kind of packet this is, we always note
|
|
// the correspondence between the sender's IP address and
|
|
// MAC address.
|
|
BrdgCompRefreshOrInsertIPEntry( ai.ipSource, pAdapt, ai.macSource );
|
|
|
|
// Always see if the information we just learned would let us
|
|
// proxy back a reply to a station doing a discovery.
|
|
BrdgHashPrefixMultiMatch( gPendingARPTable, (PUCHAR)&ai.ipSource, sizeof(IPADDRESS),
|
|
BrdgCompAnswerPendingARP, pAdapt );
|
|
|
|
if( ai.type == ArpReply )
|
|
{
|
|
BOOLEAN bIndicateReply;
|
|
ARP_TABLE_KEY atk;
|
|
LOCK_STATE LockState;
|
|
|
|
//
|
|
// The packet is an ARP reply.
|
|
//
|
|
|
|
// See if there's a table entry indicating that the local machine is trying to
|
|
// resolve this target address
|
|
atk.ipTarget = ai.ipSource;
|
|
atk.ipReqestor = 0L;
|
|
|
|
if( BrdgHashFindEntry(gPendingARPTable, (PUCHAR)&atk, &LockState) != NULL )
|
|
{
|
|
bIndicateReply = TRUE;
|
|
NdisReleaseReadWriteLock( &gPendingARPTable->tableLock, &LockState );
|
|
}
|
|
else
|
|
{
|
|
bIndicateReply = FALSE;
|
|
}
|
|
|
|
// We can't indicate the reply if we don't have the bridge's overall
|
|
// MAC address available
|
|
if( bIndicateReply && gCompHaveMACAddress )
|
|
{
|
|
bRetained = BrdgCompIndicateInboundARPReply( pPacket, pAdapt, bCanRetain, pPacketData, packetLen );
|
|
}
|
|
else
|
|
{
|
|
bRetained = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The packet is an ARP request.
|
|
//
|
|
|
|
// This function trashes ai, but that's OK.
|
|
bRetained = BrdgCompProcessInboundARPRequest( &ai, pPacket, pAdapt, bCanRetain, pPacketData, packetLen );
|
|
}
|
|
|
|
// Sanity
|
|
if( ! bCanRetain )
|
|
{
|
|
SAFEASSERT( !bRetained );
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
else
|
|
{
|
|
// The inbound ARP packet is somehow invalid. Process it as a regular packet
|
|
// (which should indicate it to the local machine) in case it's carrying something
|
|
// we don't understand.
|
|
return BrdgCompProcessInboundNonARPPacket( pPacket, pAdapt, bCanRetain, pPacketData, packetLen );
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundIPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PIP_HEADER_INFO piphi,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PPER_ADAPT_EDIT_FUNC pEditFunc,
|
|
IN PVOID pData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an inbound IP packet
|
|
|
|
Arguments:
|
|
|
|
pPacket The IP packet
|
|
piphi Decoded IP header information
|
|
pAdapt The receiving adapter
|
|
bCanRetain If we can retain the packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Size of data in buffer
|
|
pEditFunc Optional function that must be called
|
|
for each adapter before transmission
|
|
pData Context cookie for pEditFunc
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bRetained;
|
|
PIP_TABLE_ENTRY pipte;
|
|
LOCK_STATE LockState;
|
|
|
|
//
|
|
// We refresh our forwarding table with each IP packet we see. Find the entry
|
|
// for this IP address
|
|
//
|
|
if( BrdgCompIsUnicastIPAddress(piphi->ipSource) )
|
|
{
|
|
pipte = (PIP_TABLE_ENTRY)BrdgHashFindEntry( gIPForwardingTable, (PUCHAR)&piphi->ipSource, &LockState );
|
|
|
|
if( pipte != NULL )
|
|
{
|
|
BOOLEAN bInfoMatches = FALSE;
|
|
|
|
//
|
|
// Make sure the information in this entry is correct. If it's not, we do NOT clobber the old
|
|
// information, nor do we refresh the old entry; we want it to time out in due course.
|
|
//
|
|
// We only create IP forwarding table entries in response to ARP packets, as that is the only
|
|
// officially sanctioned way of learning the correspondence between an IP address and a MAC address.
|
|
//
|
|
NdisAcquireSpinLock( &pipte->lock );
|
|
if( pipte->pAdapt == pAdapt )
|
|
{
|
|
UINT Result;
|
|
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ( pipte->macAddr, &pPacketData[ETH_LENGTH_OF_ADDRESS], &Result );
|
|
|
|
if( Result == 0 )
|
|
{
|
|
bInfoMatches = TRUE;
|
|
}
|
|
}
|
|
NdisReleaseSpinLock( &pipte->lock );
|
|
|
|
if( bInfoMatches )
|
|
{
|
|
// Refresh the entry
|
|
BrdgHashRefreshEntry( (PHASH_TABLE_ENTRY)pipte );
|
|
}
|
|
else
|
|
{
|
|
// The info is mismatched; let the entry fester
|
|
THROTTLED_DBGPRINT(COMPAT, ("WARNING: Saw a packet from %i.%i.%i.%i that did not match its forwarding table entry! Table is %02x:%02x:%02x:%02x:%02x:%02x, packet is %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
((PUCHAR)&piphi->ipSource)[3], ((PUCHAR)&piphi->ipSource)[2], ((PUCHAR)&piphi->ipSource)[1],
|
|
((PUCHAR)&piphi->ipSource)[0], pipte->macAddr[0], pipte->macAddr[1], pipte->macAddr[2],
|
|
pipte->macAddr[3], pipte->macAddr[4], pipte->macAddr[5], pPacketData[ETH_LENGTH_OF_ADDRESS],
|
|
pPacketData[ETH_LENGTH_OF_ADDRESS + 1], pPacketData[ETH_LENGTH_OF_ADDRESS + 2], pPacketData[ETH_LENGTH_OF_ADDRESS + 3],
|
|
pPacketData[ETH_LENGTH_OF_ADDRESS + 4], pPacketData[ETH_LENGTH_OF_ADDRESS + 5] ));
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gIPForwardingTable->tableLock, &LockState );
|
|
}
|
|
else
|
|
{
|
|
// CONSIDER: Make a forwarding table entry here? Are there cases where this would be undesirable?
|
|
//
|
|
//THROTTLED_DBGPRINT(COMPAT, ("WARNING: Saw IP packet before ARP from %i.%i.%i.%i\n",
|
|
// ((PUCHAR)&piphi->ipSource)[3], ((PUCHAR)&piphi->ipSource)[2], ((PUCHAR)&piphi->ipSource)[1],
|
|
// ((PUCHAR)&piphi->ipSource)[0] ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The source IP address on this packet is to be ignored.
|
|
// Just about the only thing we expect is the zero address
|
|
//
|
|
if( piphi->ipSource != 0L )
|
|
{
|
|
THROTTLED_DBGPRINT(COMPAT, ("Saw a packet with a non-unicast source IP address %i.%i.%i.%i on adapter %p!\n",
|
|
((PUCHAR)&piphi->ipSource)[3], ((PUCHAR)&piphi->ipSource)[2], ((PUCHAR)&piphi->ipSource)[1],
|
|
((PUCHAR)&piphi->ipSource)[0], pAdapt));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we have refreshed the IP forwarding table entry for the sending station,
|
|
// figure out where to send the packet based on its destination.
|
|
//
|
|
|
|
// The target MAC address is the first thing in the Ethernet frame
|
|
if( ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData) )
|
|
{
|
|
//
|
|
// Packet is broadcast / multicast at the Ethernet level.
|
|
//
|
|
// We need to send it on all other compatibility-mode adapters
|
|
// (and regular adapters too if this came in on a compatibility
|
|
// adapter)
|
|
//
|
|
|
|
bRetained = BrdgCompSendToMultipleAdapters( pPacket, pAdapt, pPacketData,
|
|
bCanRetain && (!pAdapt->bCompatibilityMode), // TRUE == can retain
|
|
// If this is a compat adapter, send to all adapters
|
|
pAdapt->bCompatibilityMode,
|
|
pEditFunc, pData );
|
|
|
|
if( (!bCanRetain) || (pAdapt->bCompatibilityMode) )
|
|
{
|
|
SAFEASSERT( !bRetained );
|
|
}
|
|
|
|
if( pAdapt->bCompatibilityMode )
|
|
{
|
|
// It's our job to indicate this packet.
|
|
bRetained = BrdgCompIndicatePacketOrPacketCopy(pPacket, pPacketData, bCanRetain, pAdapt, pEditFunc, pData );
|
|
}
|
|
// else the regular-mode processing will indicate this frame
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Packet is unicast at the Ethernet level. Verify that it's targeted at a unicast IP address.
|
|
//
|
|
BOOLEAN bIsUnicast = BrdgCompIsUnicastIPAddress(piphi->ipTarget);
|
|
|
|
if( !bIsUnicast )
|
|
{
|
|
//
|
|
// Strange; this packet is unicast to us at the Ethernet level but is for a
|
|
// broadcast, multicast or zero target IP address.
|
|
//
|
|
// We will have no entries for this in our forwarding table, and we assume the
|
|
// IP stack will have no next-hop information for this address, so we just indicate
|
|
// it right away and let the IP driver figure out what this thing is.
|
|
//
|
|
THROTTLED_DBGPRINT(COMPAT, ("Packet with non-unicast target IP address %i.%i.%i.%i received in unicast Ethernet frame on adapter %p",
|
|
((PUCHAR)&piphi->ipTarget)[3], ((PUCHAR)&piphi->ipTarget)[2], ((PUCHAR)&piphi->ipTarget)[1],
|
|
((PUCHAR)&piphi->ipTarget)[0], pAdapt ));
|
|
|
|
// Process the packet below as if it were unicast to us.
|
|
}
|
|
|
|
if( (!bIsUnicast) || BrdgCompIsLocalIPAddress(piphi->ipTarget) )
|
|
{
|
|
//
|
|
// It's only appropriate for us to indicate the packet if the adapter
|
|
// on which the packet was received is a compatibility-mode adapter.
|
|
// Otherwise, the packet is indicated along regular codepaths without
|
|
// the need to edit it in any way.
|
|
//
|
|
if( pAdapt->bCompatibilityMode )
|
|
{
|
|
bRetained = BrdgCompIndicatePacketOrPacketCopy(pPacket, pPacketData, bCanRetain, pAdapt, pEditFunc, pData );
|
|
}
|
|
else
|
|
{
|
|
bRetained = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This packet is not for us. Look it up in our forwarding table to see if
|
|
// we know where the target machine is.
|
|
//
|
|
pipte = (PIP_TABLE_ENTRY)BrdgHashFindEntry( gIPForwardingTable, (PUCHAR)&piphi->ipTarget, &LockState );
|
|
|
|
if( pipte != NULL )
|
|
{
|
|
PADAPT pTargetAdapt;
|
|
UCHAR targetMAC[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
// Copy out the information we need within the spin lock
|
|
NdisAcquireSpinLock( &pipte->lock );
|
|
pTargetAdapt = pipte->pAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( targetMAC, pipte->macAddr );
|
|
NdisReleaseSpinLock( &pipte->lock );
|
|
|
|
// We will use the adapter outside the table lock
|
|
BrdgAcquireAdapterInLock( pTargetAdapt );
|
|
|
|
// Done with the table entry
|
|
NdisReleaseReadWriteLock( &gIPForwardingTable->tableLock, &LockState );
|
|
|
|
// It is strange to receive traffic that needs to be retransmitted on the same adapter.
|
|
if( pTargetAdapt == pAdapt )
|
|
{
|
|
THROTTLED_DBGPRINT(COMPAT, ("WARNING: retransmitting traffic for %i.%i.%i.%i on Adapter %p\n",
|
|
((PUCHAR)&piphi->ipTarget)[3], ((PUCHAR)&piphi->ipTarget)[2],
|
|
((PUCHAR)&piphi->ipTarget)[1], ((PUCHAR)&piphi->ipTarget)[0], pAdapt));
|
|
}
|
|
|
|
bRetained = BrdgCompEditAndSendPacketOrPacketCopy(pPacket, pPacketData, bCanRetain, targetMAC,
|
|
pTargetAdapt, pEditFunc, pData );
|
|
|
|
BrdgReleaseAdapter( pTargetAdapt );
|
|
}
|
|
else
|
|
{
|
|
IPADDRESS ipNextHop;
|
|
|
|
//
|
|
// This packet was unicast to us at the Ethernet level but is for an IP address
|
|
// that isn't in our forwarding table. Assuming the transmitting station had a
|
|
// good reason for sending us this packet, and that our forward tables are working
|
|
// correctly and aren't corrupt, two possibilities remain:
|
|
//
|
|
// a) The packet needs to be routed off the subnet by the local machine (this is
|
|
// why the target IP address doesn't appear in our tables; one does not ARP for
|
|
// an off-subnet machine before transmitting to it; one sends packets to one's
|
|
// default gateway)
|
|
//
|
|
// b) The packet needs to be routed off the subnet by some other machine. Unfortunately
|
|
// we don't know which one, since all packets that come to us have the same target
|
|
// MAC address and the target IP address is no use; what we really want is the
|
|
// first-hop IP address.
|
|
//
|
|
// To sort this out, we call TCPIP to do a route lookup for the packet's target IP
|
|
// address. If the resulting next-hop IP address appears in our forwarding table
|
|
// (i.e., it is reachable on the bridged network), we send the packet on to that
|
|
// destination. If TCPIP gives us no first-hop, or the first-hop isn't in our table
|
|
// (as would occur if the next hop is reachable through some non-bridged adapter)
|
|
// we indicate the packet so TCPIP can deal with it. In such a case, the packet is
|
|
// either not routable (and IP will drop it) or was meant to be routed by the local
|
|
// machine (in which case IP will route it to its next hop).
|
|
//
|
|
|
|
if( BrdgCompGetNextHopForTarget(piphi->ipTarget, &ipNextHop) )
|
|
{
|
|
// We got a next-hop address. See if that address is in our forwarding table.
|
|
pipte = (PIP_TABLE_ENTRY)BrdgHashFindEntry( gIPForwardingTable, (PUCHAR)&ipNextHop, &LockState );
|
|
|
|
if( pipte != NULL )
|
|
{
|
|
PADAPT pNextHopAdapt;
|
|
UCHAR nextHopMAC[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
// Must copy out the information inside the entry's spin lock
|
|
NdisAcquireSpinLock( &pipte->lock );
|
|
pNextHopAdapt = pipte->pAdapt;
|
|
ETH_COPY_NETWORK_ADDRESS( nextHopMAC, pipte->macAddr );
|
|
NdisReleaseSpinLock( &pipte->lock );
|
|
|
|
// We will use the adapter outside the table lock
|
|
BrdgAcquireAdapterInLock( pNextHopAdapt );
|
|
|
|
// We're done with the forwarding table
|
|
NdisReleaseReadWriteLock( &gIPForwardingTable->tableLock, &LockState );
|
|
|
|
// Something strange is afoot if the next hop is reachable through the same adapter
|
|
if( pNextHopAdapt == pAdapt )
|
|
{
|
|
THROTTLED_DBGPRINT(COMPAT, ("WARNING: retransmitting traffic for %i.%i.%i.%i on Adapter %p to next-hop %i.%i.%i.%i\n",
|
|
((PUCHAR)&piphi->ipTarget)[3], ((PUCHAR)&piphi->ipTarget)[2],
|
|
((PUCHAR)&piphi->ipTarget)[1], ((PUCHAR)&piphi->ipTarget)[0], pAdapt,
|
|
((PUCHAR)&ipNextHop)[3], ((PUCHAR)&ipNextHop)[2],
|
|
((PUCHAR)&ipNextHop)[1], ((PUCHAR)&ipNextHop)[0]));
|
|
}
|
|
|
|
// Send the packet out the appropriate adapter
|
|
bRetained = BrdgCompEditAndSendPacketOrPacketCopy( pPacket, pPacketData, bCanRetain, nextHopMAC,
|
|
pNextHopAdapt, pEditFunc, pData );
|
|
|
|
BrdgReleaseAdapter( pNextHopAdapt );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The next hop isn't in our forwarding table. This means that the next hop machine
|
|
// isn't reachable on the bridged network, unless we're in a screwy state with
|
|
// respect to the transmitting machine (i.e., it never ARPed for the router it
|
|
// wanted because it had a static ARP entry or some other such weirdness).
|
|
// At any rate, conclude at this point that the local machine should handle the packet.
|
|
//
|
|
bRetained = BrdgCompIndicatePacketOrPacketCopy( pPacket, pPacketData, bCanRetain, pAdapt, pEditFunc, pData );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No usable next-hop information. Conclude that the packet should be handled by
|
|
// the local machine. Indicate.
|
|
//
|
|
bRetained = BrdgCompIndicatePacketOrPacketCopy( pPacket, pPacketData, bCanRetain, pAdapt, pEditFunc, pData );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bCanRetain )
|
|
{
|
|
SAFEASSERT( !bRetained );
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
|
|
// Returns whether the packet was retained
|
|
BOOLEAN
|
|
BrdgCompProcessInboundNonARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an inbound non-ARP packet
|
|
|
|
Arguments:
|
|
|
|
pPacket The packet
|
|
pAdapt The receiving adapter
|
|
bCanRetain If we can retain the packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Size of data in buffer
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bRetained = FALSE;
|
|
IP_HEADER_INFO iphi;
|
|
|
|
SAFEASSERT( (pPacket != NULL) && (pPacketData != NULL) );
|
|
|
|
if( packetLen >= MINIMUM_SIZE_FOR_IP )
|
|
{
|
|
if( BrdgCompDecodeIPHeader(pPacketData + ETHERNET_HEADER_SIZE, &iphi) )
|
|
{
|
|
PUCHAR pBootPData;
|
|
|
|
pBootPData = BrdgCompIsBootPPacket( pPacketData, packetLen, &iphi );
|
|
|
|
if ( pBootPData != NULL )
|
|
{
|
|
// This is a BOOTP packet; do BOOTP-specific processing
|
|
bRetained = BrdgCompProcessInboundBootPPacket( pPacket, pAdapt, bCanRetain, pPacketData, packetLen, &iphi, pBootPData );
|
|
}
|
|
else
|
|
{
|
|
// Do generic IP processing
|
|
bRetained = BrdgCompProcessInboundIPPacket(pPacket, &iphi, pAdapt, bCanRetain, pPacketData, packetLen, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bCanRetain )
|
|
{
|
|
SAFEASSERT( !bRetained );
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessInboundBootPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PADAPT pAdapt,
|
|
IN BOOLEAN bCanRetain,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PIP_HEADER_INFO piphi,
|
|
IN PUCHAR pBootPData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an inbound BOOTP packet
|
|
|
|
Arguments:
|
|
|
|
pPacket The packet
|
|
pAdapt The receiving adapter
|
|
bCanRetain If we can retain the packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Size of data in buffer
|
|
piphi Decoded IP header info
|
|
pBootPData Pointer to BOOTP payload within the packet
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
UCHAR targetMAC[ETH_LENGTH_OF_ADDRESS];
|
|
BOOLEAN bIsRequest;
|
|
PADAPT pTargetAdapt = NULL;
|
|
|
|
if( BrdgCompPreprocessBootPPacket(pPacketData, piphi, pBootPData, pAdapt, &bIsRequest, &pTargetAdapt, targetMAC) )
|
|
{
|
|
if( bIsRequest )
|
|
{
|
|
//
|
|
// This is a request packet. It can be processed as a regular inbound IP packet,
|
|
// subject to appropriate rewriting at each step.
|
|
//
|
|
SAFEASSERT( pTargetAdapt == NULL );
|
|
return BrdgCompProcessInboundIPPacket( pPacket, piphi, pAdapt, bCanRetain, pPacketData, packetLen,
|
|
BrdgCompRewriteBootPPacketForAdapt, piphi );
|
|
}
|
|
else
|
|
{
|
|
BOOLEAN bUsingCopyPacket, bRetained;
|
|
|
|
//
|
|
// This is a reply packet. We can rewrite it once for all purposes.
|
|
//
|
|
|
|
// Make a copy if necessary so we can edit.
|
|
if( ! bCanRetain )
|
|
{
|
|
pPacket = BrdgFwdMakeCompatCopyPacket( pPacket, &pPacketData, &packetLen, FALSE );
|
|
|
|
if( (pPacket == NULL) || (pPacketData == NULL) )
|
|
{
|
|
// Free the target adapter before bailing out
|
|
if( pTargetAdapt != NULL )
|
|
{
|
|
BrdgReleaseAdapter( pTargetAdapt );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
bUsingCopyPacket = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bUsingCopyPacket = FALSE;
|
|
}
|
|
|
|
// Rewrite the packet to the retrieved MAC address.
|
|
BrdgCompRewriteBootPClientAddress( pPacketData, piphi, targetMAC );
|
|
|
|
if( pTargetAdapt != NULL )
|
|
{
|
|
// If the reply was sent by broadcast, respect this, even if we think
|
|
// we know the unicast MAC address of the target.
|
|
if( ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData) )
|
|
{
|
|
// Broadcast around the reply
|
|
bRetained = BrdgCompSendToMultipleAdapters( pPacket, pAdapt, pPacketData, TRUE, pAdapt->bCompatibilityMode,
|
|
NULL, NULL );
|
|
}
|
|
else
|
|
{
|
|
// Unicast back the reply
|
|
ETH_COPY_NETWORK_ADDRESS( pPacketData, targetMAC );
|
|
BrdgCompSendPacket( pPacket, pPacketData, pTargetAdapt );
|
|
|
|
bRetained = TRUE;
|
|
}
|
|
|
|
// The target adapter came back with an incremented refcount
|
|
BrdgReleaseAdapter( pTargetAdapt );
|
|
}
|
|
else
|
|
{
|
|
// This reply is for the local machine!
|
|
UINT Result;
|
|
|
|
// The recorded MAC address should be the MAC address of the bridge.
|
|
SAFEASSERT( gCompHaveMACAddress );
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ( targetMAC, gCompMACAddress, &Result );
|
|
SAFEASSERT( Result == 0 );
|
|
|
|
// Indicate the edited reply
|
|
BrdgCompIndicatePacket( pPacket, pPacketData, pAdapt );
|
|
bRetained = TRUE;
|
|
}
|
|
|
|
if( bUsingCopyPacket )
|
|
{
|
|
if( !bRetained )
|
|
{
|
|
// Our copy packet was not retained.
|
|
BrdgFwdReleaseCompatPacket( pPacket );
|
|
}
|
|
|
|
// If we were using a copy packet, we definitely did not retain the packet passed in
|
|
bRetained = FALSE;
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Something went wrong in the preprocessing.
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ===========================================================================
|
|
//
|
|
// OUTBOUND PACKET PROCESSING
|
|
//
|
|
// ===========================================================================
|
|
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessOutboundNonARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PADAPT pTargetAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an outbound non-ARP packet. This function may retain the
|
|
given packet if it wishes.
|
|
|
|
Arguments:
|
|
|
|
pPacket The packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Length of the data buffer
|
|
pTargetAdapt The target adapter, as determined
|
|
by a previous MAC-table lookup
|
|
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
IP_HEADER_INFO iphi;
|
|
BOOLEAN bRetained = FALSE, bIsMulticast;
|
|
|
|
if( packetLen >= MINIMUM_SIZE_FOR_IP &&
|
|
BrdgCompDecodeIPHeader(pPacketData + ETHERNET_HEADER_SIZE, &iphi) )
|
|
{
|
|
PUCHAR pBootPData;
|
|
|
|
pBootPData = BrdgCompIsBootPPacket(pPacketData, packetLen, &iphi);
|
|
|
|
if( pBootPData != NULL )
|
|
{
|
|
// Do special BOOTP processing
|
|
return BrdgCompProcessOutboundBootPPacket( pPacket, pPacketData, packetLen, pTargetAdapt, pBootPData, &iphi );
|
|
}
|
|
}
|
|
|
|
bIsMulticast = (BOOLEAN)(ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData));
|
|
|
|
// We edit and transmit the packet even if it doesn't appear to be IP.
|
|
if( (pTargetAdapt == NULL) || bIsMulticast )
|
|
{
|
|
// Don't expect a target adapter when the outbound frame is broadcast
|
|
if( bIsMulticast )
|
|
{
|
|
SAFEASSERT( pTargetAdapt == NULL );
|
|
}
|
|
|
|
// We need to send this packet to all compat adapters.
|
|
bRetained = BrdgCompSendToMultipleAdapters( pPacket, NULL, pPacketData, TRUE, /*Can retain*/
|
|
FALSE /* Compat-mode adapters only*/,
|
|
NULL /*No editing function*/, NULL );
|
|
}
|
|
else
|
|
{
|
|
BrdgCompSendPacket( pPacket, pPacketData, pTargetAdapt );
|
|
|
|
// The packet has been handed off to the forwarding engine
|
|
bRetained = TRUE;
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessOutboundARPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PADAPT pTargetAdapt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an outbound ARP packet. This function may retain the
|
|
given packet if it wishes.
|
|
|
|
Arguments:
|
|
|
|
pPacket The packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Length of the data buffer
|
|
pTargetAdapt The target adapter, as determined
|
|
by a previous MAC-table lookup
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bRetained = FALSE, bIsMulticast;
|
|
ARPINFO ai;
|
|
|
|
if( packetLen < SIZE_OF_ARP_PACKET )
|
|
{
|
|
// Packet is too small to be ARP; process as non-ARP
|
|
return BrdgCompProcessOutboundNonARPPacket( pPacket, pPacketData, packetLen, pTargetAdapt );
|
|
}
|
|
|
|
if( BrdgCompDecodeARPPacket(pPacketData, packetLen, &ai) )
|
|
{
|
|
if( ai.type == ArpRequest )
|
|
{
|
|
ARP_TABLE_KEY atk;
|
|
PARP_TABLE_ENTRY pEntry;
|
|
LOCK_STATE LockState;
|
|
BOOLEAN bIsNewEntry;
|
|
|
|
// Note that the local machine is trying to resolve this target IP address by
|
|
// inserting or refreshing an entry with 0.0.0.0 as the requestor
|
|
atk.ipReqestor = 0L; // Special value for local machine
|
|
atk.ipTarget = ai.ipTarget;
|
|
|
|
pEntry = (PARP_TABLE_ENTRY)BrdgHashRefreshOrInsert( gPendingARPTable, (PUCHAR)&atk, &bIsNewEntry,
|
|
&LockState );
|
|
|
|
if( pEntry != NULL )
|
|
{
|
|
if( bIsNewEntry)
|
|
{
|
|
// Even though this entry isn't really ever used, initialize it so
|
|
// functions walking across table entries don't get confused or crash.
|
|
NdisAllocateSpinLock( &pEntry->lock );
|
|
pEntry->pOriginalAdapt = NULL;
|
|
pEntry->originalMAC[0] = pEntry->originalMAC[1] = pEntry->originalMAC[2] =
|
|
pEntry->originalMAC[3] = pEntry->originalMAC[4] =pEntry->originalMAC[5] = 0;
|
|
}
|
|
|
|
NdisReleaseReadWriteLock( &gPendingARPTable->tableLock, &LockState );
|
|
}
|
|
}
|
|
|
|
// Check if this frame looks like it should be relayed to all compat adapters
|
|
bIsMulticast = (BOOLEAN)(ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData));
|
|
|
|
if( (pTargetAdapt == NULL) || bIsMulticast )
|
|
{
|
|
// Don't expect a target adapter when the outbound frame is broadcast
|
|
if( bIsMulticast )
|
|
{
|
|
SAFEASSERT( pTargetAdapt == NULL );
|
|
}
|
|
|
|
// We need to send this packet to all compat adapters.
|
|
bRetained = BrdgCompSendToMultipleAdapters( pPacket, NULL, pPacketData, TRUE,/*Can retain*/
|
|
FALSE /* Compat-mode adapters only*/,
|
|
BrdgCompRewriteOutboundARPPacket, NULL );
|
|
}
|
|
else
|
|
{
|
|
// Edit the packet for the outbound adapter
|
|
BrdgCompRewriteOutboundARPPacket( pPacketData, pTargetAdapt, NULL );
|
|
|
|
// Send the packet on its way
|
|
BrdgCompSendPacket( pPacket, pPacketData, pTargetAdapt );
|
|
|
|
// The packet has been handed off to the forwarding engine
|
|
bRetained = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The packet didn't look like an ARP packet. Process it otherwise.
|
|
return BrdgCompProcessOutboundNonARPPacket( pPacket, pPacketData, packetLen, pTargetAdapt );
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
|
|
BOOLEAN
|
|
BrdgCompProcessOutboundBootPPacket(
|
|
IN PNDIS_PACKET pPacket,
|
|
IN PUCHAR pPacketData,
|
|
IN UINT packetLen,
|
|
IN PADAPT pTargetAdapt,
|
|
IN PUCHAR pBootPData,
|
|
IN PIP_HEADER_INFO piphi
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes an outbound BOOTP packet. This function may retain the
|
|
given packet if it wishes.
|
|
|
|
Arguments:
|
|
|
|
pPacket The packet
|
|
pPacketData The packet's data buffer
|
|
packetLen Length of the data buffer
|
|
pTargetAdapt The target adapter, as determined
|
|
by a previous MAC-table lookup
|
|
pBootPData Pointer to the BOOTP payload within the packet
|
|
piphi Decoded info from the packet's IP header
|
|
|
|
Return Value:
|
|
|
|
Whether we retained the packet
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bIsRequest, bRetained;
|
|
PADAPT pRequestorAdapt = NULL;
|
|
UCHAR macRequestor[ETH_LENGTH_OF_ADDRESS];
|
|
|
|
if( BrdgCompPreprocessBootPPacket( pPacketData, piphi, pBootPData, NULL, &bIsRequest, &pRequestorAdapt, macRequestor ) )
|
|
{
|
|
if( bIsRequest )
|
|
{
|
|
//
|
|
// This is a BOOTP request. Transmit as appropriate but rewrite for each adapter.
|
|
//
|
|
SAFEASSERT( pRequestorAdapt == NULL );
|
|
|
|
if( (pTargetAdapt == NULL) || ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData) )
|
|
{
|
|
bRetained = BrdgCompSendToMultipleAdapters( pPacket, NULL, pPacketData, TRUE, FALSE, BrdgCompRewriteBootPPacketForAdapt,
|
|
piphi );
|
|
}
|
|
else
|
|
{
|
|
// Rewrite the packet before transmission
|
|
BrdgCompRewriteBootPPacketForAdapt( pPacketData, pTargetAdapt, piphi );
|
|
|
|
// Unicast out the packet
|
|
BrdgCompSendPacket( pPacket, pPacketData, pTargetAdapt );
|
|
bRetained = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is a BOOTP reply. No editing is necessary; just send it.
|
|
//
|
|
if( (pTargetAdapt == NULL) || ETH_IS_BROADCAST(pPacketData) || ETH_IS_MULTICAST(pPacketData) )
|
|
{
|
|
bRetained = BrdgCompSendToMultipleAdapters( pPacket, NULL, pPacketData, TRUE, FALSE, NULL, NULL );
|
|
}
|
|
else
|
|
{
|
|
UINT Result;
|
|
|
|
// Verify for sanity that the target we're sending it to matches the information
|
|
// in the table.
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ( macRequestor, pPacketData, &Result );
|
|
SAFEASSERT( Result == 0 );
|
|
SAFEASSERT( pTargetAdapt == pRequestorAdapt );
|
|
|
|
// This packet is unicast, probably part of an established conversation with a
|
|
// DHCP server.
|
|
BrdgCompSendPacket( pPacket, pPacketData, pTargetAdapt );
|
|
bRetained = TRUE;
|
|
}
|
|
|
|
// This comes back with its refcount incremented
|
|
if( pRequestorAdapt != NULL )
|
|
{
|
|
BrdgReleaseAdapter( pRequestorAdapt );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Preprocessing failed
|
|
bRetained = FALSE;
|
|
}
|
|
|
|
return bRetained;
|
|
}
|
|
|